Phase 3: embed web UI with go:embed, pinned TypeScript toolchain, drop i18n, add /healthz
continuous-integration/drone/push Build encountered an error
continuous-integration/drone/push Build encountered an error
- html/embed.go embeds html/ (pages, css, img, js, video); src/assets.go
serves it, with os.DirFS("html") under -dev. Static assets get an ETag
and Cache-Control: no-cache; HTML pages are still templated (only the
login error message is substituted now).
- Delete the generated src/webUI.go (783 KB base64), src/html-build.go and
cmd/webui-gen; Dockerfile no longer runs a generator.
- Language layer removed: 254 {{.x}} placeholders inlined as English
strings in ts/*.ts and the two auth pages; html/lang/en.json, the
LanguageUI struct and the 'language' setting are gone.
- ts/tsconfig.json + package.json pin typescript 5.9.3; the seven sources
compile (ES2020, global scripts) into one committed html/js/app.js.
Ten unreferenced legacy scripts under html/js/ deleted; all pages load
js/app.js.
- Fix the six type errors that blocked a clean compile, including a real
bug: a missing semicolon in the search shortcut handler made the code
call the result of preventDefault(), so the shortcut threw instead of
focusing the search box.
- /healthz liveness endpoint; Dockerfile healthcheck and README use it.
- Drone: go vet, and a webui-check step that rebuilds the bundle and fails
if the committed app.js is stale.
- README-DEV.md documents build, UI toolchain, -dev, versioning, CI.
This commit is contained in:
@@ -7,8 +7,17 @@ steps:
|
|||||||
image: cache.coadcorp.com/library/golang:1.27.1
|
image: cache.coadcorp.com/library/golang:1.27.1
|
||||||
commands:
|
commands:
|
||||||
- go mod download
|
- go mod download
|
||||||
|
- go vet ./...
|
||||||
- go test ./...
|
- go test ./...
|
||||||
|
|
||||||
|
- name: webui-check
|
||||||
|
image: cache.coadcorp.com/library/node:22-alpine
|
||||||
|
commands:
|
||||||
|
- cp html/js/app.js /tmp/app.js.committed
|
||||||
|
- npm ci --no-audit --no-fund
|
||||||
|
- npm run build
|
||||||
|
- cmp /tmp/app.js.committed html/js/app.js || (echo "html/js/app.js is stale - run 'npm run build' and commit the result" && exit 1)
|
||||||
|
|
||||||
- name: go-build
|
- name: go-build
|
||||||
image: cache.coadcorp.com/library/golang:1.27.1
|
image: cache.coadcorp.com/library/golang:1.27.1
|
||||||
commands:
|
commands:
|
||||||
|
|||||||
@@ -11,3 +11,4 @@ de.json
|
|||||||
.gocache/
|
.gocache/
|
||||||
agent.md
|
agent.md
|
||||||
skill.md
|
skill.md
|
||||||
|
node_modules/
|
||||||
|
|||||||
+1
-3
@@ -10,8 +10,6 @@ RUN go mod download
|
|||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
RUN go run ./cmd/webui-gen
|
|
||||||
|
|
||||||
ARG TARGETOS
|
ARG TARGETOS
|
||||||
ARG TARGETARCH
|
ARG TARGETARCH
|
||||||
RUN XTEVE_VERSION="$(grep -m1 '^#### ' changelog-beta.md | cut -d' ' -f2 | sed 's/-beta$//')" \
|
RUN XTEVE_VERSION="$(grep -m1 '^#### ' changelog-beta.md | cut -d' ' -f2 | sed 's/-beta$//')" \
|
||||||
@@ -47,6 +45,6 @@ ENV XTEVE_CONFIG=/xteve/config
|
|||||||
ENV XTEVE_PORT=34400
|
ENV XTEVE_PORT=34400
|
||||||
|
|
||||||
HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \
|
||||||
CMD wget -qO- "http://127.0.0.1:${XTEVE_PORT}/lineup_status.json" > /dev/null || exit 1
|
CMD wget -qO- "http://127.0.0.1:${XTEVE_PORT}/healthz" > /dev/null || exit 1
|
||||||
|
|
||||||
ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
|
ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
|
||||||
|
|||||||
+57
-1
@@ -1 +1,57 @@
|
|||||||
# Information for the developers will come soon
|
# Developer notes
|
||||||
|
|
||||||
|
## Layout
|
||||||
|
|
||||||
|
| Path | What it is |
|
||||||
|
|---|---|
|
||||||
|
| `xteve.go` | `main`: flags, version, start-up |
|
||||||
|
| `src/` | the application (one Go package `src`) |
|
||||||
|
| `src/internal/` | authentication, image cache, M3U parser |
|
||||||
|
| `html/` | web interface: HTML pages, CSS, images, the compiled `js/app.js`, and `embed.go` which embeds the lot |
|
||||||
|
| `ts/` | TypeScript sources for the web interface |
|
||||||
|
| `docker/` | container entrypoint |
|
||||||
|
| `tasks/` | improvement plan and checklist |
|
||||||
|
|
||||||
|
## Build the binary
|
||||||
|
|
||||||
|
Go 1.27.1 is pinned in `go.mod`; the go command downloads it automatically.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go build ./...
|
||||||
|
go vet ./...
|
||||||
|
go test ./...
|
||||||
|
```
|
||||||
|
|
||||||
|
The web interface is embedded with `go:embed` from `html/`, so a bare clone builds a complete binary.
|
||||||
|
|
||||||
|
## Web interface
|
||||||
|
|
||||||
|
The UI is plain TypeScript compiled as global scripts (no modules yet) into a single bundle, `html/js/app.js`, which is committed. CI rebuilds it and fails if the committed file is stale.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm ci # installs the pinned TypeScript compiler
|
||||||
|
npm run build # ts/*.ts -> html/js/app.js
|
||||||
|
npm run check # type-check only
|
||||||
|
```
|
||||||
|
|
||||||
|
File order in the bundle is fixed in `ts/tsconfig.json` and matches the old per-page script order. All pages load the same bundle.
|
||||||
|
|
||||||
|
English strings are inlined in the TypeScript. There is no language layer.
|
||||||
|
|
||||||
|
## Run from source
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go run . -config /path/to/config -port 34400
|
||||||
|
```
|
||||||
|
|
||||||
|
Add `-dev` to serve the web interface from the local `html/` directory instead of the embedded copy, so CSS and HTML edits show up on reload. JavaScript still needs `npm run build`.
|
||||||
|
|
||||||
|
## Versioning and release
|
||||||
|
|
||||||
|
The version lives in two places that must agree: `var Version` in `xteve.go` and the first `#### ` heading in `changelog-beta.md` (without the `-beta` suffix). CI fails on drift.
|
||||||
|
|
||||||
|
Drone (`.drone.yml`) runs vet, tests, the bundle check, hadolint and compose validation on every push. Pushes to `master` publish `registry.coadcorp.com/nathan/xteve:latest` and `:<sha>`; other branches publish `:<branch>` and `:<sha>`.
|
||||||
|
|
||||||
|
## Endpoints useful for operations
|
||||||
|
|
||||||
|
* `GET /healthz` returns `{"status":"ok","version":...,"scanInProgress":...}` with no authentication. The Docker healthcheck uses it.
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ Host networking improves LAN discovery behavior (SSDP/DLNA) for Plex/Emby in man
|
|||||||
|
|
||||||
* Multi-stage build (Go builder + minimal Alpine runtime)
|
* Multi-stage build (Go builder + minimal Alpine runtime)
|
||||||
* Runs as non-root user (`xteve`)
|
* Runs as non-root user (`xteve`)
|
||||||
* Built-in healthcheck against `http://127.0.0.1:${XTEVE_PORT}/lineup_status.json`
|
* Built-in healthcheck against `http://127.0.0.1:${XTEVE_PORT}/healthz`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"xteve/src"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
src.HTMLInit("webUI", "src", "html"+string(os.PathSeparator), "src"+string(os.PathSeparator)+"webUI.go")
|
|
||||||
if err := src.BuildGoFile(); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -6,11 +6,7 @@
|
|||||||
<title>xTeVe</title>
|
<title>xTeVe</title>
|
||||||
<link rel="stylesheet" href="css/screen.css" type="text/css">
|
<link rel="stylesheet" href="css/screen.css" type="text/css">
|
||||||
<link rel="stylesheet" href="css/base.css" type="text/css">
|
<link rel="stylesheet" href="css/base.css" type="text/css">
|
||||||
<script language="javascript" type="text/javascript" src="js/configuration_ts.js"></script>
|
<script src="js/app.js"></script>
|
||||||
<script language="javascript" type="text/javascript" src="js/network_ts.js"></script>
|
|
||||||
<script language="javascript" type="text/javascript" src="js/menu_ts.js"></script>
|
|
||||||
<script language="javascript" type="text/javascript" src="js/settings_ts.js"></script>
|
|
||||||
<script language="javascript" type="text/javascript" src="js/base_ts.js"></script>
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="auth-screen wizard-screen" onload="javascript: readyForConfiguration(0);">
|
<body class="auth-screen wizard-screen" onload="javascript: readyForConfiguration(0);">
|
||||||
|
|||||||
@@ -6,8 +6,7 @@
|
|||||||
<title>xTeVe</title>
|
<title>xTeVe</title>
|
||||||
<link rel="stylesheet" href="css/screen.css" type="text/css">
|
<link rel="stylesheet" href="css/screen.css" type="text/css">
|
||||||
<link rel="stylesheet" href="css/base.css" type="text/css">
|
<link rel="stylesheet" href="css/base.css" type="text/css">
|
||||||
<script language="javascript" type="text/javascript" src="js/network_ts.js"></script>
|
<script src="js/app.js"></script>
|
||||||
<script language="javascript" type="text/javascript" src="js/authentication_ts.js"></script>
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="auth-screen">
|
<body class="auth-screen">
|
||||||
@@ -17,7 +16,7 @@
|
|||||||
<main id="box" role="main" aria-labelledby="head-text">
|
<main id="box" role="main" aria-labelledby="head-text">
|
||||||
|
|
||||||
<div id="headline">
|
<div id="headline">
|
||||||
<h1 id="head-text" class="center">{{.account.headline}}</h1>
|
<h1 id="head-text" class="center">Create user account</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p id="err" class="errorMsg center" role="alert" aria-live="assertive" aria-atomic="true"></p>
|
<p id="err" class="errorMsg center" role="alert" aria-live="assertive" aria-atomic="true"></p>
|
||||||
@@ -26,11 +25,11 @@
|
|||||||
|
|
||||||
<form id="authentication" action="" method="post" aria-describedby="err" novalidate>
|
<form id="authentication" action="" method="post" aria-describedby="err" novalidate>
|
||||||
|
|
||||||
<label for="username">{{.account.username.title}}:</label>
|
<label for="username">Username:</label>
|
||||||
<input id="username" type="text" name="username" placeholder="Username" value="" autocomplete="username">
|
<input id="username" type="text" name="username" placeholder="Username" value="" autocomplete="username">
|
||||||
<label for="password">{{.account.password.title}}:</label>
|
<label for="password">Password:</label>
|
||||||
<input id="password" type="password" name="password" placeholder="Password" value="" autocomplete="new-password">
|
<input id="password" type="password" name="password" placeholder="Password" value="" autocomplete="new-password">
|
||||||
<label for="confirm">{{.account.confirm.title}}:</label>
|
<label for="confirm">Confirm:</label>
|
||||||
<input id="confirm" type="password" name="confirm" placeholder="Confirm" value="" autocomplete="new-password">
|
<input id="confirm" type="password" name="confirm" placeholder="Confirm" value="" autocomplete="new-password">
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
@@ -38,7 +37,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="box-footer">
|
<div id="box-footer">
|
||||||
<input id="submit" class="" type="button" value="{{.button.craeteAccount}}" aria-label="{{.button.craeteAccount}}" onclick="javascript: login();">
|
<input id="submit" class="" type="button" value="Create Account" aria-label="Create Account" onclick="javascript: login();">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
// Package webui embeds the static web interface (HTML, CSS, the compiled
|
||||||
|
// JavaScript bundle, images and the tuner-limit video) into the binary.
|
||||||
|
//
|
||||||
|
// Build the JavaScript bundle with `npm run build` (see README-DEV.md); the
|
||||||
|
// result, html/js/app.js, is committed so `go build` works from a bare clone.
|
||||||
|
package webui
|
||||||
|
|
||||||
|
import "embed"
|
||||||
|
|
||||||
|
// FS holds the web interface files, rooted at the html/ directory.
|
||||||
|
//
|
||||||
|
//go:embed *.html css img js video
|
||||||
|
var FS embed.FS
|
||||||
+1
-6
@@ -6,12 +6,7 @@
|
|||||||
<title>xTeVe</title>
|
<title>xTeVe</title>
|
||||||
<link rel="stylesheet" href="css/screen.css" type="text/css">
|
<link rel="stylesheet" href="css/screen.css" type="text/css">
|
||||||
<link rel="stylesheet" href="css/base.css" type="text/css">
|
<link rel="stylesheet" href="css/base.css" type="text/css">
|
||||||
|
<script src="js/app.js"></script>
|
||||||
<script language="javascript" type="text/javascript" src="js/network_ts.js"></script>
|
|
||||||
<script language="javascript" type="text/javascript" src="js/menu_ts.js"></script>
|
|
||||||
<script language="javascript" type="text/javascript" src="js/settings_ts.js"></script>
|
|
||||||
<script language="javascript" type="text/javascript" src="js/logs_ts.js"></script>
|
|
||||||
<script language="javascript" type="text/javascript" src="js/base_ts.js"></script>
|
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
|||||||
+1711
-496
File diff suppressed because it is too large
Load Diff
@@ -1,42 +0,0 @@
|
|||||||
function createFirstAccount(elm) {
|
|
||||||
var err = false;
|
|
||||||
var div = document.getElementById(elm);
|
|
||||||
console.log(div);
|
|
||||||
|
|
||||||
var form = document.getElementById('authentication');
|
|
||||||
|
|
||||||
const username = document.getElementById('username');
|
|
||||||
const password = document.getElementById('password');
|
|
||||||
const confirm = document.getElementById('confirm');
|
|
||||||
|
|
||||||
var inputs = div.getElementsByTagName('INPUT')
|
|
||||||
console.log(confirm);
|
|
||||||
|
|
||||||
switch(confirm) {
|
|
||||||
case null: break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
for (var i = 0; i < inputs.length; i++) {
|
|
||||||
if (inputs[i].value.length == 0) {
|
|
||||||
inputs[i].style.borderColor = 'red';
|
|
||||||
err = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(err) {
|
|
||||||
case true: return; break;
|
|
||||||
case false:
|
|
||||||
if (password.value != confirm.value) {
|
|
||||||
confirm.style.borderColor = 'red';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
form.submit();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
function login() {
|
|
||||||
var err = false;
|
|
||||||
var firstInvalid = null;
|
|
||||||
var data = new Object();
|
|
||||||
var div = document.getElementById("content");
|
|
||||||
var form = document.getElementById("authentication");
|
|
||||||
var errElement = document.getElementById("err");
|
|
||||||
if (errElement != null) {
|
|
||||||
errElement.innerHTML = "";
|
|
||||||
}
|
|
||||||
var inputs = div.getElementsByTagName("INPUT");
|
|
||||||
console.log(inputs);
|
|
||||||
for (var i = inputs.length - 1; i >= 0; i--) {
|
|
||||||
var key = inputs[i].name;
|
|
||||||
var value = inputs[i].value;
|
|
||||||
if (value.length == 0) {
|
|
||||||
inputs[i].style.borderColor = "red";
|
|
||||||
inputs[i].setAttribute("aria-invalid", "true");
|
|
||||||
if (firstInvalid == null) {
|
|
||||||
firstInvalid = inputs[i];
|
|
||||||
}
|
|
||||||
err = true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
inputs[i].style.borderColor = "";
|
|
||||||
inputs[i].setAttribute("aria-invalid", "false");
|
|
||||||
}
|
|
||||||
data[key] = value;
|
|
||||||
}
|
|
||||||
if (err == true) {
|
|
||||||
if (firstInvalid != null) {
|
|
||||||
firstInvalid.focus();
|
|
||||||
}
|
|
||||||
data = new Object();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (data.hasOwnProperty("confirm")) {
|
|
||||||
if (data["confirm"] != data["password"]) {
|
|
||||||
document.getElementById('password').style.borderColor = "red";
|
|
||||||
document.getElementById('confirm').style.borderColor = "red";
|
|
||||||
document.getElementById('password').setAttribute("aria-invalid", "true");
|
|
||||||
document.getElementById('confirm').setAttribute("aria-invalid", "true");
|
|
||||||
document.getElementById("err").innerHTML = "{{.account.failed}}";
|
|
||||||
document.getElementById('password').focus();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
console.log(data);
|
|
||||||
form.submit();
|
|
||||||
}
|
|
||||||
document.addEventListener("DOMContentLoaded", function () {
|
|
||||||
var form = document.getElementById("authentication");
|
|
||||||
if (form == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var inputs = form.getElementsByTagName("INPUT");
|
|
||||||
for (var i = 0; i < inputs.length; i++) {
|
|
||||||
inputs[i].addEventListener("keydown", function (event) {
|
|
||||||
if (event.key == "Enter") {
|
|
||||||
event.preventDefault();
|
|
||||||
login();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (inputs.length > 0) {
|
|
||||||
inputs[0].focus();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
-331
@@ -1,331 +0,0 @@
|
|||||||
var config = new Object();
|
|
||||||
var menu = new Object();
|
|
||||||
var subMenu = new Object();
|
|
||||||
var activeStreams = new Object();
|
|
||||||
var xEPG = new Object();
|
|
||||||
var users = new Object();
|
|
||||||
var log = new Object();
|
|
||||||
var undo = new Object();
|
|
||||||
var webSockets = true;
|
|
||||||
var closeLog, version, activeMenu;
|
|
||||||
var columnToSort = 0
|
|
||||||
|
|
||||||
|
|
||||||
if (window.WebSocket === undefined) {
|
|
||||||
alert("Your browser does not support WebSockets");
|
|
||||||
webSockets = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function pageReady() {
|
|
||||||
var data = new Object();
|
|
||||||
data["cmd"] = "getServerConfig";
|
|
||||||
xTeVe(data);
|
|
||||||
//showLoadingScreen(false);
|
|
||||||
|
|
||||||
var resizeHandle = document.getElementById("openStreams");
|
|
||||||
var box = document.getElementById("myStreamsBox");
|
|
||||||
resizeHandle.addEventListener("mousedown", initialiseResize, false);
|
|
||||||
|
|
||||||
function initialiseResize(e) {
|
|
||||||
window.addEventListener("mousemove", startResizing, false);
|
|
||||||
window.addEventListener("mouseup", stopResizing, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
function startResizing(e) {
|
|
||||||
box.style.height = (e.clientY - box.offsetTop) + "px";
|
|
||||||
|
|
||||||
var elm = document.getElementById("allStreams");
|
|
||||||
if (e.clientY > 120) {
|
|
||||||
elm.className = "visible";
|
|
||||||
} else {
|
|
||||||
elm.className = "notVisible";
|
|
||||||
}
|
|
||||||
|
|
||||||
calculateWrapperHeight();
|
|
||||||
|
|
||||||
}
|
|
||||||
function stopResizing(e) {
|
|
||||||
window.removeEventListener('mousemove', startResizing, false);
|
|
||||||
window.removeEventListener('mouseup', stopResizing, false);
|
|
||||||
calculateWrapperHeight();
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener("resize", function(){
|
|
||||||
calculateWrapperHeight();
|
|
||||||
}, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function getObjKeys(obj) {
|
|
||||||
var keys = new Array();
|
|
||||||
|
|
||||||
for (var i in obj) {
|
|
||||||
if (obj.hasOwnProperty(i)) {
|
|
||||||
keys.push(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return keys;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function createElement(item) {
|
|
||||||
//console.log(item);
|
|
||||||
var element = document.createElement(item["_element"]);
|
|
||||||
if (item.hasOwnProperty("_text")) {
|
|
||||||
//element.innerHTML = "<p>" + item["_text"] + "</p>";
|
|
||||||
element.innerHTML = item["_text"];
|
|
||||||
}
|
|
||||||
|
|
||||||
var keys = getObjKeys(item);
|
|
||||||
for (var i = 0; i < keys.length; i++) {
|
|
||||||
if (keys[i].charAt(0) != "_") {
|
|
||||||
//console.log(keys[i], item[keys[i]]);
|
|
||||||
element.setAttribute(keys[i], item[keys[i]]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//console.log(element);
|
|
||||||
return element;
|
|
||||||
}
|
|
||||||
|
|
||||||
function modifyOption(id, options, values) {
|
|
||||||
var select = document.getElementById(id);
|
|
||||||
select.innerHTML = "";
|
|
||||||
|
|
||||||
for (var i = 0; i < options.length; i++) {
|
|
||||||
|
|
||||||
var element = document.createElement("OPTION")
|
|
||||||
|
|
||||||
element.value = values[i];
|
|
||||||
element.innerHTML = options[i];
|
|
||||||
|
|
||||||
document.getElementById(id).appendChild(element);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function startWebSocket() {
|
|
||||||
if (webSockets == false) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//ws.send('{"cmd": "getServerConfig1"}');
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkErr(obj) {
|
|
||||||
//alert(obj["err"])
|
|
||||||
//screenLog(obj["err"], "error")
|
|
||||||
console.log(obj);
|
|
||||||
var newObj = new Object();
|
|
||||||
var newErr = new Object();
|
|
||||||
newErr["key"] = "Error";
|
|
||||||
newErr["value"] = obj["err"];
|
|
||||||
newErr["type"] = "error";
|
|
||||||
|
|
||||||
newObj[0] = newErr
|
|
||||||
showLog(newObj);
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
function screenLog(msg, msgType, show) {
|
|
||||||
return
|
|
||||||
clearTimeout(closeLog)
|
|
||||||
var div = document.getElementById("screenLog");
|
|
||||||
var newMsg = new Object();
|
|
||||||
|
|
||||||
newMsg["_element"] = "P";
|
|
||||||
|
|
||||||
switch(msgType) {
|
|
||||||
case "error": newMsg["class"] = "errorMsg"; break;
|
|
||||||
case "warning": newMsg["class"] = "warningMsg"; break;
|
|
||||||
//default: newMsg["class"] = "infoMsg"
|
|
||||||
}
|
|
||||||
|
|
||||||
newMsg["_text"] = msg;
|
|
||||||
|
|
||||||
div.appendChild(createElement(newMsg));
|
|
||||||
|
|
||||||
div.scrollTop = div.scrollHeight;
|
|
||||||
|
|
||||||
if (show == false) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.className = ""
|
|
||||||
closeLog = setTimeout(closeScreenLog, 10000);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function closeScreenLog() {
|
|
||||||
var div = document.getElementById("screenLog");
|
|
||||||
div.className = "screenLogHidden"
|
|
||||||
}
|
|
||||||
|
|
||||||
function showScreenLog() {
|
|
||||||
clearTimeout(closeLog)
|
|
||||||
var div = document.getElementById("screenLog");
|
|
||||||
var currentClass = div.className;
|
|
||||||
div.className = "screenLogHidden"
|
|
||||||
|
|
||||||
switch(currentClass) {
|
|
||||||
case "screenLogHidden": div.className = ""; break;
|
|
||||||
case "": div.className = "screenLogHidden"; break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function showLoadingScreen(elm) {
|
|
||||||
var div = document.getElementById("loading");
|
|
||||||
switch(elm) {
|
|
||||||
case true: div.className = "block"; break;
|
|
||||||
case false: div.className = "none"; break;
|
|
||||||
|
|
||||||
/*
|
|
||||||
case true: div.style.display = "block"; break;
|
|
||||||
case false: div.style.display = "none"; break;
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function createClintInfo(obj) {
|
|
||||||
//console.log(obj);
|
|
||||||
var keys = getObjKeys(obj);
|
|
||||||
for (var i = 0; i < keys.length; i++) {
|
|
||||||
if(document.getElementById(keys[i])){
|
|
||||||
document.getElementById(keys[i]).innerHTML = obj[keys[i]];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//document.getElementById("clientInfo").className = "visible";
|
|
||||||
}
|
|
||||||
|
|
||||||
function showElement(elmID, type) {
|
|
||||||
switch(type) {
|
|
||||||
case true: cssClass = "block"; break;
|
|
||||||
case false: cssClass = "none"; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
document.getElementById(elmID).className = cssClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
function showPopUpElement(elm) {
|
|
||||||
var allElements = new Array("deleteUserDetail", "mapping-detail", "user-detail", "file-detail");
|
|
||||||
|
|
||||||
for (var i = 0; i < allElements.length; i++) {
|
|
||||||
showElement(allElements[i], false)
|
|
||||||
}
|
|
||||||
|
|
||||||
showElement(elm, true)
|
|
||||||
|
|
||||||
setTimeout(function(){
|
|
||||||
showElement("popup", true);
|
|
||||||
}, 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
// body...
|
|
||||||
|
|
||||||
function showStreams(force) {
|
|
||||||
|
|
||||||
var elmBox = document.getElementById("myStreamsBox");
|
|
||||||
var elm = document.getElementById("allStreams");
|
|
||||||
//console.log(elm);
|
|
||||||
show = elm.className;
|
|
||||||
|
|
||||||
switch(force) {
|
|
||||||
case true: show = "notVisible"; break;
|
|
||||||
case false: show = "visible"; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(show) {
|
|
||||||
case "notVisible":
|
|
||||||
elm.className = "visible";
|
|
||||||
elmBox.style.height = "100px";
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
elm.className = "notVisible";
|
|
||||||
elmBox.style.height = "20px";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
var show = elm.style.display; {
|
|
||||||
//console.log(elm.style.display);
|
|
||||||
}
|
|
||||||
|
|
||||||
calculateWrapperHeight();
|
|
||||||
}
|
|
||||||
|
|
||||||
function xteveBackup() {
|
|
||||||
console.log("xteveBackup");
|
|
||||||
var data = new Object();
|
|
||||||
data["cmd"] = "xteveBackup";
|
|
||||||
|
|
||||||
xTeVe(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
function xteveRestore(elm) {
|
|
||||||
var restore = document.createElement("INPUT");
|
|
||||||
restore.setAttribute("type", "file");
|
|
||||||
restore.setAttribute("class", "notVisible");
|
|
||||||
restore.setAttribute("name", "");
|
|
||||||
restore.id = "upload";
|
|
||||||
|
|
||||||
document.body.appendChild(restore);
|
|
||||||
restore.click();
|
|
||||||
|
|
||||||
restore.onchange = function() {
|
|
||||||
var filename = restore.files[0].name
|
|
||||||
//console.log(restore.srcElement.files[0]);
|
|
||||||
var check = confirm("File: " + filename + "\nAll data will be replaced with those from the backup.\nShould the files be restored?");
|
|
||||||
if (check == true) {
|
|
||||||
var reader = new FileReader();
|
|
||||||
var file = document.querySelector('input[type=file]').files[0];
|
|
||||||
if (file) {
|
|
||||||
reader.readAsDataURL(file);
|
|
||||||
reader.onload = function() {
|
|
||||||
console.log(reader.result);
|
|
||||||
var data = new Object();
|
|
||||||
data["cmd"] = "xteveRestore"
|
|
||||||
data["base64"] = reader.result
|
|
||||||
|
|
||||||
xTeVe(data);
|
|
||||||
return
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
alert("File could not be loaded")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getBase64(file) {
|
|
||||||
var reader = new FileReader();
|
|
||||||
reader.readAsDataURL(file);
|
|
||||||
reader.onload = function() {
|
|
||||||
console.log(reader.result);
|
|
||||||
};
|
|
||||||
reader.onerror = function(error) {
|
|
||||||
console.log('Error: ', error);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function logout() {
|
|
||||||
document.cookie.split(';').forEach(function(c) {
|
|
||||||
document.cookie = c.trim().split('=')[0] + '=;' + 'expires=Thu, 01 Jan 1970 00:00:00 UTC;';
|
|
||||||
});
|
|
||||||
location.reload();
|
|
||||||
}
|
|
||||||
|
|
||||||
function getCookie(name) {
|
|
||||||
var value = "; " + document.cookie;
|
|
||||||
var parts = value.split("; " + name + "=");
|
|
||||||
if (parts.length == 2) return parts.pop().split(";").shift();
|
|
||||||
}
|
|
||||||
|
|
||||||
function setCookie(token) {
|
|
||||||
//console.log(token);
|
|
||||||
document.cookie = "Token=" + token
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,615 +0,0 @@
|
|||||||
var SERVER = new Object();
|
|
||||||
var BULK_EDIT = false;
|
|
||||||
var COLUMN_TO_SORT;
|
|
||||||
var SEARCH_MAPPING = new Object();
|
|
||||||
var UNDO = new Object();
|
|
||||||
var SERVER_CONNECTION = false;
|
|
||||||
var WS_AVAILABLE = false;
|
|
||||||
var ACTIVE_MENU_ID = "";
|
|
||||||
var LAST_FOCUSED_ELEMENT = null;
|
|
||||||
var LAST_BULK_CHECKBOX = null;
|
|
||||||
// Menu
|
|
||||||
var menuItems = new Array();
|
|
||||||
menuItems.push(new MainMenuItem("playlist", "{{.mainMenu.item.playlist}}", "m3u.png", "{{.mainMenu.headline.playlist}}"));
|
|
||||||
//menuItems.push(new MainMenuItem("pmsID", "{{.mainMenu.item.pmsID}}", "number.png", "{{.mainMenu.headline.pmsID}}"))
|
|
||||||
menuItems.push(new MainMenuItem("filter", "{{.mainMenu.item.filter}}", "filter.png", "{{.mainMenu.headline.filter}}"));
|
|
||||||
menuItems.push(new MainMenuItem("xmltv", "{{.mainMenu.item.xmltv}}", "xmltv.png", "{{.mainMenu.headline.xmltv}}"));
|
|
||||||
menuItems.push(new MainMenuItem("mapping", "{{.mainMenu.item.mapping}}", "mapping.png", "{{.mainMenu.headline.mapping}}"));
|
|
||||||
menuItems.push(new MainMenuItem("users", "{{.mainMenu.item.users}}", "users.png", "{{.mainMenu.headline.users}}"));
|
|
||||||
menuItems.push(new MainMenuItem("settings", "{{.mainMenu.item.settings}}", "settings.png", "{{.mainMenu.headline.settings}}"));
|
|
||||||
menuItems.push(new MainMenuItem("log", "{{.mainMenu.item.log}}", "log.png", "{{.mainMenu.headline.log}}"));
|
|
||||||
menuItems.push(new MainMenuItem("logout", "{{.mainMenu.item.logout}}", "logout.png", "{{.mainMenu.headline.logout}}"));
|
|
||||||
// Kategorien für die Einstellungen
|
|
||||||
var settingsCategory = new Array();
|
|
||||||
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.general}}", "tuner,epgSource,api,use_plexAPI,plex.url,plex.token"));
|
|
||||||
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.files}}", "update,files.update,temp.path,cache.images,xepg.missing.epg.mode,xepg.replace.missing.images"));
|
|
||||||
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.streaming}}", "buffer,udpxy,buffer.size.kb,buffer.timeout,user.agent,ffmpeg.path,ffmpeg.options,vlc.path,vlc.options"));
|
|
||||||
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.backup}}", "backup.path,backup.keep"));
|
|
||||||
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.authentication}}", "authentication.web,authentication.pms,authentication.m3u,authentication.xml,authentication.api"));
|
|
||||||
function showPopUpElement(elm) {
|
|
||||||
var allElements = new Array("popup-custom");
|
|
||||||
for (var i = 0; i < allElements.length; i++) {
|
|
||||||
showElement(allElements[i], false);
|
|
||||||
}
|
|
||||||
showElement(elm, true);
|
|
||||||
setTimeout(function () {
|
|
||||||
showElement("popup", true);
|
|
||||||
}, 10);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
function showElement(elmID, type) {
|
|
||||||
var cssClass;
|
|
||||||
switch (type) {
|
|
||||||
case true:
|
|
||||||
cssClass = "block";
|
|
||||||
break;
|
|
||||||
case false:
|
|
||||||
cssClass = "none";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
var element = document.getElementById(elmID);
|
|
||||||
if (element == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
element.className = cssClass;
|
|
||||||
element.setAttribute("aria-hidden", type == true ? "false" : "true");
|
|
||||||
if (elmID == "loading" && document.body != null) {
|
|
||||||
document.body.setAttribute("aria-busy", type == true ? "true" : "false");
|
|
||||||
}
|
|
||||||
if (elmID == "popup") {
|
|
||||||
var popupContent_1 = document.getElementById("popup-custom");
|
|
||||||
if (type == true) {
|
|
||||||
LAST_FOCUSED_ELEMENT = document.activeElement;
|
|
||||||
if (popupContent_1 != null) {
|
|
||||||
setTimeout(function () {
|
|
||||||
popupContent_1.focus();
|
|
||||||
}, 20);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (LAST_FOCUSED_ELEMENT != null && LAST_FOCUSED_ELEMENT.focus != undefined) {
|
|
||||||
setTimeout(function () {
|
|
||||||
LAST_FOCUSED_ELEMENT.focus();
|
|
||||||
}, 20);
|
|
||||||
}
|
|
||||||
LAST_FOCUSED_ELEMENT = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function announceToScreenReader(message) {
|
|
||||||
if (message == undefined || message.length == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var region = document.getElementById("sr-announcer");
|
|
||||||
if (region == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
region.innerText = "";
|
|
||||||
setTimeout(function () {
|
|
||||||
region.innerText = message;
|
|
||||||
}, 20);
|
|
||||||
}
|
|
||||||
function setConnectionState(state, text) {
|
|
||||||
var label = text;
|
|
||||||
if (label == undefined || label.length == 0) {
|
|
||||||
switch (state) {
|
|
||||||
case "online":
|
|
||||||
label = "Connected";
|
|
||||||
break;
|
|
||||||
case "busy":
|
|
||||||
label = "Syncing";
|
|
||||||
break;
|
|
||||||
case "offline":
|
|
||||||
label = "Offline";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
label = "Connecting";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var indicator = document.getElementById("connection-indicator");
|
|
||||||
if (indicator == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
indicator.className = "status-" + state;
|
|
||||||
indicator.innerText = label;
|
|
||||||
indicator.setAttribute("aria-label", "Connection status: " + label);
|
|
||||||
announceToScreenReader("Connection status " + label);
|
|
||||||
}
|
|
||||||
function changeButtonAction(element, buttonID, attribute) {
|
|
||||||
var value = element.options[element.selectedIndex].value;
|
|
||||||
document.getElementById(buttonID).setAttribute(attribute, value);
|
|
||||||
}
|
|
||||||
function getLocalData(dataType, id) {
|
|
||||||
var data = new Object();
|
|
||||||
switch (dataType) {
|
|
||||||
case "m3u":
|
|
||||||
data = SERVER["settings"]["files"][dataType][id];
|
|
||||||
break;
|
|
||||||
case "hdhr":
|
|
||||||
data = SERVER["settings"]["files"][dataType][id];
|
|
||||||
break;
|
|
||||||
case "filter":
|
|
||||||
case "custom-filter":
|
|
||||||
case "group-title":
|
|
||||||
if (id == -1) {
|
|
||||||
data["active"] = true;
|
|
||||||
data["caseSensitive"] = false;
|
|
||||||
data["description"] = "";
|
|
||||||
data["exclude"] = "";
|
|
||||||
data["filter"] = "";
|
|
||||||
data["include"] = "";
|
|
||||||
data["name"] = "";
|
|
||||||
data["type"] = "group-title";
|
|
||||||
SERVER["settings"]["filter"][id] = data;
|
|
||||||
}
|
|
||||||
data = SERVER["settings"]["filter"][id];
|
|
||||||
break;
|
|
||||||
case "xmltv":
|
|
||||||
data = SERVER["settings"]["files"][dataType][id];
|
|
||||||
break;
|
|
||||||
case "users":
|
|
||||||
data = SERVER["users"][id]["data"];
|
|
||||||
break;
|
|
||||||
case "mapping":
|
|
||||||
data = SERVER["xepg"]["epgMapping"][id];
|
|
||||||
break;
|
|
||||||
case "m3uGroups":
|
|
||||||
data = SERVER["data"]["playlist"]["m3u"]["groups"];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
function getObjKeys(obj) {
|
|
||||||
var keys = new Array();
|
|
||||||
for (var i in obj) {
|
|
||||||
if (obj.hasOwnProperty(i)) {
|
|
||||||
keys.push(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return keys;
|
|
||||||
}
|
|
||||||
function getAllSelectedChannels() {
|
|
||||||
var channels = new Array();
|
|
||||||
if (BULK_EDIT == false) {
|
|
||||||
return channels;
|
|
||||||
}
|
|
||||||
var trs = document.getElementById("content_table").getElementsByTagName("TR");
|
|
||||||
for (var i = 1; i < trs.length; i++) {
|
|
||||||
if (trs[i].style.display != "none") {
|
|
||||||
if (trs[i].firstChild.firstChild.checked == true) {
|
|
||||||
channels.push(trs[i].id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return channels;
|
|
||||||
}
|
|
||||||
function scheduleChannelRangeSelection(checkbox, event) {
|
|
||||||
var shiftPressed = false;
|
|
||||||
if (event != undefined && event.shiftKey == true) {
|
|
||||||
shiftPressed = true;
|
|
||||||
}
|
|
||||||
// Run after the native checkbox toggle so we copy the final checked state.
|
|
||||||
setTimeout(function () {
|
|
||||||
selectChannelRange(checkbox, shiftPressed);
|
|
||||||
}, 0);
|
|
||||||
}
|
|
||||||
function selectChannelRange(checkbox, shiftPressed) {
|
|
||||||
if (BULK_EDIT == false || checkbox == undefined || checkbox == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var table = document.getElementById("content_table");
|
|
||||||
if (table == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var trs = table.getElementsByTagName("TR");
|
|
||||||
var visibleCheckboxes = new Array();
|
|
||||||
for (var i = 1; i < trs.length; i++) {
|
|
||||||
if (trs[i].style.display != "none") {
|
|
||||||
var bulkCheckbox = trs[i].querySelector("input.bulk");
|
|
||||||
if (bulkCheckbox != null) {
|
|
||||||
visibleCheckboxes.push(bulkCheckbox);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var currentIndex = visibleCheckboxes.indexOf(checkbox);
|
|
||||||
var previousIndex = -1;
|
|
||||||
if (LAST_BULK_CHECKBOX != null) {
|
|
||||||
previousIndex = visibleCheckboxes.indexOf(LAST_BULK_CHECKBOX);
|
|
||||||
}
|
|
||||||
if (shiftPressed == true && previousIndex > -1 && currentIndex > -1) {
|
|
||||||
var start = Math.min(previousIndex, currentIndex);
|
|
||||||
var end = Math.max(previousIndex, currentIndex);
|
|
||||||
for (var i = start; i <= end; i++) {
|
|
||||||
visibleCheckboxes[i].checked = checkbox.checked;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LAST_BULK_CHECKBOX = checkbox;
|
|
||||||
}
|
|
||||||
function selectAllChannels() {
|
|
||||||
var bulk = false;
|
|
||||||
var trs = document.getElementById("content_table").getElementsByTagName("TR");
|
|
||||||
if (trs[0].firstChild.firstChild.checked == true) {
|
|
||||||
bulk = true;
|
|
||||||
}
|
|
||||||
for (var i = 1; i < trs.length; i++) {
|
|
||||||
if (trs[i].style.display != "none") {
|
|
||||||
switch (bulk) {
|
|
||||||
case true:
|
|
||||||
trs[i].firstChild.firstChild.checked = true;
|
|
||||||
break;
|
|
||||||
case false:
|
|
||||||
trs[i].firstChild.firstChild.checked = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LAST_BULK_CHECKBOX = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
function bulkEdit() {
|
|
||||||
BULK_EDIT = !BULK_EDIT;
|
|
||||||
var className;
|
|
||||||
var rows = document.getElementsByClassName("bulk");
|
|
||||||
switch (BULK_EDIT) {
|
|
||||||
case true:
|
|
||||||
className = "bulk showBulk";
|
|
||||||
break;
|
|
||||||
case false:
|
|
||||||
className = "bulk hideBulk";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
for (var i = 0; i < rows.length; i++) {
|
|
||||||
rows[i].className = className;
|
|
||||||
rows[i].checked = false;
|
|
||||||
}
|
|
||||||
LAST_BULK_CHECKBOX = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
function sortTable(column) {
|
|
||||||
//console.log(columm);
|
|
||||||
if (column == COLUMN_TO_SORT) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var table = document.getElementById("content_table");
|
|
||||||
var tableHead = table.getElementsByTagName("TR")[0];
|
|
||||||
var tableItems = tableHead.getElementsByTagName("TD");
|
|
||||||
for (var h = 0; h < tableItems.length; h++) {
|
|
||||||
if (tableItems[h].getAttribute("role") == "columnheader") {
|
|
||||||
tableItems[h].setAttribute("aria-sort", "none");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var sortObj = new Object();
|
|
||||||
var x, xValue;
|
|
||||||
var tableHeader;
|
|
||||||
var sortByString = false;
|
|
||||||
if (column > 0 && COLUMN_TO_SORT > 0) {
|
|
||||||
tableItems[COLUMN_TO_SORT].classList.remove("sortThis");
|
|
||||||
tableItems[COLUMN_TO_SORT].classList.add("pointer");
|
|
||||||
tableItems[column].classList.remove("pointer");
|
|
||||||
tableItems[column].classList.add("sortThis");
|
|
||||||
}
|
|
||||||
COLUMN_TO_SORT = column;
|
|
||||||
var mobileSort = document.getElementById("mapping-sort-mobile");
|
|
||||||
if (mobileSort != null && (column == 1 || column == 3 || column == 4 || column == 5)) {
|
|
||||||
mobileSort.value = column.toString();
|
|
||||||
}
|
|
||||||
if (tableItems[column] != undefined && tableItems[column].getAttribute("role") == "columnheader") {
|
|
||||||
tableItems[column].setAttribute("aria-sort", "ascending");
|
|
||||||
}
|
|
||||||
var rows = table.rows;
|
|
||||||
if (rows[1] != undefined) {
|
|
||||||
tableHeader = rows[0];
|
|
||||||
x = rows[1].getElementsByTagName("TD")[column];
|
|
||||||
for (i = 1; i < rows.length; i++) {
|
|
||||||
x = rows[i].getElementsByTagName("TD")[column];
|
|
||||||
switch (x.childNodes[0].tagName.toLowerCase()) {
|
|
||||||
case "input":
|
|
||||||
xValue = x.getElementsByTagName("INPUT")[0].value.toLowerCase();
|
|
||||||
break;
|
|
||||||
case "p":
|
|
||||||
xValue = x.getElementsByTagName("P")[0].innerText.toLowerCase();
|
|
||||||
break;
|
|
||||||
default: console.log(x.childNodes[0].tagName);
|
|
||||||
}
|
|
||||||
if (xValue == "" || xValue == NaN) {
|
|
||||||
xValue = i;
|
|
||||||
sortObj[i] = rows[i];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
switch (isNaN(xValue)) {
|
|
||||||
case false:
|
|
||||||
xValue = parseFloat(xValue);
|
|
||||||
sortObj[xValue] = rows[i];
|
|
||||||
break;
|
|
||||||
case true:
|
|
||||||
sortByString = true;
|
|
||||||
sortObj[xValue.toLowerCase() + i] = rows[i];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while (table.firstChild) {
|
|
||||||
table.removeChild(table.firstChild);
|
|
||||||
}
|
|
||||||
var sortValues = getObjKeys(sortObj);
|
|
||||||
if (sortByString == true) {
|
|
||||||
sortValues.sort();
|
|
||||||
console.log(sortValues);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
function sortFloat(a, b) {
|
|
||||||
return a - b;
|
|
||||||
}
|
|
||||||
sortValues.sort(sortFloat);
|
|
||||||
}
|
|
||||||
table.appendChild(tableHeader);
|
|
||||||
for (var i = 0; i < sortValues.length; i++) {
|
|
||||||
table.appendChild(sortObj[sortValues[i]]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
function createSearchObj() {
|
|
||||||
SEARCH_MAPPING = new Object();
|
|
||||||
var data = SERVER["xepg"]["epgMapping"];
|
|
||||||
var channels = getObjKeys(data);
|
|
||||||
var channelKeys = ["x-active", "x-channelID", "x-name", "_file.m3u.name", "x-group-title", "x-xmltv-file"];
|
|
||||||
channels.forEach(function (id) {
|
|
||||||
SEARCH_MAPPING[id] = "";
|
|
||||||
channelKeys.forEach(function (key) {
|
|
||||||
if (key == "x-active") {
|
|
||||||
switch (data[id][key]) {
|
|
||||||
case true:
|
|
||||||
SEARCH_MAPPING[id] = "online ";
|
|
||||||
break;
|
|
||||||
case false:
|
|
||||||
SEARCH_MAPPING[id] = "offline ";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (key == "x-xmltv-file") {
|
|
||||||
var xmltvFile = getValueFromProviderFile(data[id][key], "xmltv", "name");
|
|
||||||
if (xmltvFile != undefined) {
|
|
||||||
SEARCH_MAPPING[id] = SEARCH_MAPPING[id] + xmltvFile + " ";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
SEARCH_MAPPING[id] = SEARCH_MAPPING[id] + data[id][key] + " ";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
function searchInMapping() {
|
|
||||||
var searchValue = document.getElementById("searchMapping").value;
|
|
||||||
var trs = document.getElementById("content_table").getElementsByTagName("TR");
|
|
||||||
for (var i = 1; i < trs.length; ++i) {
|
|
||||||
var id = trs[i].getAttribute("id");
|
|
||||||
var element = SEARCH_MAPPING[id];
|
|
||||||
if (element == undefined) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
switch (element.toLowerCase().includes(searchValue.toLowerCase())) {
|
|
||||||
case true:
|
|
||||||
document.getElementById(id).style.display = "";
|
|
||||||
break;
|
|
||||||
case false:
|
|
||||||
document.getElementById(id).style.display = "none";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
announceToScreenReader("Search updated");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
function calculateWrapperHeight() {
|
|
||||||
var elm = document.getElementById("box-wrapper");
|
|
||||||
var content = document.getElementById("content");
|
|
||||||
if (elm != null && content != null) {
|
|
||||||
var contentTop = content.getBoundingClientRect().top;
|
|
||||||
var freeSpace = window.innerHeight - contentTop - 26;
|
|
||||||
if (freeSpace < 180) {
|
|
||||||
freeSpace = 180;
|
|
||||||
}
|
|
||||||
elm.style.height = freeSpace + "px";
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
function changeChannelNumber(element) {
|
|
||||||
var dbID = element.parentNode.parentNode.id;
|
|
||||||
var newNumber = parseFloat(element.value);
|
|
||||||
var channelNumbers = [];
|
|
||||||
var data = SERVER["xepg"]["epgMapping"];
|
|
||||||
var channels = getObjKeys(data);
|
|
||||||
if (isNaN(newNumber)) {
|
|
||||||
alert("{{.alert.invalidChannelNumber}}");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
channels.forEach(function (id) {
|
|
||||||
var channelNumber = parseFloat(data[id]["x-channelID"]);
|
|
||||||
channelNumbers.push(channelNumber);
|
|
||||||
});
|
|
||||||
for (var i = 0; i < channelNumbers.length; i++) {
|
|
||||||
if (channelNumbers.indexOf(newNumber) == -1) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (Math.floor(newNumber) == newNumber) {
|
|
||||||
newNumber = newNumber + 1;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
newNumber = newNumber + 0.1;
|
|
||||||
newNumber.toFixed(1);
|
|
||||||
newNumber = Math.round(newNumber * 10) / 10;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
data[dbID]["x-channelID"] = newNumber.toString();
|
|
||||||
element.value = newNumber;
|
|
||||||
console.log(data[dbID]["x-channelID"]);
|
|
||||||
if (COLUMN_TO_SORT == 1) {
|
|
||||||
COLUMN_TO_SORT = -1;
|
|
||||||
sortTable(1);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
function backup() {
|
|
||||||
var data = new Object();
|
|
||||||
console.log("Backup data");
|
|
||||||
var cmd = "xteveBackup";
|
|
||||||
console.log("SEND TO SERVER");
|
|
||||||
console.log(data);
|
|
||||||
var server = new Server(cmd);
|
|
||||||
server.request(data);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
function toggleChannelStatus(id) {
|
|
||||||
var element;
|
|
||||||
var status;
|
|
||||||
if (document.getElementById("active")) {
|
|
||||||
var checkbox = document.getElementById("active");
|
|
||||||
status = (checkbox).checked;
|
|
||||||
}
|
|
||||||
var ids = getAllSelectedChannels();
|
|
||||||
if (ids.length == 0) {
|
|
||||||
ids.push(id);
|
|
||||||
}
|
|
||||||
ids.forEach(function (id) {
|
|
||||||
var channel = SERVER["xepg"]["epgMapping"][id];
|
|
||||||
channel["x-active"] = status;
|
|
||||||
switch (channel["x-active"]) {
|
|
||||||
case true:
|
|
||||||
if (channel["x-xmltv-file"] == "-" || channel["x-mapping"] == "-") {
|
|
||||||
if (BULK_EDIT == false) {
|
|
||||||
alert(channel["x-name"] + ": Missing XMLTV file / channel");
|
|
||||||
checkbox.checked = false;
|
|
||||||
}
|
|
||||||
channel["x-active"] = false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case false:
|
|
||||||
// code...
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (channel["x-active"] == false) {
|
|
||||||
document.getElementById(id).className = "notActiveEPG";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
document.getElementById(id).className = "activeEPG";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
function restore() {
|
|
||||||
if (document.getElementById('upload')) {
|
|
||||||
document.getElementById('upload').remove();
|
|
||||||
}
|
|
||||||
var restore = document.createElement("INPUT");
|
|
||||||
restore.setAttribute("type", "file");
|
|
||||||
restore.setAttribute("class", "notVisible");
|
|
||||||
restore.setAttribute("name", "");
|
|
||||||
restore.id = "upload";
|
|
||||||
document.body.appendChild(restore);
|
|
||||||
restore.click();
|
|
||||||
restore.onchange = function () {
|
|
||||||
var filename = restore.files[0].name;
|
|
||||||
var check = confirm("File: " + filename + "\n{{.confirm.restore}}");
|
|
||||||
if (check == true) {
|
|
||||||
var reader = new FileReader();
|
|
||||||
var file = document.querySelector('input[type=file]').files[0];
|
|
||||||
if (file) {
|
|
||||||
reader.readAsDataURL(file);
|
|
||||||
reader.onload = function () {
|
|
||||||
console.log(reader.result);
|
|
||||||
var data = new Object();
|
|
||||||
var cmd = "xteveRestore";
|
|
||||||
data["base64"] = reader.result;
|
|
||||||
var server = new Server(cmd);
|
|
||||||
server.request(data);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
alert("File could not be loaded");
|
|
||||||
}
|
|
||||||
restore.remove();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
function uploadLogo() {
|
|
||||||
if (document.getElementById('upload')) {
|
|
||||||
document.getElementById('upload').remove();
|
|
||||||
}
|
|
||||||
var upload = document.createElement("INPUT");
|
|
||||||
upload.setAttribute("type", "file");
|
|
||||||
upload.setAttribute("class", "notVisible");
|
|
||||||
upload.setAttribute("name", "");
|
|
||||||
upload.id = "upload";
|
|
||||||
document.body.appendChild(upload);
|
|
||||||
upload.click();
|
|
||||||
upload.onblur = function () {
|
|
||||||
alert();
|
|
||||||
};
|
|
||||||
upload.onchange = function () {
|
|
||||||
var filename = upload.files[0].name;
|
|
||||||
var reader = new FileReader();
|
|
||||||
var file = document.querySelector('input[type=file]').files[0];
|
|
||||||
if (file) {
|
|
||||||
reader.readAsDataURL(file);
|
|
||||||
reader.onload = function () {
|
|
||||||
console.log(reader.result);
|
|
||||||
var data = new Object();
|
|
||||||
var cmd = "uploadLogo";
|
|
||||||
data["base64"] = reader.result;
|
|
||||||
data["filename"] = file.name;
|
|
||||||
var server = new Server(cmd);
|
|
||||||
server.request(data);
|
|
||||||
var updateLogo = document.getElementById('update-icon');
|
|
||||||
updateLogo.checked = false;
|
|
||||||
updateLogo.className = "changed";
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
alert("File could not be loaded");
|
|
||||||
}
|
|
||||||
upload.remove();
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
function checkUndo(key) {
|
|
||||||
switch (key) {
|
|
||||||
case "epgMapping":
|
|
||||||
if (UNDO.hasOwnProperty(key)) {
|
|
||||||
SERVER["xepg"][key] = JSON.parse(JSON.stringify(UNDO[key]));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
UNDO[key] = JSON.parse(JSON.stringify(SERVER["xepg"][key]));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
function sortSelect(elem) {
|
|
||||||
var tmpAry = [];
|
|
||||||
var selectedValue = elem[elem.selectedIndex].value;
|
|
||||||
for (var i = 0; i < elem.options.length; i++)
|
|
||||||
tmpAry.push(elem.options[i]);
|
|
||||||
tmpAry.sort(function (a, b) { return (a.text < b.text) ? -1 : 1; });
|
|
||||||
while (elem.options.length > 0)
|
|
||||||
elem.options[0] = null;
|
|
||||||
var newSelectedIndex = 0;
|
|
||||||
for (var i = 0; i < tmpAry.length; i++) {
|
|
||||||
elem.options[i] = tmpAry[i];
|
|
||||||
if (elem.options[i].value == selectedValue)
|
|
||||||
newSelectedIndex = i;
|
|
||||||
}
|
|
||||||
elem.selectedIndex = newSelectedIndex; // Set new selected index after sorting
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
function updateLog() {
|
|
||||||
console.log("TOKEN");
|
|
||||||
var server = new Server("updateLog");
|
|
||||||
server.request(new Object());
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
var __extends = (this && this.__extends) || (function () {
|
|
||||||
var extendStatics = function (d, b) {
|
|
||||||
extendStatics = Object.setPrototypeOf ||
|
|
||||||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
|
||||||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
|
|
||||||
return extendStatics(d, b);
|
|
||||||
};
|
|
||||||
return function (d, b) {
|
|
||||||
extendStatics(d, b);
|
|
||||||
function __() { this.constructor = d; }
|
|
||||||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
||||||
};
|
|
||||||
})();
|
|
||||||
var MainMenu = /** @class */ (function () {
|
|
||||||
function MainMenu() {
|
|
||||||
this.DocumentID = "main-menu";
|
|
||||||
this.HTMLTag = "LI";
|
|
||||||
}
|
|
||||||
MainMenu.prototype.create = function () {
|
|
||||||
console.log(this.DocumentID);
|
|
||||||
};
|
|
||||||
return MainMenu;
|
|
||||||
}());
|
|
||||||
var MainMenuItem = /** @class */ (function (_super) {
|
|
||||||
__extends(MainMenuItem, _super);
|
|
||||||
function MainMenuItem() {
|
|
||||||
return _super !== null && _super.apply(this, arguments) || this;
|
|
||||||
}
|
|
||||||
MainMenuItem.prototype.create2 = function () {
|
|
||||||
var element = document.createElement(this.HTMLTag);
|
|
||||||
element.innerText = this.Value;
|
|
||||||
console.log(element);
|
|
||||||
};
|
|
||||||
return MainMenuItem;
|
|
||||||
}(MainMenu));
|
|
||||||
function pageReady() {
|
|
||||||
var item = new MainMenuItem();
|
|
||||||
item.Value = "Test";
|
|
||||||
item.create2();
|
|
||||||
}
|
|
||||||
@@ -1,294 +0,0 @@
|
|||||||
var configMenu = new Object();
|
|
||||||
var wizard = new Array("key", "tuner", "epgSource", "m3u", "complete");
|
|
||||||
var activeWizard;
|
|
||||||
var dvrIP
|
|
||||||
|
|
||||||
var configMenu_tuner = new Object();
|
|
||||||
configMenu_tuner["_element"] = "SELECT";
|
|
||||||
configMenu_tuner["_menuType"] = "singleInput";
|
|
||||||
configMenu_tuner["_configKey"] = "tuner";
|
|
||||||
configMenu_tuner["_label"] = "Available tuners";
|
|
||||||
configMenu_tuner["name"] = "tuner";
|
|
||||||
configMenu_tuner["id"] = "Tuner";
|
|
||||||
configMenu_tuner["placeholder"] = "Tuner";
|
|
||||||
configMenu_tuner["_usage"] = "This setting is only used by Plex and Emby.<br>The number of concurrent streams allowed by the IPTV provider."
|
|
||||||
|
|
||||||
|
|
||||||
var optionValues = new Array();
|
|
||||||
for (var i = 1; i <= 100; i++) {
|
|
||||||
optionValues.push(i)
|
|
||||||
}
|
|
||||||
configMenu_tuner["_optionValues"] = optionValues;
|
|
||||||
|
|
||||||
var configMenu_epg = new Object();
|
|
||||||
configMenu_epg["_element"] = "SELECT";
|
|
||||||
configMenu_epg["_menuType"] = "singleInput";
|
|
||||||
configMenu_epg["_configKey"] = "epgSource";
|
|
||||||
configMenu_epg["_label"] = "Selection of the EPG source";
|
|
||||||
configMenu_epg["name"] = "epgSource";
|
|
||||||
configMenu_epg["id"] = "EPG source";
|
|
||||||
configMenu_epg["placeholder"] = "EPG source";
|
|
||||||
configMenu_epg["_optionValues"] = new Array("PMS", "XEPG");
|
|
||||||
configMenu_epg["_usage"] = "PMS: Use EPG data from Plex or Emby<br>XEPG: Use of external EPG data (XMLTV)<br> Several XMLTV sources possible<br> Allows editing and order channels<br> M3U / XMLTV export (HTTP link for IPTV apps)"
|
|
||||||
|
|
||||||
var configMenu_m3u = new Object();
|
|
||||||
configMenu_m3u["_element"] = "INPUT";
|
|
||||||
configMenu_m3u["_menuType"] = "inputArray";
|
|
||||||
configMenu_m3u["_configKey"] = "file";
|
|
||||||
configMenu_m3u["_label"] = "M3U File: local or remote";
|
|
||||||
configMenu_m3u["name"] = "file";
|
|
||||||
configMenu_m3u["id"] = "m3u";
|
|
||||||
configMenu_m3u["type"] = "text";
|
|
||||||
configMenu_m3u["placeholder"] = "M3U File";
|
|
||||||
configMenu_m3u["_usage"] = "Remote playlist: http://your.provider.com/file.m3u<br>Local playlist: /path/to/file.m3u"
|
|
||||||
|
|
||||||
|
|
||||||
configMenu_m3u["value"] = "http://websrv.local:8080/kabel.m3u";
|
|
||||||
|
|
||||||
var configMenu_complete = new Object();
|
|
||||||
configMenu_complete["_element"] = "H2";
|
|
||||||
configMenu_complete["_menuType"] = "inputArray";
|
|
||||||
configMenu_complete["_configKey"] = "file";
|
|
||||||
configMenu_complete["_text"] = "xTeVe was successfully set up";
|
|
||||||
configMenu_complete["name"] = "complete";
|
|
||||||
configMenu_complete["id"] = "complete";
|
|
||||||
configMenu_complete["type"] = "text";
|
|
||||||
configMenu_complete["class"] = "center";
|
|
||||||
|
|
||||||
configMenu["tuner"] = configMenu_tuner;
|
|
||||||
configMenu["epgSource"] = configMenu_epg;
|
|
||||||
configMenu["m3u"] = configMenu_m3u;
|
|
||||||
configMenu["complete"] = configMenu_complete;
|
|
||||||
|
|
||||||
function readyForConfiguration() {
|
|
||||||
var data = new Object();
|
|
||||||
data["cmd"] = "getServerConfig";
|
|
||||||
xTeVe(data);
|
|
||||||
showLoadingScreen(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
function createConfiguration(elm) {
|
|
||||||
|
|
||||||
activeWizard = elm;
|
|
||||||
var item = configMenu[elm];
|
|
||||||
|
|
||||||
var div = document.getElementById("content");
|
|
||||||
div.innerHTML = "";
|
|
||||||
div.setAttribute("data-configKey", item["_configKey"]);
|
|
||||||
div.setAttribute("data-menuType", item["_menuType"]);
|
|
||||||
|
|
||||||
switch(item.hasOwnProperty("_label")) {
|
|
||||||
case true:
|
|
||||||
var newItem = new Object();
|
|
||||||
newItem["_element"] = "LABEL";
|
|
||||||
newItem["_text"] = item["_label"];
|
|
||||||
newItem["for"] = item["id"];
|
|
||||||
div.appendChild(createElement(newItem));
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(item["_element"]) {
|
|
||||||
case "SELECT":
|
|
||||||
div.appendChild(createElement(item));
|
|
||||||
var selectElement = div.getElementsByTagName("SELECT")[0];
|
|
||||||
var values = item["_optionValues"];
|
|
||||||
for (var i = 0; i < values.length; i++) {
|
|
||||||
var newEntry = new Object;
|
|
||||||
newEntry["_element"] = "OPTION";
|
|
||||||
newEntry["_text"] = item["id"] + ": " + values[i];
|
|
||||||
newEntry["value"] = values[i];
|
|
||||||
selectElement.appendChild(createElement(newEntry));
|
|
||||||
}
|
|
||||||
//return
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
div.appendChild(createElement(item));
|
|
||||||
break;
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
//alert()
|
|
||||||
|
|
||||||
switch(item.hasOwnProperty("_usage")) {
|
|
||||||
case true:
|
|
||||||
var usageItem = new Object();
|
|
||||||
usageItem["_element"] = "PRE"
|
|
||||||
usageItem["_text"] = item["_usage"];
|
|
||||||
div.appendChild(createElement(usageItem));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (activeWizard == "complete") {
|
|
||||||
document.getElementById("next").value = "Finished"
|
|
||||||
//document.getElementById("next").setAttribute("onclick", "javascript: location.reload();")
|
|
||||||
}
|
|
||||||
|
|
||||||
//div.appendChild(createElement(item));
|
|
||||||
}
|
|
||||||
|
|
||||||
function saveData() {
|
|
||||||
|
|
||||||
var div = document.getElementById("content");
|
|
||||||
var inputs = div.getElementsByTagName("INPUT");
|
|
||||||
var selects = div.getElementsByTagName("SELECT");
|
|
||||||
var value;
|
|
||||||
var data = new Object();
|
|
||||||
var valueArr = new Array();
|
|
||||||
var newData = false;
|
|
||||||
|
|
||||||
if (activeWizard == "complete") {
|
|
||||||
data["cmd"] = "wizardCompleted";
|
|
||||||
showLoadingScreen(true)
|
|
||||||
xTeVe(data);
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var i = 0; i < inputs.length; i++) {
|
|
||||||
var menuType = inputs[i].parentElement.getAttribute("data-menutype");
|
|
||||||
if (inputs[i].value != undefined && inputs[i].value != "" ) {
|
|
||||||
newData = true;
|
|
||||||
|
|
||||||
console.log(inputs[i].id)
|
|
||||||
switch(inputs[i].id) {
|
|
||||||
case "m3u":
|
|
||||||
var newPlaylist = new Object();
|
|
||||||
newPlaylist["file.source"] = inputs[i].value;
|
|
||||||
//newPlaylist["name"] = inputs[i].value;
|
|
||||||
newPlaylist["type"] = "m3u";
|
|
||||||
newPlaylist["new"] = true;
|
|
||||||
|
|
||||||
data["files"] = new Object();
|
|
||||||
data["files"]["m3u"] = new Object();
|
|
||||||
data["files"]["m3u"]["-"] = newPlaylist;
|
|
||||||
|
|
||||||
data["cmd"] = "saveFilesM3U";
|
|
||||||
xTeVe(data)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
switch(menuType) {
|
|
||||||
case "singleInput":
|
|
||||||
data[inputs[i].name] = inputs[i].value; break;
|
|
||||||
case "inputArray":
|
|
||||||
valueArr.push(inputs[i].value);
|
|
||||||
data[inputs[i].name] = valueArr; break
|
|
||||||
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
} else {
|
|
||||||
inputs[i].style.borderBottomColor = "red";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
for (var i = 0; i < selects.length; i++) {
|
|
||||||
var value = selects[i].options[selects[i].selectedIndex].value;
|
|
||||||
if (isNaN(value) == false) {
|
|
||||||
value = parseInt(value);
|
|
||||||
data[selects[i].name] = value;
|
|
||||||
newData = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
data[selects[i].name] = value;
|
|
||||||
newData = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//console.log(data, newData);
|
|
||||||
if (newData == true) {
|
|
||||||
config = data
|
|
||||||
data["cmd"] = "saveConfig";
|
|
||||||
xTeVe(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function xTeVe(data) {
|
|
||||||
|
|
||||||
if (webSockets == false) {
|
|
||||||
alert("Your browser does not support WebSockets");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (activeWizard == "m3u" || activeWizard == "epgSource") {
|
|
||||||
showLoadingScreen(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
var protocolWS
|
|
||||||
switch(window.location.protocol) {
|
|
||||||
case "http:": protocolWS = "ws://"; break;
|
|
||||||
case "https:": protocolWS = "wss://"; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
var ws = new WebSocket(protocolWS + window.location.hostname + ":" + window.location.port + "/data/" + "?Token=" + getCookie("Token"));
|
|
||||||
|
|
||||||
ws.onopen = function() {
|
|
||||||
ws.send(JSON.stringify(data));
|
|
||||||
}
|
|
||||||
|
|
||||||
ws.onmessage = function (e) {
|
|
||||||
|
|
||||||
var response = JSON.parse(e.data);
|
|
||||||
|
|
||||||
if (response.hasOwnProperty("clientInfo")) {
|
|
||||||
createClintInfo(response["clientInfo"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response.hasOwnProperty("status")) {
|
|
||||||
if (response["status"] == false) {
|
|
||||||
document.getElementById("headline").style.borderColor = "red";
|
|
||||||
showErr(response["err"]);
|
|
||||||
showLoadingScreen(false)
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
document.getElementById("err").innerHTML = "";
|
|
||||||
document.getElementById("headline").style.borderColor = "lawngreen";
|
|
||||||
}
|
|
||||||
|
|
||||||
dvrIP = response["DVR"]
|
|
||||||
switch(response["configurationWizard"]) {
|
|
||||||
case true:
|
|
||||||
if (activeWizard == undefined) {
|
|
||||||
activeWizard = wizard[0]
|
|
||||||
}
|
|
||||||
var n = wizard.indexOf(activeWizard);
|
|
||||||
n++;
|
|
||||||
activeWizard = wizard[n]
|
|
||||||
|
|
||||||
if (activeWizard == undefined) {
|
|
||||||
data["cmd"] = "wizardCompleted";
|
|
||||||
xTeVe(data)
|
|
||||||
} else {
|
|
||||||
//console.log(activeWizard);
|
|
||||||
createConfiguration(activeWizard);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(response["reload"]) {
|
|
||||||
|
|
||||||
|
|
||||||
case true:
|
|
||||||
|
|
||||||
setTimeout(function(){
|
|
||||||
location.reload();
|
|
||||||
}, 100);
|
|
||||||
|
|
||||||
//location.reload();
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
setTimeout(function(){ showLoadingScreen(false); }, 300);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function showErr(elm) {
|
|
||||||
document.getElementById("err").innerHTML = elm;
|
|
||||||
}
|
|
||||||
@@ -1,182 +0,0 @@
|
|||||||
var __extends = (this && this.__extends) || (function () {
|
|
||||||
var extendStatics = function (d, b) {
|
|
||||||
extendStatics = Object.setPrototypeOf ||
|
|
||||||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
|
||||||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
|
|
||||||
return extendStatics(d, b);
|
|
||||||
};
|
|
||||||
return function (d, b) {
|
|
||||||
extendStatics(d, b);
|
|
||||||
function __() { this.constructor = d; }
|
|
||||||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
||||||
};
|
|
||||||
})();
|
|
||||||
var WizardCategory = /** @class */ (function () {
|
|
||||||
function WizardCategory() {
|
|
||||||
this.DocumentID = "content";
|
|
||||||
}
|
|
||||||
WizardCategory.prototype.createCategoryHeadline = function (value) {
|
|
||||||
var element = document.createElement("H4");
|
|
||||||
element.innerHTML = value;
|
|
||||||
return element;
|
|
||||||
};
|
|
||||||
return WizardCategory;
|
|
||||||
}());
|
|
||||||
var WizardItem = /** @class */ (function (_super) {
|
|
||||||
__extends(WizardItem, _super);
|
|
||||||
function WizardItem(key, headline) {
|
|
||||||
var _this = _super.call(this) || this;
|
|
||||||
_this.headline = headline;
|
|
||||||
_this.key = key;
|
|
||||||
return _this;
|
|
||||||
}
|
|
||||||
WizardItem.prototype.createWizard = function () {
|
|
||||||
var headline = this.createCategoryHeadline(this.headline);
|
|
||||||
var key = this.key;
|
|
||||||
var content = new PopupContent();
|
|
||||||
var description;
|
|
||||||
var wizardField = null;
|
|
||||||
var doc = document.getElementById(this.DocumentID);
|
|
||||||
doc.setAttribute("aria-busy", "true");
|
|
||||||
doc.innerHTML = "";
|
|
||||||
doc.appendChild(headline);
|
|
||||||
switch (key) {
|
|
||||||
case "tuner":
|
|
||||||
var text = new Array();
|
|
||||||
var values = new Array();
|
|
||||||
for (var i = 1; i <= 100; i++) {
|
|
||||||
text.push(i);
|
|
||||||
values.push(i);
|
|
||||||
}
|
|
||||||
var select = content.createSelect(text, values, "1", key);
|
|
||||||
select.setAttribute("class", "wizard");
|
|
||||||
select.id = key;
|
|
||||||
doc.appendChild(select);
|
|
||||||
wizardField = select;
|
|
||||||
description = "{{.wizard.tuner.description}}";
|
|
||||||
break;
|
|
||||||
case "epgSource":
|
|
||||||
var text = ["PMS", "XEPG"];
|
|
||||||
var values = ["PMS", "XEPG"];
|
|
||||||
var select = content.createSelect(text, values, "XEPG", key);
|
|
||||||
select.setAttribute("class", "wizard");
|
|
||||||
select.id = key;
|
|
||||||
doc.appendChild(select);
|
|
||||||
wizardField = select;
|
|
||||||
description = "{{.wizard.epgSource.description}}";
|
|
||||||
break;
|
|
||||||
case "m3u":
|
|
||||||
var input = content.createInput("text", key, "");
|
|
||||||
input.setAttribute("placeholder", "{{.wizard.m3u.placeholder}}");
|
|
||||||
input.setAttribute("class", "wizard");
|
|
||||||
input.id = key;
|
|
||||||
doc.appendChild(input);
|
|
||||||
wizardField = input;
|
|
||||||
description = "{{.wizard.m3u.description}}";
|
|
||||||
break;
|
|
||||||
case "xmltv":
|
|
||||||
var input = content.createInput("text", key, "");
|
|
||||||
input.setAttribute("placeholder", "{{.wizard.xmltv.placeholder}}");
|
|
||||||
input.setAttribute("class", "wizard");
|
|
||||||
input.id = key;
|
|
||||||
doc.appendChild(input);
|
|
||||||
wizardField = input;
|
|
||||||
description = "{{.wizard.xmltv.description}}";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
console.log(key);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
var pre = document.createElement("PRE");
|
|
||||||
pre.id = "wizard-description-" + key;
|
|
||||||
pre.innerHTML = description;
|
|
||||||
doc.appendChild(pre);
|
|
||||||
if (wizardField != null) {
|
|
||||||
wizardField.setAttribute("aria-label", this.headline);
|
|
||||||
wizardField.setAttribute("aria-describedby", pre.id);
|
|
||||||
setTimeout(function () {
|
|
||||||
wizardField.focus();
|
|
||||||
}, 20);
|
|
||||||
}
|
|
||||||
doc.setAttribute("aria-busy", "false");
|
|
||||||
if (typeof announceToScreenReader == "function") {
|
|
||||||
announceToScreenReader(this.headline + " step");
|
|
||||||
}
|
|
||||||
console.log(headline, key);
|
|
||||||
};
|
|
||||||
return WizardItem;
|
|
||||||
}(WizardCategory));
|
|
||||||
function readyForConfiguration(wizard) {
|
|
||||||
var server = new Server("getServerConfig");
|
|
||||||
server.request(new Object());
|
|
||||||
showElement("loading", false);
|
|
||||||
configurationWizard[wizard].createWizard();
|
|
||||||
}
|
|
||||||
function saveWizard() {
|
|
||||||
var cmd = "saveWizard";
|
|
||||||
var div = document.getElementById("content");
|
|
||||||
var config = div.getElementsByClassName("wizard");
|
|
||||||
var wizard = new Object();
|
|
||||||
for (var i = 0; i < config.length; i++) {
|
|
||||||
var name;
|
|
||||||
var value;
|
|
||||||
switch (config[i].tagName) {
|
|
||||||
case "SELECT":
|
|
||||||
name = config[i].name;
|
|
||||||
value = config[i].value;
|
|
||||||
// Wenn der Wert eine Zahl ist, wird dieser als Zahl gespeichert
|
|
||||||
if (isNaN(value)) {
|
|
||||||
wizard[name] = value;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
wizard[name] = parseInt(value);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "INPUT":
|
|
||||||
switch (config[i].type) {
|
|
||||||
case "text":
|
|
||||||
name = config[i].name;
|
|
||||||
value = config[i].value;
|
|
||||||
if (value.length == 0) {
|
|
||||||
var msg = name.toUpperCase() + ": " + "{{.alert.missingInput}}";
|
|
||||||
alert(msg);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
wizard[name] = value;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
// code...
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var data = new Object();
|
|
||||||
data["wizard"] = wizard;
|
|
||||||
var server = new Server(cmd);
|
|
||||||
server.request(data);
|
|
||||||
console.log(data);
|
|
||||||
}
|
|
||||||
// Wizard
|
|
||||||
var configurationWizard = new Array();
|
|
||||||
configurationWizard.push(new WizardItem("tuner", "{{.wizard.tuner.title}}"));
|
|
||||||
configurationWizard.push(new WizardItem("epgSource", "{{.wizard.epgSource.title}}"));
|
|
||||||
configurationWizard.push(new WizardItem("m3u", "{{.wizard.m3u.title}}"));
|
|
||||||
configurationWizard.push(new WizardItem("xmltv", "{{.wizard.xmltv.title}}"));
|
|
||||||
document.addEventListener("DOMContentLoaded", function () {
|
|
||||||
var container = document.getElementById("content");
|
|
||||||
if (container == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
container.addEventListener("keydown", function (event) {
|
|
||||||
if (event.key != "Enter") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var target = event.target;
|
|
||||||
if (target == null || target.tagName != "INPUT") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
event.preventDefault();
|
|
||||||
saveWizard();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
-329
@@ -1,329 +0,0 @@
|
|||||||
function showConfig(obj) {
|
|
||||||
config = obj;
|
|
||||||
//setMenuItem();
|
|
||||||
createMenu();
|
|
||||||
//document.getElementById("page").className = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
showMyStreams
|
|
||||||
|
|
||||||
function showMyStreams(allStreamsObj) {
|
|
||||||
|
|
||||||
var streamTypeKeys = getObjKeys(allStreamsObj)
|
|
||||||
|
|
||||||
for (var s = 0; s < streamTypeKeys.length; s++) {
|
|
||||||
var streamType = streamTypeKeys[s];
|
|
||||||
var obj = new Object();
|
|
||||||
obj = allStreamsObj[streamType];
|
|
||||||
switch(streamType) {
|
|
||||||
case "activeStreams": activeStreams = obj; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
document.getElementById(streamType).innerHTML = "";
|
|
||||||
|
|
||||||
var streamsObj = new Object();
|
|
||||||
var streamsNames = new Array();
|
|
||||||
|
|
||||||
var keys = getObjKeys(obj)
|
|
||||||
|
|
||||||
// Create Object (streamsObj) for the streams and sort by name (streamsNames)
|
|
||||||
for (var i = 0; i < keys.length; i++) {
|
|
||||||
var name = obj[keys[i]]["name"];
|
|
||||||
var tmp = new Object();
|
|
||||||
var streamKey = getObjKeys(obj[keys[i]]);
|
|
||||||
|
|
||||||
for (var j = 0; j < streamKey.length; j++) {
|
|
||||||
tmp[streamKey[j]] = obj[keys[i]][streamKey[j]];
|
|
||||||
}
|
|
||||||
|
|
||||||
streamsObj[name] = tmp;
|
|
||||||
streamsNames.push(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
streamsNames.sort();
|
|
||||||
|
|
||||||
// Create Table for activeStreams
|
|
||||||
var table = document.getElementById(streamType);
|
|
||||||
|
|
||||||
for (var i = 0; i < streamsNames.length; i++) {
|
|
||||||
var newEntry = new Object();
|
|
||||||
newEntry["_element"] = "TR";
|
|
||||||
table.appendChild(createElement(newEntry));
|
|
||||||
var line = table.lastChild;
|
|
||||||
|
|
||||||
var tmp = streamsObj[streamsNames[i]]
|
|
||||||
var keys = getObjKeys(tmp)
|
|
||||||
|
|
||||||
var newKey = new Object()
|
|
||||||
newKey["_element"] = "TD";
|
|
||||||
//newKey["_text"] = streamsNames[i];
|
|
||||||
switch(streamType) {
|
|
||||||
case "activeStreams": newKey["_text"] = "Channel (+):"; break;
|
|
||||||
case "inactiveStreams": newKey["_text"] = "Channel (-):"; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
newKey["class"] = "tdKey";
|
|
||||||
console.log();
|
|
||||||
|
|
||||||
|
|
||||||
var newVal = new Object()
|
|
||||||
newVal["_element"] = "TD";
|
|
||||||
newVal["_text"] = streamsNames[i];
|
|
||||||
newVal["class"] = "tdVal";
|
|
||||||
//newVal["_text"] = value;
|
|
||||||
|
|
||||||
line.appendChild(createElement(newKey));
|
|
||||||
line.appendChild(createElement(newVal));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
function showActiveStreams(obj) {
|
|
||||||
document.getElementById("activeStreams").innerHTML = "";
|
|
||||||
activeStreams = obj;
|
|
||||||
var streamsObj = new Object();
|
|
||||||
var streamsNames = new Array();
|
|
||||||
|
|
||||||
var keys = getObjKeys(obj)
|
|
||||||
|
|
||||||
// Create Object (streamsObj) for the streams and sort by name (streamsNames)
|
|
||||||
for (var i = 0; i < keys.length; i++) {
|
|
||||||
var name = obj[keys[i]]["name"];
|
|
||||||
var tmp = new Object();
|
|
||||||
var streamKey = getObjKeys(obj[keys[i]]);
|
|
||||||
|
|
||||||
for (var j = 0; j < streamKey.length; j++) {
|
|
||||||
tmp[streamKey[j]] = obj[keys[i]][streamKey[j]];
|
|
||||||
}
|
|
||||||
|
|
||||||
streamsObj[name] = tmp;
|
|
||||||
streamsNames.push(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
streamsNames.sort();
|
|
||||||
|
|
||||||
// Create Table for activeStreams
|
|
||||||
var table = document.getElementById("activeStreams");
|
|
||||||
|
|
||||||
for (var i = 0; i < streamsNames.length; i++) {
|
|
||||||
var newEntry = new Object();
|
|
||||||
newEntry["_element"] = "TR";
|
|
||||||
table.appendChild(createElement(newEntry));
|
|
||||||
var line = table.lastChild;
|
|
||||||
|
|
||||||
var tmp = streamsObj[streamsNames[i]]
|
|
||||||
var keys = getObjKeys(tmp)
|
|
||||||
|
|
||||||
var newKey = new Object()
|
|
||||||
newKey["_element"] = "TD";
|
|
||||||
//newKey["_text"] = streamsNames[i];
|
|
||||||
newKey["_text"] = "Channel:";
|
|
||||||
newKey["class"] = "tdKey";
|
|
||||||
console.log();
|
|
||||||
|
|
||||||
|
|
||||||
var newVal = new Object()
|
|
||||||
newVal["_element"] = "TD";
|
|
||||||
newVal["_text"] = streamsNames[i];
|
|
||||||
newVal["class"] = "tdVal";
|
|
||||||
//newVal["_text"] = value;
|
|
||||||
|
|
||||||
line.appendChild(createElement(newKey));
|
|
||||||
line.appendChild(createElement(newVal));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseLogs(obj) {
|
|
||||||
log = obj
|
|
||||||
var keys = getObjKeys(obj)
|
|
||||||
|
|
||||||
var msgType;
|
|
||||||
for (var i = 0; i < keys.length; i++) {
|
|
||||||
switch(keys[i]) {
|
|
||||||
case "warnings": msgType = "warningMsg"; break;
|
|
||||||
case "errors": msgType = "errorMsg"; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(obj[keys[i]]) {
|
|
||||||
case 0: msgType = "tdVal"; break;
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(document.getElementById(keys[i])){
|
|
||||||
document.getElementById(keys[i]).className = msgType;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function cancelData(element) {
|
|
||||||
createMenu();
|
|
||||||
}
|
|
||||||
|
|
||||||
function saveData(element) {
|
|
||||||
var data = new Object();
|
|
||||||
var div = element.parentNode.parentNode;
|
|
||||||
var inputs = div.getElementsByTagName("INPUT");
|
|
||||||
|
|
||||||
var configKey = div.getAttribute("data-configkey");
|
|
||||||
var menuType = div.getAttribute("data-menutype");
|
|
||||||
var value;
|
|
||||||
var valueArr = new Array();
|
|
||||||
|
|
||||||
for (var i = 0; i < inputs.length; i++) {
|
|
||||||
if (inputs[i].type == "text" && inputs[i].value != undefined && inputs[i].value != "" ) {
|
|
||||||
console.log(inputs[i].value, menuType)
|
|
||||||
switch(menuType) {
|
|
||||||
case "inputArray": valueArr.push(inputs[i].value); break;
|
|
||||||
case "singleInput": value = inputs[i].value; break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(menuType) {
|
|
||||||
case "inputArray": data[configKey] = valueArr; break;
|
|
||||||
case "singleInput":
|
|
||||||
if (isNaN(value) == false) {
|
|
||||||
value = parseInt(value);
|
|
||||||
data[configKey] = value;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value == undefined) {
|
|
||||||
data["delete"] = configKey;
|
|
||||||
} else {
|
|
||||||
data[configKey] = value
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
data["cmd"] = "saveConfig";
|
|
||||||
console.log(data);
|
|
||||||
xTeVe(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
function xTeVe(data) {
|
|
||||||
if (webSockets == false) {
|
|
||||||
alert("Your browser does not support WebSockets");
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
if (data["cmd"] != "getLog") {
|
|
||||||
showLoadingScreen(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
delete undo["epgMapping"];
|
|
||||||
|
|
||||||
var protocolWS
|
|
||||||
switch(window.location.protocol) {
|
|
||||||
case "http:": protocolWS = "ws://"; break;
|
|
||||||
case "https:": protocolWS = "wss://"; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
var ws = new WebSocket(protocolWS + window.location.hostname + ":" + window.location.port + "/data/" + "?Token=" + getCookie("Token"));
|
|
||||||
ws.onopen = function() {
|
|
||||||
console.log(data)
|
|
||||||
ws.send(JSON.stringify(data));
|
|
||||||
}
|
|
||||||
|
|
||||||
ws.onmessage = function (e) {
|
|
||||||
var response = JSON.parse(e.data);
|
|
||||||
console.log(response);
|
|
||||||
|
|
||||||
if (response.hasOwnProperty("clientInfo")) {
|
|
||||||
createClintInfo(response["clientInfo"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response.hasOwnProperty("log")) {
|
|
||||||
createClintInfo(response["log"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response.hasOwnProperty("status")) {
|
|
||||||
if (response["status"] == false) {
|
|
||||||
alert(response["err"])
|
|
||||||
if(response.hasOwnProperty("reload")) {
|
|
||||||
location.reload();
|
|
||||||
}
|
|
||||||
//checkErr(response)
|
|
||||||
console.log(response);
|
|
||||||
updateXteveStatus(response);
|
|
||||||
setTimeout(function(){ showLoadingScreen(false); }, 300);
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
updateXteveStatus(response)
|
|
||||||
|
|
||||||
//console.log(data["cmd"]);
|
|
||||||
switch(data["cmd"]) {
|
|
||||||
case "saveUserData": createMenu(); break;
|
|
||||||
case "saveNewUser": createMenu(); break;
|
|
||||||
case "saveFilesXMLTV": //createMenu(); break;
|
|
||||||
case "saveFilesM3U": //createMenu(); return; break;
|
|
||||||
case "saveConfig":
|
|
||||||
data = new Object();
|
|
||||||
data["cmd"] = "checkToken";
|
|
||||||
xTeVe(data);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "emptyLog": writeLogInDiv(); break;
|
|
||||||
case "getLog": return; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config["files"] == undefined || config["files"].length == 0) {
|
|
||||||
createMenu();
|
|
||||||
document.getElementById(10).click()
|
|
||||||
}
|
|
||||||
|
|
||||||
setTimeout(function(){ showLoadingScreen(false); }, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateXteveStatus(response) {
|
|
||||||
var keys = getObjKeys(response);
|
|
||||||
//console.log(keys);
|
|
||||||
|
|
||||||
for (var i = 0; i < keys.length; i++) {
|
|
||||||
switch(keys[i]) {
|
|
||||||
case "alert": alert(response[keys[i]]); break;
|
|
||||||
case "config": showConfig(response[keys[i]]); break;
|
|
||||||
case "log": parseLogs(response[keys[i]]); break;
|
|
||||||
case "myStreams": showMyStreams(response[keys[i]]); break;
|
|
||||||
case "xEPG": xEPG = response[keys[i]]; break;
|
|
||||||
case "users": users = response[keys[i]]; break;
|
|
||||||
case "token": document.cookie = "Token=" + response[keys[i]]; break;
|
|
||||||
case "reload": location.reload(); break;
|
|
||||||
case "openLink": window.location = response["openLink"]; break;
|
|
||||||
//case "version": version = response[keys[i]]; break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getValueFromProviderFile(xXmltvFile, fileType, key) {
|
|
||||||
|
|
||||||
var fileID = xXmltvFile.substring(0, xXmltvFile.lastIndexOf('.'))
|
|
||||||
|
|
||||||
if (config["files"][fileType].hasOwnProperty(fileID) == true) {
|
|
||||||
var data = config["files"][fileType][fileID];
|
|
||||||
return data[key]
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,379 +0,0 @@
|
|||||||
function openFiles(elm, fileType) {
|
|
||||||
//document.getElementById("settings").innerHTML = "Test";
|
|
||||||
|
|
||||||
columnToSort = 0;
|
|
||||||
var newDiv = document.getElementById("settings");
|
|
||||||
|
|
||||||
var newEntry = new Object();
|
|
||||||
newEntry["_element"] = "HR";
|
|
||||||
newDiv.appendChild(createElement(newEntry));
|
|
||||||
|
|
||||||
var newEntry = new Object();
|
|
||||||
newEntry["_element"] = "INPUT";
|
|
||||||
newEntry["type"] = "button";
|
|
||||||
newEntry["class"] = "button";
|
|
||||||
newEntry["value"] = "New";
|
|
||||||
newEntry["onclick"] = 'fileDetail("-", "' + fileType + '")';
|
|
||||||
newDiv.appendChild(createElement(newEntry));
|
|
||||||
|
|
||||||
var newEntry = new Object();
|
|
||||||
newEntry["_element"] = "INPUT";
|
|
||||||
newEntry["type"] = "button";
|
|
||||||
newEntry["class"] = "button";
|
|
||||||
newEntry["value"] = "Update";
|
|
||||||
newEntry["onclick"] = "fileDetail(0)";
|
|
||||||
//newDiv.appendChild(createElement(newEntry));
|
|
||||||
|
|
||||||
var div = document.getElementById("settings");
|
|
||||||
|
|
||||||
// Build table
|
|
||||||
var newTable = new Object();
|
|
||||||
newTable["_element"] = "TABLE";
|
|
||||||
newTable["id"] = "id_mapping";
|
|
||||||
newTable["class"] = "table-mapping";
|
|
||||||
div.appendChild(createElement(newTable));
|
|
||||||
|
|
||||||
setTimeout(function(){
|
|
||||||
createFilesTable(fileType);
|
|
||||||
}, 10);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function createFilesTable(fileType) {
|
|
||||||
var table = document.getElementById("id_mapping");
|
|
||||||
var availableFileTypes = new Array();
|
|
||||||
|
|
||||||
table.innerHTML = "";
|
|
||||||
var newTR = new Object();
|
|
||||||
newTR["_element"] = "TR";
|
|
||||||
newTR["class"] = "table-mapping-header";
|
|
||||||
table.appendChild(createElement(newTR));
|
|
||||||
|
|
||||||
var tr = table.lastChild;
|
|
||||||
|
|
||||||
switch(fileType) {
|
|
||||||
case "xmltv":
|
|
||||||
availableFileTypes = new Array("xmltv");
|
|
||||||
var trHeadlines = new Array("Guide", "Last Update", "Availability %", "Channels", "Programs")
|
|
||||||
var compatibilityKeys = new Array("xmltv.channels", "xmltv.programs")
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "m3u":
|
|
||||||
availableFileTypes = new Array("m3u", "hdhr");
|
|
||||||
var trHeadlines = new Array("Playlist", "Last Update", "Availability %", "Type", "Streams", "group-title %", "tvg-id %", "Unique ID %");
|
|
||||||
var compatibilityKeys = new Array("streams", "group.title", "tvg.id", "stream.id");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var i = 0; i < trHeadlines.length; i++) {
|
|
||||||
var newTD = new Object();
|
|
||||||
newTD["_element"] = "TD";
|
|
||||||
newTD["_text"] = trHeadlines[i];
|
|
||||||
tr.appendChild(createElement(newTD));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var i = 0; i < availableFileTypes.length; i++) {
|
|
||||||
|
|
||||||
var fileType = availableFileTypes[i]
|
|
||||||
|
|
||||||
var data = config["files"][fileType];
|
|
||||||
|
|
||||||
var allFiles = getObjKeys(data)
|
|
||||||
|
|
||||||
for (var f = 0; f < allFiles.length; f++) {
|
|
||||||
var elm = data[allFiles[f]];
|
|
||||||
var table = document.getElementById("id_mapping");
|
|
||||||
var fileID = elm["id.provider"];
|
|
||||||
var name = elm["name"];
|
|
||||||
var lastUpdate = elm["last.update"];
|
|
||||||
var availability = elm["provider.availability"];
|
|
||||||
var type = elm["type"].toUpperCase();
|
|
||||||
var compatibility = elm["compatibility"];
|
|
||||||
|
|
||||||
// Create TR
|
|
||||||
var newTR = new Object();
|
|
||||||
newTR["_element"] = "TR";
|
|
||||||
newTR["class"] = "";
|
|
||||||
newTR["id"] = fileID;
|
|
||||||
newTR["onclick"] = 'javascript: fileDetail("' + fileID + '","' + fileType + '");';
|
|
||||||
table.appendChild(createElement(newTR));
|
|
||||||
|
|
||||||
var tr = table.lastChild;
|
|
||||||
|
|
||||||
// Create file name TD
|
|
||||||
var newTD = new Object();
|
|
||||||
newTD["_element"] = "P";
|
|
||||||
newTD["_text"] = name;
|
|
||||||
createNewTD(newTD, tr);
|
|
||||||
|
|
||||||
// Create last update TD
|
|
||||||
var newTD = new Object();
|
|
||||||
newTD["_element"] = "P";
|
|
||||||
newTD["_text"] = lastUpdate;
|
|
||||||
createNewTD(newTD, tr);
|
|
||||||
|
|
||||||
// Create availability TD
|
|
||||||
var newTD = new Object();
|
|
||||||
newTD["_element"] = "P";
|
|
||||||
newTD["_text"] = availability;
|
|
||||||
createNewTD(newTD, tr);
|
|
||||||
|
|
||||||
if (fileType == "m3u" || fileType == "hdhr") {
|
|
||||||
|
|
||||||
// Create Type TD
|
|
||||||
var newTD = new Object();
|
|
||||||
newTD["_element"] = "P";
|
|
||||||
newTD["_text"] = type;
|
|
||||||
createNewTD(newTD, tr);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create all compatibility TDs
|
|
||||||
|
|
||||||
for (var j = 0; j < compatibilityKeys.length; j++) {
|
|
||||||
var newTD = new Object();
|
|
||||||
newTD["_element"] = "P";
|
|
||||||
newTD["_text"] = compatibility[compatibilityKeys[j]];
|
|
||||||
createNewTD(newTD, tr);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
sortTable(0)
|
|
||||||
|
|
||||||
// usage Info
|
|
||||||
var div = document.getElementById("settings");
|
|
||||||
switch(menu[activeMenu.id].hasOwnProperty("_usage")) {
|
|
||||||
case true:
|
|
||||||
var usageItem = new Object();
|
|
||||||
usageItem["_element"] = "PRE"
|
|
||||||
usageItem["_text"] = menu[activeMenu.id]["_usage"];
|
|
||||||
|
|
||||||
var newHR = new Object();
|
|
||||||
newHR["_element"] = "HR"
|
|
||||||
div.appendChild(createElement(newHR));
|
|
||||||
div.appendChild(createElement(usageItem));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
calculateWrapperHeight();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function fileDetail(fileID, fileType) {
|
|
||||||
|
|
||||||
optionsText = new Array("M3U", "HDHomeRun - [Experimental]")
|
|
||||||
optionsValue = new Array("m3u", "hdhr")
|
|
||||||
|
|
||||||
switch (fileType) {
|
|
||||||
|
|
||||||
case "m3u":
|
|
||||||
document.getElementById("name").setAttribute("placeholder", "Playlist name");
|
|
||||||
document.getElementById("description").setAttribute("placeholder", "Description of this playlist");
|
|
||||||
document.getElementById("file-detail-headline").innerHTML = "M3U Playlist";
|
|
||||||
document.getElementById("file-path").innerHTML = "M3U File:";
|
|
||||||
document.getElementById("file.source").setAttribute("placeholder", "Local or remote");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "hdhr":
|
|
||||||
document.getElementById("name").setAttribute("placeholder", "HDHomeRun name");
|
|
||||||
document.getElementById("description").setAttribute("placeholder", "Description of this HDHomeRun tuner");
|
|
||||||
document.getElementById("file-detail-headline").innerHTML = "HDHomeRun";
|
|
||||||
document.getElementById("file-path").innerHTML = "HDHomeRun IP:";
|
|
||||||
document.getElementById("file.source").setAttribute("placeholder", "IP address and port of the tuner (192.168.1.10:5004)");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "xmltv":
|
|
||||||
document.getElementById("name").setAttribute("placeholder", "XMLTV name");
|
|
||||||
document.getElementById("description").setAttribute("placeholder", "Description of this XMLTV file");
|
|
||||||
document.getElementById("file-detail-headline").innerHTML = "XMLTV File";
|
|
||||||
document.getElementById("file-path").innerHTML = "XMLTV File:";
|
|
||||||
document.getElementById("file.source").setAttribute("placeholder", "Local or remote");
|
|
||||||
|
|
||||||
optionsText = new Array("XMLTV")
|
|
||||||
optionsValue = new Array("xmltv")
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
modifyOption("type", optionsText, optionsValue)
|
|
||||||
|
|
||||||
showPopUpElement('file-detail');
|
|
||||||
|
|
||||||
document.getElementById("saveFileDetail").setAttribute("onclick", 'javascript: saveFileDetail("' + fileID + '","' + fileType + '", false)');
|
|
||||||
document.getElementById("updateFileDetail").setAttribute("onclick", 'javascript: updateFile("' + fileID + '","' + fileType + '", false)');
|
|
||||||
document.getElementById("deleteFileDetail").setAttribute("onclick", 'javascript: saveFileDetail("' + fileID + '","' + fileType + '", true)');
|
|
||||||
|
|
||||||
var data = new Object();
|
|
||||||
|
|
||||||
switch(fileID) {
|
|
||||||
|
|
||||||
case "-": // New file
|
|
||||||
data["name"] = "";
|
|
||||||
data["description"] = "";
|
|
||||||
data["file.source"] = "";
|
|
||||||
data["type"] = fileType;
|
|
||||||
|
|
||||||
document.getElementById("deleteFileDetail").className = "delete";
|
|
||||||
document.getElementById("type").setAttribute("onchange", "changeFileType(this);")
|
|
||||||
document.getElementById("type").setAttribute("data-id", fileID)
|
|
||||||
|
|
||||||
showElement("deleteFileDetail", false);
|
|
||||||
showElement("updateFileDetail", false);
|
|
||||||
|
|
||||||
if (fileType == "xmltv") {
|
|
||||||
showElement("type", false);
|
|
||||||
showElement("file-type", false);
|
|
||||||
} else {
|
|
||||||
showElement("type", true);
|
|
||||||
showElement("file-type", true);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
data = config["files"][fileType][fileID];
|
|
||||||
document.getElementById("deleteFileDetail").className = "delete";
|
|
||||||
|
|
||||||
showElement("updateFileDetail", true);
|
|
||||||
showElement("type", false);
|
|
||||||
showElement("file-type", false);
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
var keys = getObjKeys(data);
|
|
||||||
|
|
||||||
for (var i = 0; i < keys.length; i++) {
|
|
||||||
|
|
||||||
if(document.getElementById(keys[i])){
|
|
||||||
document.getElementById(keys[i]).value = data[keys[i]];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function changeFileType(elm) {
|
|
||||||
|
|
||||||
var fileID = elm.getAttribute("data-id");
|
|
||||||
var fileType = elm.options[elm.selectedIndex].value;
|
|
||||||
|
|
||||||
fileDetail(fileID, fileType)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function saveFileDetail(fileID, fileType, deleteFile) {
|
|
||||||
|
|
||||||
if (fileID == undefined) {
|
|
||||||
alert("ID is missing!!!");
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var inputs = document.getElementById("file-detail").getElementsByTagName("INPUT");
|
|
||||||
var selects = document.getElementById("file-detail").getElementsByTagName("SELECT");
|
|
||||||
var newFileData = new Object();
|
|
||||||
var data = new Object();
|
|
||||||
|
|
||||||
for (var i = 0; i < inputs.length; i++) {
|
|
||||||
switch(inputs[i].type) {
|
|
||||||
case "text": newFileData[inputs[i].name] = inputs[i].value; break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var i = 0; i < selects.length; i++) {
|
|
||||||
newFileData[selects[i].id] = selects[i].options[selects[i].selectedIndex].value;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (deleteFile == true) {
|
|
||||||
switch(fileType) {
|
|
||||||
case "m3u": var alertText = "Delete this playlist?"; break;
|
|
||||||
case "hdhr": var alertText = "Delete this HDHomeRun tuner?"; break;
|
|
||||||
case "xmltv": var alertText = "Delete this XMLTV file?"; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (confirm(alertText)) {
|
|
||||||
newFileData["delete"] = true
|
|
||||||
data = buildFilesObj(fileType, fileID, newFileData);
|
|
||||||
console.log(data);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
showElement("popup", false);
|
|
||||||
return
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
switch(config["files"][fileType].hasOwnProperty(fileID)) {
|
|
||||||
|
|
||||||
case true:
|
|
||||||
data = config["files"][fileType][fileID];
|
|
||||||
if (data["file.source"] != newFileData["file.source"]) {
|
|
||||||
data["update"] = true
|
|
||||||
} else {
|
|
||||||
data["updatePlaylistName"] = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case false:
|
|
||||||
newFileData["new"] = true;
|
|
||||||
data = buildFilesObj(fileType, fileID, newFileData);
|
|
||||||
break
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(fileType) {
|
|
||||||
|
|
||||||
case "m3u": data["cmd"] = "saveFilesM3U"; break;
|
|
||||||
case "hdhr": data["cmd"] = "saveFilesHDHR"; break;
|
|
||||||
case "xmltv": data["cmd"] = "saveFilesXMLTV"; break;
|
|
||||||
|
|
||||||
}
|
|
||||||
//console.log(data);
|
|
||||||
xTeVe(data);
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateFile(fileID, fileType, allFiles) {
|
|
||||||
|
|
||||||
switch(config["files"][fileType].hasOwnProperty(fileID)) {
|
|
||||||
|
|
||||||
case true:
|
|
||||||
|
|
||||||
var data = new Object();
|
|
||||||
var data = buildFilesObj(fileType, fileID, config["files"][fileType][fileID])
|
|
||||||
data["new"] = true
|
|
||||||
|
|
||||||
switch(fileType) {
|
|
||||||
|
|
||||||
case "m3u": data["cmd"] = "updateFileM3U"; break;
|
|
||||||
case "hdhr": data["cmd"] = "updateFileHDHR"; break;
|
|
||||||
case "xmltv": data["cmd"] = "updateFileXMLTV"; break;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
xTeVe(data);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildFilesObj(fileType, fileID, obj) {
|
|
||||||
|
|
||||||
var data = new Object();
|
|
||||||
data["files"] = new Object();
|
|
||||||
data["files"][fileType] = new Object();
|
|
||||||
data["files"][fileType][fileID] = obj
|
|
||||||
return data
|
|
||||||
|
|
||||||
}
|
|
||||||
-114
@@ -1,114 +0,0 @@
|
|||||||
var logInterval
|
|
||||||
|
|
||||||
|
|
||||||
function updateLog() {
|
|
||||||
var data = new Object();
|
|
||||||
data["cmd"] = "getLog";
|
|
||||||
xTeVe(data);
|
|
||||||
writeLogInDiv();
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
function writeLogInDiv() {
|
|
||||||
var logs = log["log"];
|
|
||||||
var div = document.getElementById("settings").lastChild.lastChild;
|
|
||||||
div.innerHTML = "";
|
|
||||||
|
|
||||||
var max = 50;
|
|
||||||
|
|
||||||
|
|
||||||
for (var i = 0; i < logs.length; i++) {
|
|
||||||
var newEntry = new Object();
|
|
||||||
newEntry["_element"] = "P";
|
|
||||||
|
|
||||||
if (logs[i].includes("ERROR")) {
|
|
||||||
// case "warnings": msgType = "warningMsg"; break;
|
|
||||||
newEntry["class"] = "errorMsg";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (logs[i].includes("WARNING")) {
|
|
||||||
// case "warnings": msgType = "warningMsg"; break;
|
|
||||||
newEntry["class"] = "warningMsg";
|
|
||||||
}
|
|
||||||
|
|
||||||
newEntry["_text"] = logs[i];
|
|
||||||
|
|
||||||
div.appendChild(createElement(newEntry));
|
|
||||||
}
|
|
||||||
|
|
||||||
calculateWrapperHeight();
|
|
||||||
var scrollDiv = document.getElementById("box-wrapper");
|
|
||||||
scrollDiv.scrollTop = scrollDiv.scrollHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
function showLog(obj) {
|
|
||||||
//logInterval = setInterval(updateLog, 5000);
|
|
||||||
|
|
||||||
var logs = log["log"];
|
|
||||||
|
|
||||||
var div = document.getElementById("settings");
|
|
||||||
|
|
||||||
var newEntry = new Object();
|
|
||||||
newEntry["_element"] = "HR";
|
|
||||||
div.appendChild(createElement(newEntry));
|
|
||||||
//div = div.lastChild;
|
|
||||||
|
|
||||||
var newEntry = new Object();
|
|
||||||
newEntry["_element"] = "INPUT";
|
|
||||||
newEntry["type"] = "button";
|
|
||||||
newEntry["class"] = "button";
|
|
||||||
newEntry["value"] = "Empty Log";
|
|
||||||
newEntry["onclick"] = "emptyLog()";
|
|
||||||
div.appendChild(createElement(newEntry));
|
|
||||||
|
|
||||||
var newEntry = new Object();
|
|
||||||
newEntry["_element"] = "code";
|
|
||||||
newEntry["_text"] = "Update Log: ";
|
|
||||||
div.appendChild(createElement(newEntry));
|
|
||||||
|
|
||||||
var newEntry = new Object();
|
|
||||||
newEntry["_element"] = "INPUT";
|
|
||||||
newEntry["type"] = "checkbox";
|
|
||||||
//newEntry["checked"] = "checkbox";
|
|
||||||
newEntry["onclick"] = "logUpdates(this)";
|
|
||||||
div.appendChild(createElement(newEntry));
|
|
||||||
|
|
||||||
var newEntry = new Object();
|
|
||||||
newEntry["_element"] = "HR";
|
|
||||||
div.appendChild(createElement(newEntry));
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var newWrapper = new Object();
|
|
||||||
newWrapper["_element"] = "DIV";
|
|
||||||
newWrapper["id"] = "box-wrapper";
|
|
||||||
div.appendChild(createElement(newWrapper));
|
|
||||||
div = div.lastChild;
|
|
||||||
|
|
||||||
var newPre = new Object();
|
|
||||||
newPre["_element"] = "PRE";
|
|
||||||
newPre["id"] = "logScreen";
|
|
||||||
div.appendChild(createElement(newPre));
|
|
||||||
|
|
||||||
div = div.lastChild;
|
|
||||||
|
|
||||||
writeLogInDiv()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
function emptyLog() {
|
|
||||||
var data = new Object();
|
|
||||||
data["cmd"] = "emptyLog";
|
|
||||||
xTeVe(data);
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
function logUpdates(elm) {
|
|
||||||
switch(elm.checked) {
|
|
||||||
case false: clearInterval(logInterval); break;
|
|
||||||
case true: logInterval = setInterval(updateLog, 5000); break;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
var Log = /** @class */ (function () {
|
|
||||||
function Log() {
|
|
||||||
}
|
|
||||||
Log.prototype.createLog = function (entry) {
|
|
||||||
var element = document.createElement("PRE");
|
|
||||||
if (entry.indexOf("WARNING") != -1) {
|
|
||||||
element.className = "warningMsg";
|
|
||||||
}
|
|
||||||
if (entry.indexOf("ERROR") != -1) {
|
|
||||||
element.className = "errorMsg";
|
|
||||||
}
|
|
||||||
if (entry.indexOf("DEBUG") != -1) {
|
|
||||||
element.className = "debugMsg";
|
|
||||||
}
|
|
||||||
element.innerHTML = entry;
|
|
||||||
return element;
|
|
||||||
};
|
|
||||||
return Log;
|
|
||||||
}());
|
|
||||||
function showLogs(bottom) {
|
|
||||||
var log = new Log();
|
|
||||||
var logs = SERVER["log"]["log"];
|
|
||||||
var div = document.getElementById("content_log");
|
|
||||||
var wrapper = document.getElementById("box-wrapper");
|
|
||||||
var shouldStickToBottom = bottom;
|
|
||||||
div.innerHTML = "";
|
|
||||||
if (wrapper != null && shouldStickToBottom == false) {
|
|
||||||
var distanceToBottom = wrapper.scrollHeight - wrapper.scrollTop - wrapper.clientHeight;
|
|
||||||
if (distanceToBottom < 80) {
|
|
||||||
shouldStickToBottom = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var keys = getObjKeys(logs);
|
|
||||||
keys.forEach(function (logID) {
|
|
||||||
var entry = log.createLog(logs[logID]);
|
|
||||||
div.append(entry);
|
|
||||||
});
|
|
||||||
setTimeout(function () {
|
|
||||||
if (shouldStickToBottom == true && wrapper != null) {
|
|
||||||
wrapper.scrollTop = wrapper.scrollHeight;
|
|
||||||
}
|
|
||||||
}, 10);
|
|
||||||
}
|
|
||||||
function resetLogs() {
|
|
||||||
var cmd = "resetLogs";
|
|
||||||
var data = new Object();
|
|
||||||
var server = new Server(cmd);
|
|
||||||
server.request(data);
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
-754
@@ -1,754 +0,0 @@
|
|||||||
|
|
||||||
function setMenuItem() {
|
|
||||||
|
|
||||||
menu = new Object();
|
|
||||||
subMenu = new Object();
|
|
||||||
|
|
||||||
var menu_m3u = new Object();
|
|
||||||
menu_m3u["_menuType"] = "inputArray";
|
|
||||||
menu_m3u["_element"] = "LI";
|
|
||||||
menu_m3u["_configKey"] = "files.m3u";
|
|
||||||
menu_m3u["_text"] = "Playlist";
|
|
||||||
menu_m3u["_icon"] = "img/m3u.png";
|
|
||||||
menu_m3u["_headline"] = "Playlists: Local or remote";
|
|
||||||
menu_m3u["_usage"] = "<b>Info</b><br>Availability: File availability in percent<br>Streams: Number of streams in the file.<br>group-title: Streams that are assigned to a group. Simplifies filtering streams<br>tvg-id: This ID is used for automatic mapping, must match with the channel ID in the XMLTV file.<br>Unique ID: Streams with a unique ID to identify them. Allows channel name changes in the M3U without losing the XMLTV mapping (PPV / live events).<br><br><b>Usage M3U:</b><br>Remote playlist: http://your.iptv.provider.com/file.m3u<br>Local playlist: /path/to/file.m3u<br><br><b>Usage HDHomeRun:</b><br>IP: 192.168.1.10:5004<br>"
|
|
||||||
menu_m3u["name"] = "file";
|
|
||||||
menu_m3u["id"] = "file";
|
|
||||||
menu_m3u["value"] = menu_m3u["name"];
|
|
||||||
menu_m3u["placeholder"] = "Playlist: local or remote";
|
|
||||||
menu_m3u["onclick"] = "javascript: toggleMenu(this);";
|
|
||||||
menu_m3u["class"] = "menu-notActive";
|
|
||||||
|
|
||||||
|
|
||||||
var menu_filter = new Object();
|
|
||||||
menu_filter["_menuType"] = "inputArray";
|
|
||||||
menu_filter["_element"] = "LI";
|
|
||||||
menu_filter["_configKey"] = "filter";
|
|
||||||
menu_filter["_text"] = "Filter";
|
|
||||||
menu_filter["_icon"] = "img/filter.png";
|
|
||||||
menu_filter["_headline"] = "Filter by M3U parameters, e.g. group-title";
|
|
||||||
menu_filter["_usage"] = "<b>Usage:</b><br>Sport - All sports channels<br>Sport {HD} - All HD sports channels<br>Sport {HD} !{ES,DE} - All HD sports channels, but no Spanish and German<br><br>To filter the streams of a HDHomeRun, the playlist name can be entered:<br>My tuner {HD}"
|
|
||||||
//menu_filter["_usage"] = "<b>Usage:</b><br>All sports channels: Sport<br>All HD sports channels: Sport {HD}<br>All HD sports channels, but no Spanish and German: Sport {HD} !{ES,DE}"
|
|
||||||
menu_filter["name"] = "filter";
|
|
||||||
menu_filter["id"] = "M3U";
|
|
||||||
menu_filter["value"] = menu_filter["name"];
|
|
||||||
menu_filter["placeholder"] = "Filter streams: Sport";
|
|
||||||
menu_filter["onclick"] = "javascript: toggleMenu(this);";
|
|
||||||
menu_filter["class"] = "menu-notActive";
|
|
||||||
|
|
||||||
var menu_id = new Object();
|
|
||||||
menu_id["_menuType"] = "inputArray";
|
|
||||||
menu_id["_element"] = "LI";
|
|
||||||
menu_id["_configKey"] = "id";
|
|
||||||
menu_id["_text"] = "PMS ID";
|
|
||||||
menu_id["_icon"] = "img/number.png";
|
|
||||||
menu_id["_headline"] = "Setup PMS guide number";
|
|
||||||
menu_id["_usage"] = 'Some playlists have unique channel IDs.<br>Enter the keyword of the ID. The channel assignment in PMS will change as a result.<br><br>e.g. channelID<br>#EXTINF:0 type="stream" <b>channelId</b>="81", My Streaming Channel HD<br><br>Only enter here if you know what you are doing!'
|
|
||||||
menu_id["name"] = "id";
|
|
||||||
menu_id["id"] = "id";
|
|
||||||
menu_id["value"] = menu_id["name"];
|
|
||||||
menu_id["placeholder"] = "Unique ID from the M3U file";
|
|
||||||
menu_id["onclick"] = "javascript: toggleMenu(this);";
|
|
||||||
menu_id["class"] = "menu-notActive";
|
|
||||||
|
|
||||||
|
|
||||||
var menu_xmltv = new Object();
|
|
||||||
menu_xmltv["_menuType"] = "inputArray";
|
|
||||||
menu_xmltv["_element"] = "LI";
|
|
||||||
menu_xmltv["_configKey"] = "files.xmltv";
|
|
||||||
menu_xmltv["_text"] = "XMLTV";
|
|
||||||
menu_xmltv["_icon"] = "img/xmltv.png";
|
|
||||||
menu_xmltv["_headline"] = "XMLTV files: Local or remote";
|
|
||||||
menu_xmltv["_usage"] = "<b>Info:</b><br>Availability: File availability in percent<br>Channels: Number of channels in the file<br>Programs: Number of EPG data<br><br><b>Usage:</b><br>Remote XMLTV file: http://your.epg.provider.com/guide.xml<br>Local XMLTV file: /path/to/guide.xml"
|
|
||||||
menu_xmltv["name"] = "xmltv";
|
|
||||||
menu_xmltv["id"] = "xmltv";
|
|
||||||
menu_xmltv["value"] = menu_xmltv["name"];
|
|
||||||
menu_xmltv["placeholder"] = "XMLTV File: local or remote";
|
|
||||||
menu_xmltv["onclick"] = "javascript: toggleMenu(this);";
|
|
||||||
menu_xmltv["class"] = "menu-notActive";
|
|
||||||
|
|
||||||
menu_mapping = new Object();
|
|
||||||
menu_mapping["_element"] = "LI";
|
|
||||||
menu_mapping["_text"] = "Mapping";
|
|
||||||
menu_mapping["_icon"] = "img/mapping.png";
|
|
||||||
menu_mapping["_configKey"] = "mapping";
|
|
||||||
menu_mapping["_headline"] = "XMLTV assignment and sorting of channels";
|
|
||||||
menu_mapping["id"] = "mapping";
|
|
||||||
menu_mapping["onclick"] = "javascript: toggleMenu(this);";
|
|
||||||
menu_mapping["class"] = "menu-notActive phone";
|
|
||||||
|
|
||||||
menu_users = new Object();
|
|
||||||
menu_users["_element"] = "LI";
|
|
||||||
menu_users["_text"] = "Users";
|
|
||||||
menu_users["_icon"] = "img/users.png";
|
|
||||||
menu_users["_configKey"] = "users";
|
|
||||||
menu_users["_headline"] = "Administration of users and permissions";
|
|
||||||
menu_users["id"] = "users";
|
|
||||||
menu_users["onclick"] = "javascript: toggleMenu(this);";
|
|
||||||
menu_users["class"] = "menu-notActive";
|
|
||||||
menu_users["_usage"] = "<b>Authorization groups:</b><br>WEB: Users can log in to the web interface<br>PMS: Programs like Plex can access the channel list. Login via DVR IP: username:password@xteve.ip:port<br>M3U: Allows clients to download the M3U playlist.<br>XML: Allows clients to download the XMLTV file.<br>API: Allows clients to use the API interface.<br><br>!!! For PMS authentication, only the following special characters are valid: !$()=.,-:;<br><br>The individual authentication groups can be activated / deactivated in the settings menu."
|
|
||||||
|
|
||||||
menu_settings = new Object();
|
|
||||||
menu_settings["_element"] = "LI";
|
|
||||||
menu_settings["_text"] = "Settings";
|
|
||||||
menu_settings["_icon"] = "img/settings.png";
|
|
||||||
menu_settings["_configKey"] = "settings";
|
|
||||||
menu_settings["_headline"] = "Settings";
|
|
||||||
menu_settings["_subMenu"] = "701,702,703,704,705,706,707,708,799,710,711,712,713,714";
|
|
||||||
menu_settings["id"] = "settings";
|
|
||||||
menu_settings["onclick"] = "javascript: toggleMenu(this);";
|
|
||||||
menu_settings["class"] = "menu-notActive";
|
|
||||||
|
|
||||||
menu_log = new Object();
|
|
||||||
menu_log["_element"] = "LI";
|
|
||||||
menu_log["_text"] = "Log";
|
|
||||||
menu_log["_icon"] = "img/log.png";
|
|
||||||
menu_log["_headline"] = "Log";
|
|
||||||
menu_log["_configKey"] = "log";
|
|
||||||
menu_log["id"] = "log";
|
|
||||||
menu_log["onclick"] = "javascript: toggleMenu(this);";
|
|
||||||
menu_log["class"] = "menu-notActive";
|
|
||||||
|
|
||||||
menu_logout = new Object();
|
|
||||||
menu_logout["_element"] = "LI";
|
|
||||||
menu_logout["_text"] = "Logout";
|
|
||||||
menu_logout["_icon"] = "img/logout.png";
|
|
||||||
menu_logout["id"] = "logout";
|
|
||||||
menu_logout["onclick"] = "javascript: logout();";
|
|
||||||
menu_logout["class"] = "menu-notActive";
|
|
||||||
|
|
||||||
var menu_schedule = new Object();
|
|
||||||
menu_schedule["_menuType"] = "inputArray";
|
|
||||||
menu_schedule["_element"] = "LI";
|
|
||||||
menu_schedule["_configKey"] = "update";
|
|
||||||
menu_schedule["_text"] = "Schedule";
|
|
||||||
menu_schedule["_icon"] = "img/schedule.png";
|
|
||||||
menu_schedule["_headline"] = "Schedule for updating M3U, XMLTV files and creating a local backup";
|
|
||||||
menu_schedule["_usage"] = "<b>Usage:</b><br>0815 = 8:15 am<br>1930 = 7:30 pm"
|
|
||||||
menu_schedule["name"] = "update";
|
|
||||||
menu_schedule["id"] = "update";
|
|
||||||
menu_schedule["value"] = menu_id["name"];
|
|
||||||
menu_schedule["placeholder"]= "time of day (24-hour clock)";
|
|
||||||
menu_schedule["onclick"] = "javascript: toggleMenu(this);";
|
|
||||||
menu_schedule["class"] = "menu-notActive";
|
|
||||||
|
|
||||||
var menu_filesUpdate = new Object();
|
|
||||||
menu_filesUpdate["_element"] = "LI";
|
|
||||||
menu_filesUpdate["_menuType"] = "checkbox";
|
|
||||||
menu_filesUpdate["_configKey"] = "files.update";
|
|
||||||
menu_filesUpdate["_label"] = "Update the provider files at system startup";
|
|
||||||
menu_filesUpdate["_headline"] = "Update the provider files at system startup";
|
|
||||||
menu_filesUpdate["_usage"] = "Playlists and XMLTV files are updated by xTeVe at system startup."
|
|
||||||
menu_filesUpdate["name"] = "files.update";
|
|
||||||
menu_filesUpdate["id"] = "files.update";
|
|
||||||
menu_filesUpdate["value"] = menu_filesUpdate["name"];
|
|
||||||
menu_filesUpdate["onclick"] = "javascript: toggleMenu(this);";
|
|
||||||
menu_filesUpdate["class"] = "menu-notActive";
|
|
||||||
|
|
||||||
var menu_tuner = new Object();
|
|
||||||
menu_tuner["_element"] = "LI";
|
|
||||||
menu_tuner["_menuType"] = "select";
|
|
||||||
menu_tuner["_configKey"] = "tuner";
|
|
||||||
menu_tuner["_label"] = "Available tuners";
|
|
||||||
menu_tuner["_text"] = "Tuner";
|
|
||||||
menu_tuner["_icon"] = "img/tuner.png";
|
|
||||||
menu_tuner["_headline"] = "Number of tuners";
|
|
||||||
menu_tuner["_usage"] = "This setting is only used by Plex and Emby.<br>The number of concurrent streams allowed by the IPTV provider.<br>After a change, xTeVe must be delete in the PMS DVR settings and set up again."
|
|
||||||
menu_tuner["name"] = "tuner";
|
|
||||||
menu_tuner["id"] = "tuner";
|
|
||||||
menu_tuner["value"] = menu_tuner["name"];
|
|
||||||
menu_tuner["placeholder"] = "Number of tuners";
|
|
||||||
menu_tuner["onclick"] = "javascript: toggleMenu(this);";
|
|
||||||
menu_tuner["class"] = "menu-notActive";
|
|
||||||
|
|
||||||
var optionValues = new Array();
|
|
||||||
for (var i = 1; i <= 100; i++) {
|
|
||||||
optionValues.push(i)
|
|
||||||
}
|
|
||||||
menu_tuner["_optionValues"] = optionValues;
|
|
||||||
|
|
||||||
var menu_epg = new Object();
|
|
||||||
menu_epg["_element"] = "LI";
|
|
||||||
menu_epg["_menuType"] = "select";
|
|
||||||
menu_epg["_configKey"] = "epgSource";
|
|
||||||
menu_epg["_label"] = "Selection of the EPG source";
|
|
||||||
menu_epg["_text"] = "EPG source";
|
|
||||||
menu_epg["_headline"] = "Selection of the EPG source";
|
|
||||||
menu_epg["_usage"] = "PMS: Use EPG data from Plex or Emby.<br>XEPG: Use of external EPG data (XMLTV).<br> Several XMLTV sources possible.<br> Allows editing and order channels.<br> M3U / XMLTV export (HTTP link for IPTV apps)."
|
|
||||||
menu_epg["name"] = "epgSource";
|
|
||||||
menu_epg["id"] = "epgSource";
|
|
||||||
menu_epg["value"] = menu_epg["name"];
|
|
||||||
menu_epg["placeholder"] = "EPG source";
|
|
||||||
menu_epg["onclick"] = "javascript: toggleMenu(this);";
|
|
||||||
menu_epg["class"] = "menu-notActive";
|
|
||||||
menu_epg["_optionValues"] = new Array("PMS", "XEPG");
|
|
||||||
|
|
||||||
var menu_xepg = new Object();
|
|
||||||
menu_xepg["_element"] = "LI";
|
|
||||||
menu_xepg["_menuType"] = "checkbox";
|
|
||||||
menu_xepg["_configKey"] = "xteveAutoUpdate";
|
|
||||||
menu_xepg["_label"] = "Automatic update of xTeVe";
|
|
||||||
menu_xepg["_headline"] = "Automatic update of xTeVe";
|
|
||||||
menu_xepg["_usage"] = "If a new version of xTeVe is available, it will be automatically installed."
|
|
||||||
menu_xepg["name"] = "xteveAutoUpdate";
|
|
||||||
menu_xepg["id"] = "xteveAutoUpdate";
|
|
||||||
menu_xepg["value"] = menu_xepg["name"];
|
|
||||||
menu_xepg["onclick"] = "javascript: toggleMenu(this);";
|
|
||||||
menu_xepg["class"] = "menu-notActive";
|
|
||||||
|
|
||||||
var menu_autoBackupPath = new Object();
|
|
||||||
menu_autoBackupPath["_element"] = "LI";
|
|
||||||
menu_autoBackupPath["_menuType"] = "singleInput";
|
|
||||||
menu_autoBackupPath["_configKey"] = "backup.path";
|
|
||||||
menu_autoBackupPath["_label"] = "Location for automatic backups";
|
|
||||||
menu_autoBackupPath["_headline"] = "Location for automatic backups";
|
|
||||||
menu_autoBackupPath["_usage"] = "Before any update of the provider data by the schedule, xTeVe creates a backup. The path for the automatic backups can be changed. xTeVe requires write permission for this folder."
|
|
||||||
menu_autoBackupPath["name"] = "backup.path";
|
|
||||||
menu_autoBackupPath["id"] = "backup.path";
|
|
||||||
menu_autoBackupPath["value"] = menu_autoBackupPath["name"];
|
|
||||||
menu_autoBackupPath["onclick"] = "javascript: toggleMenu(this);";
|
|
||||||
menu_autoBackupPath["class"] = "menu-notActive";
|
|
||||||
|
|
||||||
var menu_autoBackupKeep = new Object();
|
|
||||||
menu_autoBackupKeep["_element"] = "LI";
|
|
||||||
menu_autoBackupKeep["_menuType"] = "select";
|
|
||||||
menu_autoBackupKeep["_configKey"] = "backup.keep";
|
|
||||||
menu_autoBackupKeep["_text"] = "Keep";
|
|
||||||
menu_autoBackupKeep["_label"] = "Number of backups to keep";
|
|
||||||
menu_autoBackupKeep["_headline"] = "Number of backups to keep";
|
|
||||||
menu_autoBackupKeep["_usage"] = ""
|
|
||||||
menu_autoBackupKeep["name"] = "backup.keep";
|
|
||||||
menu_autoBackupKeep["id"] = "backup.keep";
|
|
||||||
menu_autoBackupKeep["value"] = menu_autoBackupKeep["name"];
|
|
||||||
menu_autoBackupKeep["onclick"] = "javascript: toggleMenu(this);";
|
|
||||||
menu_autoBackupKeep["class"] = "menu-notActive";
|
|
||||||
|
|
||||||
var optionValues = new Array(5, 10, 20, 30, 40, 50);
|
|
||||||
menu_autoBackupKeep["_optionValues"] = optionValues;
|
|
||||||
|
|
||||||
|
|
||||||
var menu_buffer = new Object();
|
|
||||||
menu_buffer["_element"] = "LI";
|
|
||||||
menu_buffer["_menuType"] = "checkbox";
|
|
||||||
menu_buffer["_configKey"] = "buffer";
|
|
||||||
menu_buffer["_label"] = "Stream buffering [Experimental]";
|
|
||||||
menu_buffer["_headline"] = "Stream buffering [Experimental]";
|
|
||||||
menu_buffer["_usage"] = "With activated buffer, streams can be played and recorded more fluently.<br>The stream is passed from xTeVe to Plex / Emby"
|
|
||||||
menu_buffer["name"] = "buffer";
|
|
||||||
menu_buffer["id"] = "buffer";
|
|
||||||
menu_buffer["value"] = menu_buffer["name"];
|
|
||||||
menu_buffer["onclick"] = "javascript: toggleMenu(this);";
|
|
||||||
menu_buffer["class"] = "menu-notActive";
|
|
||||||
|
|
||||||
var menu_api = new Object();
|
|
||||||
menu_api["_element"] = "LI";
|
|
||||||
menu_api["_menuType"] = "checkbox";
|
|
||||||
menu_api["_configKey"] = "api";
|
|
||||||
menu_api["_label"] = "API interface";
|
|
||||||
menu_api["_headline"] = "API interface";
|
|
||||||
menu_api["_usage"] = 'Via API interface it is possible to send commands to xTeVe. API documentation is available <a href="https://xteve.de?scroll=api">here</a> '
|
|
||||||
//menu_api["_usage"] = 'Via API interface it is possible to send commands to xTeVe. API documentation is available <a href="http://localhost:1313?scroll=api">here</a> '
|
|
||||||
menu_api["name"] = "api";
|
|
||||||
menu_api["id"] = "api";
|
|
||||||
menu_api["value"] = menu_api["name"];
|
|
||||||
menu_api["onclick"] = "javascript: toggleMenu(this);";
|
|
||||||
menu_api["class"] = "menu-notActive";
|
|
||||||
|
|
||||||
var menu_authenticationWeb = new Object();
|
|
||||||
menu_authenticationWeb["_element"] = "LI";
|
|
||||||
menu_authenticationWeb["_menuType"] = "checkbox";
|
|
||||||
menu_authenticationWeb["_configKey"] = "authentication.web";
|
|
||||||
menu_authenticationWeb["_label"] = "User authentication";
|
|
||||||
menu_authenticationWeb["_headline"] = "User authentication";
|
|
||||||
menu_authenticationWeb["_usage"] = "Access to xTeVe requires authentication."
|
|
||||||
menu_authenticationWeb["name"] = "authentication.web";
|
|
||||||
menu_authenticationWeb["id"] = "authentication.web";
|
|
||||||
menu_authenticationWeb["value"] = menu_authenticationWeb["name"];
|
|
||||||
menu_authenticationWeb["onclick"] = "javascript: toggleMenu(this);";
|
|
||||||
menu_authenticationWeb["class"] = "menu-notActive";
|
|
||||||
|
|
||||||
var menu_authenticationPms = new Object();
|
|
||||||
menu_authenticationPms["_element"] = "LI";
|
|
||||||
menu_authenticationPms["_menuType"] = "checkbox";
|
|
||||||
menu_authenticationPms["_configKey"] = "authentication.pms";
|
|
||||||
menu_authenticationPms["_label"] = "Plex authentication.";
|
|
||||||
menu_authenticationPms["_headline"] = "Plex authentication.";
|
|
||||||
menu_authenticationPms["_usage"] = "Plex requests are only possible with authentication.<br>Warning!!! After activating this function xTeVe must be delete in the PMS DVR settings and set up again."
|
|
||||||
menu_authenticationPms["name"] = "authentication.pms";
|
|
||||||
menu_authenticationPms["id"] = "authentication.pms";
|
|
||||||
menu_authenticationPms["value"] = menu_authenticationPms["name"];
|
|
||||||
menu_authenticationPms["onclick"] = "javascript: toggleMenu(this);";
|
|
||||||
menu_authenticationPms["class"] = "menu-notActive";
|
|
||||||
|
|
||||||
var menu_authenticationM3u = new Object();
|
|
||||||
menu_authenticationM3u["_element"] = "LI";
|
|
||||||
menu_authenticationM3u["_menuType"] = "checkbox";
|
|
||||||
menu_authenticationM3u["_configKey"] = "authentication.m3u";
|
|
||||||
menu_authenticationM3u["_label"] = "M3U authentication.";
|
|
||||||
menu_authenticationM3u["_headline"] = "M3U authentication.";
|
|
||||||
menu_authenticationM3u["_usage"] = "Downloading the M3U file via an HTTP request is only possible with authentication."
|
|
||||||
menu_authenticationM3u["name"] = "authentication.m3u";
|
|
||||||
menu_authenticationM3u["id"] = "authentication.m3u";
|
|
||||||
menu_authenticationM3u["value"] = menu_authenticationM3u["name"];
|
|
||||||
menu_authenticationM3u["onclick"] = "javascript: toggleMenu(this);";
|
|
||||||
menu_authenticationM3u["class"] = "menu-notActive";
|
|
||||||
|
|
||||||
|
|
||||||
var menu_authenticationXml = new Object();
|
|
||||||
menu_authenticationXml["_element"] = "LI";
|
|
||||||
menu_authenticationXml["_menuType"] = "checkbox";
|
|
||||||
menu_authenticationXml["_configKey"] = "authentication.xml";
|
|
||||||
menu_authenticationXml["_label"] = "XEPG authentication";
|
|
||||||
menu_authenticationXml["_headline"] = "XEPG authentication";
|
|
||||||
menu_authenticationXml["_usage"] = "Downloading the XEPG (XMLTV) file via an HTTP request is only possible with authentication."
|
|
||||||
menu_authenticationXml["name"] = "authentication.xml";
|
|
||||||
menu_authenticationXml["id"] = "authentication.xml";
|
|
||||||
menu_authenticationXml["value"] = menu_authenticationXml["name"];
|
|
||||||
menu_authenticationXml["onclick"] = "javascript: toggleMenu(this);";
|
|
||||||
menu_authenticationXml["class"] = "menu-notActive";
|
|
||||||
|
|
||||||
var menu_authenticationApi = new Object();
|
|
||||||
menu_authenticationApi["_element"] = "LI";
|
|
||||||
menu_authenticationApi["_menuType"] = "checkbox";
|
|
||||||
menu_authenticationApi["_configKey"] = "authentication.api";
|
|
||||||
menu_authenticationApi["_label"] = "API authentication";
|
|
||||||
menu_authenticationApi["_headline"] = "API authentication";
|
|
||||||
menu_authenticationApi["_usage"] = "Access to the API interface is only possible with authentication."
|
|
||||||
menu_authenticationApi["name"] = "authentication.api";
|
|
||||||
menu_authenticationApi["id"] = "authentication.api";
|
|
||||||
menu_authenticationApi["value"] = menu_authenticationApi["name"];
|
|
||||||
menu_authenticationApi["onclick"] = "javascript: toggleMenu(this);";
|
|
||||||
menu_authenticationApi["class"] = "menu-notActive";
|
|
||||||
|
|
||||||
|
|
||||||
// Main menu
|
|
||||||
menu[10] = menu_m3u;
|
|
||||||
|
|
||||||
switch(config["epgSource"]) {
|
|
||||||
case "PMS":
|
|
||||||
menu[20] = menu_id;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "XMLTV":
|
|
||||||
menu[40] = menu_xmltv;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "XEPG":
|
|
||||||
menu[40] = menu_xmltv;
|
|
||||||
menu[50] = menu_mapping;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
menu[30] = menu_filter;
|
|
||||||
|
|
||||||
if (config["authentication.web"] == true) {
|
|
||||||
menu[60] = menu_users;
|
|
||||||
}
|
|
||||||
|
|
||||||
menu[70] = menu_settings;
|
|
||||||
menu[80] = menu_log;
|
|
||||||
if (config["authentication.web"] == true) {
|
|
||||||
menu[100] = menu_logout;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Sub-Menu
|
|
||||||
|
|
||||||
subMenu[701] = menu_schedule;
|
|
||||||
subMenu[702] = menu_filesUpdate;
|
|
||||||
subMenu[703] = menu_tuner;
|
|
||||||
subMenu[704] = menu_epg;
|
|
||||||
subMenu[705] = menu_xepg;
|
|
||||||
subMenu[706] = menu_autoBackupPath;
|
|
||||||
subMenu[707] = menu_autoBackupKeep;
|
|
||||||
subMenu[708] = menu_buffer;
|
|
||||||
|
|
||||||
subMenu[710] = menu_authenticationWeb;
|
|
||||||
|
|
||||||
if (config["authentication.web"] == true) {
|
|
||||||
subMenu[711] = menu_authenticationPms;
|
|
||||||
subMenu[712] = menu_authenticationM3u;
|
|
||||||
subMenu[713] = menu_authenticationXml;
|
|
||||||
subMenu[714] = menu_authenticationApi;
|
|
||||||
}
|
|
||||||
|
|
||||||
subMenu[799] = menu_api;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
function createMenu() {
|
|
||||||
|
|
||||||
showElement("popup", false);
|
|
||||||
|
|
||||||
//console.log(config);
|
|
||||||
setMenuItem();
|
|
||||||
var menuItems = getObjKeys(menu)
|
|
||||||
var nav = document.getElementsByTagName("NAV")[0];
|
|
||||||
nav.innerHTML = "";
|
|
||||||
var newItem = new Object();
|
|
||||||
|
|
||||||
for (var i = 0; i < menuItems.length; i++) {
|
|
||||||
|
|
||||||
|
|
||||||
var newItem = menu[menuItems[i]];
|
|
||||||
newItem["id"] = menuItems[i];
|
|
||||||
|
|
||||||
|
|
||||||
switch(newItem.hasOwnProperty("_icon")) {
|
|
||||||
case true:
|
|
||||||
var itemText = newItem["_text"];
|
|
||||||
delete newItem["_text"]
|
|
||||||
nav.appendChild(createElement(newItem));
|
|
||||||
newItem["_text"] = itemText;
|
|
||||||
var newIcon = new Object();
|
|
||||||
newIcon["_element"] = "IMG";
|
|
||||||
newIcon["src"] = newItem["_icon"];
|
|
||||||
|
|
||||||
var currentElement = document.getElementById(menuItems[i]);
|
|
||||||
currentElement.appendChild(createElement(newIcon));
|
|
||||||
|
|
||||||
|
|
||||||
var text = new Object();
|
|
||||||
text["_element"] = "P"
|
|
||||||
text["_text"] = itemText;
|
|
||||||
text["class"] = "nav-text"
|
|
||||||
currentElement.appendChild(createElement(text));
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
nav.appendChild(createElement(newIcon));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
if (activeMenu != undefined) {
|
|
||||||
//console.log(activeMenu);
|
|
||||||
toggleMenu(activeMenu);
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleMenu(elm) {
|
|
||||||
//showStreams(false);
|
|
||||||
clearInterval(logInterval)
|
|
||||||
activeMenu = elm;
|
|
||||||
var item = menu[elm.id]
|
|
||||||
var div = document.getElementById("settings");
|
|
||||||
div.innerHTML = "";
|
|
||||||
|
|
||||||
// Set Headline
|
|
||||||
var headline = new Object();
|
|
||||||
headline["_element"] = "H4";
|
|
||||||
headline["_text"] = item["_headline"];
|
|
||||||
div.appendChild(createElement(headline));
|
|
||||||
|
|
||||||
// Sub-Menu
|
|
||||||
if (item.hasOwnProperty("_subMenu") == true) {
|
|
||||||
openSubMenu(item);
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mapping, Users, Log, Files
|
|
||||||
switch(item["_configKey"]) {
|
|
||||||
case "mapping": openMappingEditor(item); return; break;
|
|
||||||
case "users": openUsers(item); return; break;
|
|
||||||
case "log": showLog(item); return; break;
|
|
||||||
case "files.m3u": openFiles(item, "m3u"); return; break;
|
|
||||||
case "files.xmltv": openFiles(item, "xmltv"); return; break;
|
|
||||||
|
|
||||||
case "filter": showStreams(true); break;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var newHR = new Object();
|
|
||||||
newHR["_element"] = "HR"
|
|
||||||
div.appendChild(createElement(newHR));
|
|
||||||
|
|
||||||
var newEntry = new Object();
|
|
||||||
newEntry["_element"] = "INPUT";
|
|
||||||
newEntry["type"] = "button";
|
|
||||||
//newEntry["class"] = "save";
|
|
||||||
newEntry["value"] = "Save";
|
|
||||||
newEntry["onclick"] = "saveData2('settings')"
|
|
||||||
div.appendChild(createElement(newEntry));
|
|
||||||
|
|
||||||
|
|
||||||
var newWrapper = new Object();
|
|
||||||
newWrapper["_element"] = "DIV";
|
|
||||||
newWrapper["id"] = "box-wrapper";
|
|
||||||
div.appendChild(createElement(newWrapper));
|
|
||||||
|
|
||||||
div = div.lastChild;
|
|
||||||
|
|
||||||
div.appendChild(createMenuItem(item))
|
|
||||||
|
|
||||||
// usage Info
|
|
||||||
switch(menu[activeMenu.id].hasOwnProperty("_usage")) {
|
|
||||||
case true:
|
|
||||||
var usageItem = new Object();
|
|
||||||
usageItem["_element"] = "PRE"
|
|
||||||
usageItem["_text"] = menu[activeMenu.id]["_usage"];
|
|
||||||
div.appendChild(createElement(usageItem));
|
|
||||||
}
|
|
||||||
|
|
||||||
calculateWrapperHeight();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function createMenuItem(item) {
|
|
||||||
var element = document.createElement("DIV");
|
|
||||||
switch(item["_menuType"]) {
|
|
||||||
case "inputArray":
|
|
||||||
if (config.hasOwnProperty(item["_configKey"]) == true) {
|
|
||||||
var value = config[item["_configKey"]];
|
|
||||||
} else {
|
|
||||||
var value = new Array();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var i = 0; i < value.length; i++) {
|
|
||||||
var newEntry = new Object();
|
|
||||||
newEntry = item
|
|
||||||
delete newEntry["onclick"];
|
|
||||||
newEntry["_element"] = "INPUT";
|
|
||||||
newEntry["value"] = value[i];
|
|
||||||
newEntry["type"] = "search";
|
|
||||||
newEntry["data-menutype"] = item["_menuType"];
|
|
||||||
newEntry["data-menukey"] = item["_configKey"];
|
|
||||||
element.appendChild(createElement(newEntry));
|
|
||||||
|
|
||||||
}
|
|
||||||
// New entry for array
|
|
||||||
var newEntry = new Object();
|
|
||||||
newEntry["_element"] = "INPUT";
|
|
||||||
newEntry["type"] = "search";
|
|
||||||
newEntry["name"] = item["name"];
|
|
||||||
newEntry["placeholder"] = item["placeholder"];
|
|
||||||
newEntry["value"] = "";
|
|
||||||
newEntry["data-menutype"] = item["_menuType"];
|
|
||||||
newEntry["data-menukey"] = item["_configKey"];
|
|
||||||
element.appendChild(createElement(newEntry));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "singleInput":
|
|
||||||
var value = config[item["_configKey"]];
|
|
||||||
if (value == undefined) {
|
|
||||||
value = "";
|
|
||||||
}
|
|
||||||
var newEntry = new Object();
|
|
||||||
newEntry = item;
|
|
||||||
delete newEntry["onclick"];
|
|
||||||
newEntry["_element"] = "INPUT";
|
|
||||||
newEntry["value"] = value;
|
|
||||||
newEntry["type"] = "search";
|
|
||||||
newEntry["data-menutype"] = item["_menuType"];
|
|
||||||
newEntry["data-menukey"] = item["_configKey"];
|
|
||||||
element.appendChild(createElement(newEntry));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "checkbox":
|
|
||||||
var value = config[item["_configKey"]];
|
|
||||||
if (value == undefined) {
|
|
||||||
value = false;
|
|
||||||
}
|
|
||||||
var newEntry = new Object();
|
|
||||||
newEntry = item;
|
|
||||||
delete newEntry["onclick"];
|
|
||||||
newEntry["_element"] = "INPUT";
|
|
||||||
newEntry["value"] = value;
|
|
||||||
newEntry["type"] = "checkbox";
|
|
||||||
newEntry["data-menutype"] = item["_menuType"];
|
|
||||||
newEntry["data-menukey"] = item["_configKey"];
|
|
||||||
element.appendChild(createElement(newEntry));
|
|
||||||
element.getElementsByTagName("INPUT")[0].checked = value;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "select":
|
|
||||||
var value = config[item["_configKey"]];
|
|
||||||
var newEntry = new Object();
|
|
||||||
newEntry = item;
|
|
||||||
delete newEntry["onclick"]
|
|
||||||
newEntry["_element"] = "SELECT";
|
|
||||||
element.appendChild(createElement(newEntry));
|
|
||||||
var selectElement = element.getElementsByTagName("SELECT")[0];
|
|
||||||
var values = item["_optionValues"];
|
|
||||||
for (var i = 0; i < values.length; i++) {
|
|
||||||
var newEntry = new Object;
|
|
||||||
newEntry["_element"] = "OPTION";
|
|
||||||
newEntry["_text"] = item["_text"] + ": " + values[i];
|
|
||||||
newEntry["value"] = values[i];
|
|
||||||
selectElement.appendChild(createElement(newEntry));
|
|
||||||
}
|
|
||||||
selectElement.value = value;
|
|
||||||
break;
|
|
||||||
|
|
||||||
}
|
|
||||||
return element;
|
|
||||||
}
|
|
||||||
|
|
||||||
function openSubMenu(item) {
|
|
||||||
var entrys = item["_subMenu"].split(",");
|
|
||||||
var div = document.getElementById("settings");
|
|
||||||
|
|
||||||
var newHR = new Object();
|
|
||||||
newHR["_element"] = "HR"
|
|
||||||
div.appendChild(createElement(newHR));
|
|
||||||
|
|
||||||
var newEntry = new Object();
|
|
||||||
newEntry["_element"] = "INPUT";
|
|
||||||
newEntry["type"] = "button";
|
|
||||||
//newEntry["class"] = "save";
|
|
||||||
newEntry["value"] = "Save";
|
|
||||||
newEntry["onclick"] = "saveData2('settings')"
|
|
||||||
div.appendChild(createElement(newEntry));
|
|
||||||
|
|
||||||
if (item["_configKey"] == "settings") {
|
|
||||||
var newEntry = new Object();
|
|
||||||
newEntry["_element"] = "INPUT";
|
|
||||||
newEntry["type"] = "button";
|
|
||||||
//newEntry["class"] = "save";
|
|
||||||
newEntry["value"] = "Backup";
|
|
||||||
newEntry["onclick"] = "xteveBackup()"
|
|
||||||
div.appendChild(createElement(newEntry));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item["_configKey"] == "settings") {
|
|
||||||
var newEntry = new Object();
|
|
||||||
newEntry["_element"] = "INPUT";
|
|
||||||
newEntry["type"] = "button";
|
|
||||||
//newEntry["class"] = "save";
|
|
||||||
newEntry["value"] = "Restore";
|
|
||||||
newEntry["onclick"] = "xteveRestore(this)"
|
|
||||||
div.appendChild(createElement(newEntry));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
var newWrapper = new Object();
|
|
||||||
newWrapper["_element"] = "DIV";
|
|
||||||
newWrapper["id"] = "box-wrapper";
|
|
||||||
div.appendChild(createElement(newWrapper));
|
|
||||||
|
|
||||||
div = div.lastChild;
|
|
||||||
|
|
||||||
|
|
||||||
for (var i = 0; i < entrys.length; i++) {
|
|
||||||
var item = subMenu[entrys[i]];
|
|
||||||
if (item == undefined) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
var container = new Object();
|
|
||||||
container["_element"] = "DIV";
|
|
||||||
div.appendChild(createElement(container));
|
|
||||||
|
|
||||||
var divContainer = div.lastChild;
|
|
||||||
|
|
||||||
var headline = new Object();
|
|
||||||
headline["_element"] = "H5";
|
|
||||||
headline["_text"] = item["_headline"];
|
|
||||||
divContainer.appendChild(createElement(headline));
|
|
||||||
|
|
||||||
divContainer.appendChild(createMenuItem(item))
|
|
||||||
|
|
||||||
switch(item.hasOwnProperty("_usage")) {
|
|
||||||
case true:
|
|
||||||
var usageItem = new Object();
|
|
||||||
usageItem["_element"] = "PRE"
|
|
||||||
usageItem["_text"] = item["_usage"];
|
|
||||||
divContainer.appendChild(createElement(usageItem));
|
|
||||||
}
|
|
||||||
|
|
||||||
var hr = new Object();
|
|
||||||
hr["_element"] = "HR";
|
|
||||||
divContainer.appendChild(createElement(hr));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
calculateWrapperHeight();
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
function saveData2(elm) {
|
|
||||||
var div = document.getElementById(elm);
|
|
||||||
var inputs = div.getElementsByTagName("INPUT");
|
|
||||||
var selects = div.getElementsByTagName("SELECT");
|
|
||||||
var value, configKey;
|
|
||||||
var data = new Object();
|
|
||||||
var valueArr = new Array();
|
|
||||||
var newData = false;
|
|
||||||
|
|
||||||
for (var i = 0; i < inputs.length; i++) {
|
|
||||||
if (inputs[i].type != "button") {
|
|
||||||
var menuType = inputs[i].getAttribute("data-menutype");
|
|
||||||
|
|
||||||
//console.log(menuType);
|
|
||||||
switch(menuType) {
|
|
||||||
case "singleInput":
|
|
||||||
value = inputs[i].value;
|
|
||||||
if (value == "" || value == undefined) {
|
|
||||||
data = new Object();
|
|
||||||
data["delete"] = inputs[i].name
|
|
||||||
newData = true;
|
|
||||||
} else {
|
|
||||||
newData = true;
|
|
||||||
data[inputs[i].name] = value;
|
|
||||||
console.log(data);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "inputArray":
|
|
||||||
value = inputs[i].value;
|
|
||||||
if (value != "" && value != undefined) {
|
|
||||||
newData = true;
|
|
||||||
valueArr.push(value)
|
|
||||||
data[inputs[i].name] = valueArr;
|
|
||||||
configKey = inputs[i].name;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "checkbox":
|
|
||||||
value = inputs[i].checked
|
|
||||||
data[inputs[i].name] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Delete config key
|
|
||||||
if (valueArr.length == 0 && newData == false) {
|
|
||||||
newData = true;
|
|
||||||
data = new Object();
|
|
||||||
data["delete"] = configKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
for (var i = 0; i < selects.length; i++) {
|
|
||||||
var value = selects[i].options[selects[i].selectedIndex].value;
|
|
||||||
switch(isNaN(value)) {
|
|
||||||
case false: value = parseInt(value); break;
|
|
||||||
}
|
|
||||||
|
|
||||||
data[selects[i].name] = value;
|
|
||||||
newData = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//console.log(data, newData);
|
|
||||||
|
|
||||||
if (newData == true) {
|
|
||||||
data["cmd"] = "saveConfig";
|
|
||||||
if (!data.hasOwnProperty('filter')) {
|
|
||||||
data["filter"] = config["filter"]
|
|
||||||
}
|
|
||||||
var settings = new Object();
|
|
||||||
settings["cmd"] = data["cmd"];
|
|
||||||
settings["settings"] = data;
|
|
||||||
console.log(settings);
|
|
||||||
xTeVe(settings);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,172 +0,0 @@
|
|||||||
var Server = /** @class */ (function () {
|
|
||||||
function Server(cmd) {
|
|
||||||
this.cmd = cmd;
|
|
||||||
}
|
|
||||||
Server.prototype.request = function (data) {
|
|
||||||
if (SERVER_CONNECTION == true) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
SERVER_CONNECTION = true;
|
|
||||||
console.log(data);
|
|
||||||
if (this.cmd != "updateLog") {
|
|
||||||
showElement("loading", true);
|
|
||||||
UNDO = new Object();
|
|
||||||
setConnectionState("busy");
|
|
||||||
}
|
|
||||||
switch (window.location.protocol) {
|
|
||||||
case "http:":
|
|
||||||
this.protocol = "ws://";
|
|
||||||
break;
|
|
||||||
case "https:":
|
|
||||||
this.protocol = "wss://";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
var wsHost = window.location.host;
|
|
||||||
if (wsHost == undefined || wsHost.length < 1) {
|
|
||||||
wsHost = window.location.hostname;
|
|
||||||
}
|
|
||||||
var url = this.protocol + wsHost + "/data/" + "?Token=" + getCookie("Token");
|
|
||||||
data["cmd"] = this.cmd;
|
|
||||||
var requestCmd = data["cmd"];
|
|
||||||
var ws = new WebSocket(url);
|
|
||||||
var isLogUpdate = data["cmd"] == "updateLog";
|
|
||||||
var responseReceived = false;
|
|
||||||
var requestFinished = false;
|
|
||||||
var timeoutMs = 12000;
|
|
||||||
var requestTimeout;
|
|
||||||
var finishRequest = function (state, responseSuccess) {
|
|
||||||
if (responseSuccess === void 0) { responseSuccess = false; }
|
|
||||||
if (requestFinished == true) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
requestFinished = true;
|
|
||||||
SERVER_CONNECTION = false;
|
|
||||||
window.clearTimeout(requestTimeout);
|
|
||||||
if (responseSuccess == true) {
|
|
||||||
if (state == "online") {
|
|
||||||
WS_FAILURE_COUNT = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
WS_FAILURE_COUNT++;
|
|
||||||
}
|
|
||||||
if (isLogUpdate == false) {
|
|
||||||
showElement("loading", false);
|
|
||||||
}
|
|
||||||
if (state != "") {
|
|
||||||
setConnectionState(state);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
requestTimeout = window.setTimeout(function () {
|
|
||||||
console.log("Websocket request timed out.");
|
|
||||||
var timeoutState = "offline";
|
|
||||||
if (isLogUpdate == true && WS_FAILURE_COUNT < 2) {
|
|
||||||
timeoutState = "idle";
|
|
||||||
}
|
|
||||||
finishRequest(timeoutState, false);
|
|
||||||
try {
|
|
||||||
ws.close();
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
console.log(err);
|
|
||||||
}
|
|
||||||
}, timeoutMs);
|
|
||||||
ws.onopen = function () {
|
|
||||||
WS_AVAILABLE = true;
|
|
||||||
if (data["cmd"] != "updateLog") {
|
|
||||||
setConnectionState("busy");
|
|
||||||
}
|
|
||||||
console.log("REQUEST (JS):");
|
|
||||||
console.log(data);
|
|
||||||
console.log("REQUEST: (JSON)");
|
|
||||||
console.log(JSON.stringify(data));
|
|
||||||
this.send(JSON.stringify(data));
|
|
||||||
};
|
|
||||||
ws.onerror = function (e) {
|
|
||||||
console.log("No websocket connection to xTeVe could be established. Check your network configuration.");
|
|
||||||
var errorState = "offline";
|
|
||||||
if (isLogUpdate == true && WS_FAILURE_COUNT < 2) {
|
|
||||||
errorState = "idle";
|
|
||||||
}
|
|
||||||
finishRequest(errorState, false);
|
|
||||||
if (WS_AVAILABLE == false && isLogUpdate == false && requestCmd != "getServerConfig") {
|
|
||||||
alert("No websocket connection to xTeVe could be established. Check your network configuration.");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
ws.onmessage = function (e) {
|
|
||||||
responseReceived = true;
|
|
||||||
finishRequest("online", true);
|
|
||||||
console.log("RESPONSE:");
|
|
||||||
var response = JSON.parse(e.data);
|
|
||||||
console.log(response);
|
|
||||||
if (response.hasOwnProperty("token")) {
|
|
||||||
document.cookie = "Token=" + response["token"];
|
|
||||||
}
|
|
||||||
if (response["status"] == false) {
|
|
||||||
setConnectionState("offline");
|
|
||||||
alert(response["err"]);
|
|
||||||
if (response.hasOwnProperty("reload")) {
|
|
||||||
location.reload();
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (response.hasOwnProperty("logoURL")) {
|
|
||||||
var div = document.getElementById("channel-icon");
|
|
||||||
div.value = response["logoURL"];
|
|
||||||
div.className = "changed";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
switch (data["cmd"]) {
|
|
||||||
case "updateLog":
|
|
||||||
SERVER["log"] = response["log"];
|
|
||||||
if (document.getElementById("content_log")) {
|
|
||||||
showLogs(false);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
SERVER = new Object();
|
|
||||||
SERVER = response;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (response.hasOwnProperty("openMenu")) {
|
|
||||||
var menu = document.getElementById(response["openMenu"]);
|
|
||||||
menu.click();
|
|
||||||
showElement("popup", false);
|
|
||||||
}
|
|
||||||
if (response.hasOwnProperty("openLink")) {
|
|
||||||
window.location = response["openLink"];
|
|
||||||
}
|
|
||||||
if (response.hasOwnProperty("alert")) {
|
|
||||||
alert(response["alert"]);
|
|
||||||
}
|
|
||||||
if (response.hasOwnProperty("reload")) {
|
|
||||||
location.reload();
|
|
||||||
}
|
|
||||||
if (response.hasOwnProperty("wizard")) {
|
|
||||||
createLayout();
|
|
||||||
configurationWizard[response["wizard"]].createWizard();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
createLayout();
|
|
||||||
};
|
|
||||||
ws.onclose = function () {
|
|
||||||
if (responseReceived == true) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var closeState = "offline";
|
|
||||||
if (isLogUpdate == true && WS_FAILURE_COUNT < 2) {
|
|
||||||
closeState = "idle";
|
|
||||||
}
|
|
||||||
finishRequest(closeState, false);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
return Server;
|
|
||||||
}());
|
|
||||||
var WS_FAILURE_COUNT = 0;
|
|
||||||
function getCookie(name) {
|
|
||||||
var value = "; " + document.cookie;
|
|
||||||
var parts = value.split("; " + name + "=");
|
|
||||||
if (parts.length == 2)
|
|
||||||
return parts.pop().split(";").shift();
|
|
||||||
}
|
|
||||||
@@ -1,557 +0,0 @@
|
|||||||
var __extends = (this && this.__extends) || (function () {
|
|
||||||
var extendStatics = function (d, b) {
|
|
||||||
extendStatics = Object.setPrototypeOf ||
|
|
||||||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
|
||||||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
|
|
||||||
return extendStatics(d, b);
|
|
||||||
};
|
|
||||||
return function (d, b) {
|
|
||||||
extendStatics(d, b);
|
|
||||||
function __() { this.constructor = d; }
|
|
||||||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
||||||
};
|
|
||||||
})();
|
|
||||||
var SettingsCategory = /** @class */ (function () {
|
|
||||||
function SettingsCategory() {
|
|
||||||
this.DocumentID = "content_settings";
|
|
||||||
}
|
|
||||||
SettingsCategory.prototype.createCategoryHeadline = function (value) {
|
|
||||||
var element = document.createElement("H4");
|
|
||||||
element.innerHTML = value;
|
|
||||||
return element;
|
|
||||||
};
|
|
||||||
SettingsCategory.prototype.createHR = function () {
|
|
||||||
var element = document.createElement("HR");
|
|
||||||
return element;
|
|
||||||
};
|
|
||||||
SettingsCategory.prototype.createSettings = function (settingsKey) {
|
|
||||||
var setting = document.createElement("TR");
|
|
||||||
var content = new PopupContent();
|
|
||||||
var data = SERVER["settings"][settingsKey];
|
|
||||||
switch (settingsKey) {
|
|
||||||
// Texteingaben
|
|
||||||
case "update":
|
|
||||||
var tdLeft = document.createElement("TD");
|
|
||||||
tdLeft.innerHTML = "{{.settings.update.title}}" + ":";
|
|
||||||
var tdRight = document.createElement("TD");
|
|
||||||
var input = content.createInput("text", "update", data.toString());
|
|
||||||
input.setAttribute("placeholder", "{{.settings.update.placeholder}}");
|
|
||||||
input.setAttribute("onchange", "javascript: this.className = 'changed'");
|
|
||||||
tdRight.appendChild(input);
|
|
||||||
setting.appendChild(tdLeft);
|
|
||||||
setting.appendChild(tdRight);
|
|
||||||
break;
|
|
||||||
case "backup.path":
|
|
||||||
var tdLeft = document.createElement("TD");
|
|
||||||
tdLeft.innerHTML = "{{.settings.backupPath.title}}" + ":";
|
|
||||||
var tdRight = document.createElement("TD");
|
|
||||||
var input = content.createInput("text", "backup.path", data);
|
|
||||||
input.setAttribute("placeholder", "{{.settings.backupPath.placeholder}}");
|
|
||||||
input.setAttribute("onchange", "javascript: this.className = 'changed'");
|
|
||||||
tdRight.appendChild(input);
|
|
||||||
setting.appendChild(tdLeft);
|
|
||||||
setting.appendChild(tdRight);
|
|
||||||
break;
|
|
||||||
case "temp.path":
|
|
||||||
var tdLeft = document.createElement("TD");
|
|
||||||
tdLeft.innerHTML = "{{.settings.tempPath.title}}" + ":";
|
|
||||||
var tdRight = document.createElement("TD");
|
|
||||||
var input = content.createInput("text", "temp.path", data);
|
|
||||||
input.setAttribute("placeholder", "{{.settings.tmpPath.placeholder}}");
|
|
||||||
input.setAttribute("onchange", "javascript: this.className = 'changed'");
|
|
||||||
tdRight.appendChild(input);
|
|
||||||
setting.appendChild(tdLeft);
|
|
||||||
setting.appendChild(tdRight);
|
|
||||||
break;
|
|
||||||
case "user.agent":
|
|
||||||
var tdLeft = document.createElement("TD");
|
|
||||||
tdLeft.innerHTML = "{{.settings.userAgent.title}}" + ":";
|
|
||||||
var tdRight = document.createElement("TD");
|
|
||||||
var input = content.createInput("text", "user.agent", data);
|
|
||||||
input.setAttribute("placeholder", "{{.settings.userAgent.placeholder}}");
|
|
||||||
input.setAttribute("onchange", "javascript: this.className = 'changed'");
|
|
||||||
tdRight.appendChild(input);
|
|
||||||
setting.appendChild(tdLeft);
|
|
||||||
setting.appendChild(tdRight);
|
|
||||||
break;
|
|
||||||
case "plex.url":
|
|
||||||
var tdLeft = document.createElement("TD");
|
|
||||||
tdLeft.innerHTML = "{{.settings.plexURL.title}}" + ":";
|
|
||||||
var tdRight = document.createElement("TD");
|
|
||||||
var input = content.createInput("text", "plex.url", data);
|
|
||||||
input.setAttribute("placeholder", "{{.settings.plexURL.placeholder}}");
|
|
||||||
input.setAttribute("onchange", "javascript: this.className = 'changed'");
|
|
||||||
tdRight.appendChild(input);
|
|
||||||
setting.appendChild(tdLeft);
|
|
||||||
setting.appendChild(tdRight);
|
|
||||||
break;
|
|
||||||
case "plex.token":
|
|
||||||
var tdLeft = document.createElement("TD");
|
|
||||||
tdLeft.innerHTML = "{{.settings.plexToken.title}}" + ":";
|
|
||||||
var tdRight = document.createElement("TD");
|
|
||||||
var input = content.createInput("password", "plex.token", data);
|
|
||||||
input.setAttribute("placeholder", "{{.settings.plexToken.placeholder}}");
|
|
||||||
input.setAttribute("autocomplete", "off");
|
|
||||||
input.setAttribute("onchange", "javascript: this.className = 'changed'");
|
|
||||||
tdRight.appendChild(input);
|
|
||||||
setting.appendChild(tdLeft);
|
|
||||||
setting.appendChild(tdRight);
|
|
||||||
break;
|
|
||||||
case "buffer.timeout":
|
|
||||||
var tdLeft = document.createElement("TD");
|
|
||||||
tdLeft.innerHTML = "{{.settings.bufferTimeout.title}}" + ":";
|
|
||||||
var tdRight = document.createElement("TD");
|
|
||||||
var input = content.createInput("text", "buffer.timeout", data);
|
|
||||||
input.setAttribute("placeholder", "{{.settings.bufferTimeout.placeholder}}");
|
|
||||||
input.setAttribute("onchange", "javascript: this.className = 'changed'");
|
|
||||||
tdRight.appendChild(input);
|
|
||||||
setting.appendChild(tdLeft);
|
|
||||||
setting.appendChild(tdRight);
|
|
||||||
break;
|
|
||||||
case "ffmpeg.path":
|
|
||||||
var tdLeft = document.createElement("TD");
|
|
||||||
tdLeft.innerHTML = "{{.settings.ffmpegPath.title}}" + ":";
|
|
||||||
var tdRight = document.createElement("TD");
|
|
||||||
var input = content.createInput("text", "ffmpeg.path", data);
|
|
||||||
input.setAttribute("placeholder", "{{.settings.ffmpegPath.placeholder}}");
|
|
||||||
input.setAttribute("onchange", "javascript: this.className = 'changed'");
|
|
||||||
tdRight.appendChild(input);
|
|
||||||
setting.appendChild(tdLeft);
|
|
||||||
setting.appendChild(tdRight);
|
|
||||||
break;
|
|
||||||
case "ffmpeg.options":
|
|
||||||
var tdLeft = document.createElement("TD");
|
|
||||||
tdLeft.innerHTML = "{{.settings.ffmpegOptions.title}}" + ":";
|
|
||||||
var tdRight = document.createElement("TD");
|
|
||||||
var input = content.createInput("text", "ffmpeg.options", data);
|
|
||||||
input.setAttribute("placeholder", "{{.settings.ffmpegOptions.placeholder}}");
|
|
||||||
input.setAttribute("onchange", "javascript: this.className = 'changed'");
|
|
||||||
tdRight.appendChild(input);
|
|
||||||
setting.appendChild(tdLeft);
|
|
||||||
setting.appendChild(tdRight);
|
|
||||||
break;
|
|
||||||
case "vlc.path":
|
|
||||||
var tdLeft = document.createElement("TD");
|
|
||||||
tdLeft.innerHTML = "{{.settings.vlcPath.title}}" + ":";
|
|
||||||
var tdRight = document.createElement("TD");
|
|
||||||
var input = content.createInput("text", "vlc.path", data);
|
|
||||||
input.setAttribute("placeholder", "{{.settings.vlcPath.placeholder}}");
|
|
||||||
input.setAttribute("onchange", "javascript: this.className = 'changed'");
|
|
||||||
tdRight.appendChild(input);
|
|
||||||
setting.appendChild(tdLeft);
|
|
||||||
setting.appendChild(tdRight);
|
|
||||||
break;
|
|
||||||
case "vlc.options":
|
|
||||||
var tdLeft = document.createElement("TD");
|
|
||||||
tdLeft.innerHTML = "{{.settings.vlcOptions.title}}" + ":";
|
|
||||||
var tdRight = document.createElement("TD");
|
|
||||||
var input = content.createInput("text", "vlc.options", data);
|
|
||||||
input.setAttribute("placeholder", "{{.settings.vlcOptions.placeholder}}");
|
|
||||||
input.setAttribute("onchange", "javascript: this.className = 'changed'");
|
|
||||||
tdRight.appendChild(input);
|
|
||||||
setting.appendChild(tdLeft);
|
|
||||||
setting.appendChild(tdRight);
|
|
||||||
break;
|
|
||||||
// Checkboxen
|
|
||||||
case "authentication.web":
|
|
||||||
var tdLeft = document.createElement("TD");
|
|
||||||
tdLeft.innerHTML = "{{.settings.authenticationWEB.title}}" + ":";
|
|
||||||
var tdRight = document.createElement("TD");
|
|
||||||
var input = content.createCheckbox(settingsKey);
|
|
||||||
input.checked = data;
|
|
||||||
input.setAttribute("onchange", "javascript: this.className = 'changed'");
|
|
||||||
tdRight.appendChild(input);
|
|
||||||
setting.appendChild(tdLeft);
|
|
||||||
setting.appendChild(tdRight);
|
|
||||||
break;
|
|
||||||
case "authentication.pms":
|
|
||||||
var tdLeft = document.createElement("TD");
|
|
||||||
tdLeft.innerHTML = "{{.settings.authenticationPMS.title}}" + ":";
|
|
||||||
var tdRight = document.createElement("TD");
|
|
||||||
var input = content.createCheckbox(settingsKey);
|
|
||||||
input.checked = data;
|
|
||||||
input.setAttribute("onchange", "javascript: this.className = 'changed'");
|
|
||||||
tdRight.appendChild(input);
|
|
||||||
setting.appendChild(tdLeft);
|
|
||||||
setting.appendChild(tdRight);
|
|
||||||
break;
|
|
||||||
case "authentication.m3u":
|
|
||||||
var tdLeft = document.createElement("TD");
|
|
||||||
tdLeft.innerHTML = "{{.settings.authenticationM3U.title}}" + ":";
|
|
||||||
var tdRight = document.createElement("TD");
|
|
||||||
var input = content.createCheckbox(settingsKey);
|
|
||||||
input.checked = data;
|
|
||||||
input.setAttribute("onchange", "javascript: this.className = 'changed'");
|
|
||||||
tdRight.appendChild(input);
|
|
||||||
setting.appendChild(tdLeft);
|
|
||||||
setting.appendChild(tdRight);
|
|
||||||
break;
|
|
||||||
case "authentication.xml":
|
|
||||||
var tdLeft = document.createElement("TD");
|
|
||||||
tdLeft.innerHTML = "{{.settings.authenticationXML.title}}" + ":";
|
|
||||||
var tdRight = document.createElement("TD");
|
|
||||||
var input = content.createCheckbox(settingsKey);
|
|
||||||
input.checked = data;
|
|
||||||
input.setAttribute("onchange", "javascript: this.className = 'changed'");
|
|
||||||
tdRight.appendChild(input);
|
|
||||||
setting.appendChild(tdLeft);
|
|
||||||
setting.appendChild(tdRight);
|
|
||||||
break;
|
|
||||||
case "authentication.api":
|
|
||||||
var tdLeft = document.createElement("TD");
|
|
||||||
tdLeft.innerHTML = "{{.settings.authenticationAPI.title}}" + ":";
|
|
||||||
var tdRight = document.createElement("TD");
|
|
||||||
var input = content.createCheckbox(settingsKey);
|
|
||||||
input.checked = data;
|
|
||||||
input.setAttribute("onchange", "javascript: this.className = 'changed'");
|
|
||||||
tdRight.appendChild(input);
|
|
||||||
setting.appendChild(tdLeft);
|
|
||||||
setting.appendChild(tdRight);
|
|
||||||
break;
|
|
||||||
case "files.update":
|
|
||||||
var tdLeft = document.createElement("TD");
|
|
||||||
tdLeft.innerHTML = "{{.settings.filesUpdate.title}}" + ":";
|
|
||||||
var tdRight = document.createElement("TD");
|
|
||||||
var input = content.createCheckbox(settingsKey);
|
|
||||||
input.checked = data;
|
|
||||||
input.setAttribute("onchange", "javascript: this.className = 'changed'");
|
|
||||||
tdRight.appendChild(input);
|
|
||||||
setting.appendChild(tdLeft);
|
|
||||||
setting.appendChild(tdRight);
|
|
||||||
break;
|
|
||||||
case "cache.images":
|
|
||||||
var tdLeft = document.createElement("TD");
|
|
||||||
tdLeft.innerHTML = "{{.settings.cacheImages.title}}" + ":";
|
|
||||||
var tdRight = document.createElement("TD");
|
|
||||||
var input = content.createCheckbox(settingsKey);
|
|
||||||
input.checked = data;
|
|
||||||
input.setAttribute("onchange", "javascript: this.className = 'changed'");
|
|
||||||
tdRight.appendChild(input);
|
|
||||||
setting.appendChild(tdLeft);
|
|
||||||
setting.appendChild(tdRight);
|
|
||||||
break;
|
|
||||||
case "xepg.replace.missing.images":
|
|
||||||
var tdLeft = document.createElement("TD");
|
|
||||||
tdLeft.innerHTML = "{{.settings.replaceEmptyImages.title}}" + ":";
|
|
||||||
var tdRight = document.createElement("TD");
|
|
||||||
var input = content.createCheckbox(settingsKey);
|
|
||||||
input.checked = data;
|
|
||||||
input.setAttribute("onchange", "javascript: this.className = 'changed'");
|
|
||||||
tdRight.appendChild(input);
|
|
||||||
setting.appendChild(tdLeft);
|
|
||||||
setting.appendChild(tdRight);
|
|
||||||
break;
|
|
||||||
case "api":
|
|
||||||
var tdLeft = document.createElement("TD");
|
|
||||||
tdLeft.innerHTML = "{{.settings.api.title}}" + ":";
|
|
||||||
var tdRight = document.createElement("TD");
|
|
||||||
var input = content.createCheckbox(settingsKey);
|
|
||||||
input.checked = data;
|
|
||||||
input.setAttribute("onchange", "javascript: this.className = 'changed'");
|
|
||||||
tdRight.appendChild(input);
|
|
||||||
setting.appendChild(tdLeft);
|
|
||||||
setting.appendChild(tdRight);
|
|
||||||
break;
|
|
||||||
case "use_plexAPI":
|
|
||||||
var tdLeft = document.createElement("TD");
|
|
||||||
tdLeft.innerHTML = "{{.settings.usePlexAPI.title}}" + ":";
|
|
||||||
var tdRight = document.createElement("TD");
|
|
||||||
var input = content.createCheckbox(settingsKey);
|
|
||||||
input.checked = data;
|
|
||||||
input.setAttribute("onchange", "javascript: this.className = 'changed'");
|
|
||||||
tdRight.appendChild(input);
|
|
||||||
setting.appendChild(tdLeft);
|
|
||||||
setting.appendChild(tdRight);
|
|
||||||
break;
|
|
||||||
// Select
|
|
||||||
case "xepg.missing.epg.mode":
|
|
||||||
var tdLeft = document.createElement("TD");
|
|
||||||
tdLeft.innerHTML = "{{.settings.xepgMissingEPGMode.title}}" + ":";
|
|
||||||
var tdRight = document.createElement("TD");
|
|
||||||
var text = ["{{.settings.xepgMissingEPGMode.info_strict}}", "{{.settings.xepgMissingEPGMode.info_relaxed}}"];
|
|
||||||
var values = ["strict", "relaxed"];
|
|
||||||
var select = content.createSelect(text, values, data, settingsKey);
|
|
||||||
select.setAttribute("onchange", "javascript: this.className = 'changed'");
|
|
||||||
tdRight.appendChild(select);
|
|
||||||
setting.appendChild(tdLeft);
|
|
||||||
setting.appendChild(tdRight);
|
|
||||||
break;
|
|
||||||
case "tuner":
|
|
||||||
var tdLeft = document.createElement("TD");
|
|
||||||
tdLeft.innerHTML = "{{.settings.tuner.title}}" + ":";
|
|
||||||
var tdRight = document.createElement("TD");
|
|
||||||
var text = new Array();
|
|
||||||
var values = new Array();
|
|
||||||
for (var i = 1; i <= 100; i++) {
|
|
||||||
text.push(i);
|
|
||||||
values.push(i);
|
|
||||||
}
|
|
||||||
var select = content.createSelect(text, values, data, settingsKey);
|
|
||||||
select.setAttribute("onchange", "javascript: this.className = 'changed'");
|
|
||||||
tdRight.appendChild(select);
|
|
||||||
setting.appendChild(tdLeft);
|
|
||||||
setting.appendChild(tdRight);
|
|
||||||
break;
|
|
||||||
case "epgSource":
|
|
||||||
var tdLeft = document.createElement("TD");
|
|
||||||
tdLeft.innerHTML = "{{.settings.epgSource.title}}" + ":";
|
|
||||||
var tdRight = document.createElement("TD");
|
|
||||||
var text = ["PMS", "XEPG"];
|
|
||||||
var values = ["PMS", "XEPG"];
|
|
||||||
var select = content.createSelect(text, values, data, settingsKey);
|
|
||||||
select.setAttribute("onchange", "javascript: this.className = 'changed'");
|
|
||||||
tdRight.appendChild(select);
|
|
||||||
setting.appendChild(tdLeft);
|
|
||||||
setting.appendChild(tdRight);
|
|
||||||
break;
|
|
||||||
case "backup.keep":
|
|
||||||
var tdLeft = document.createElement("TD");
|
|
||||||
tdLeft.innerHTML = "{{.settings.backupKeep.title}}" + ":";
|
|
||||||
var tdRight = document.createElement("TD");
|
|
||||||
var text = ["5", "10", "20", "30", "40", "50"];
|
|
||||||
var values = ["5", "10", "20", "30", "40", "50"];
|
|
||||||
var select = content.createSelect(text, values, data, settingsKey);
|
|
||||||
select.setAttribute("onchange", "javascript: this.className = 'changed'");
|
|
||||||
tdRight.appendChild(select);
|
|
||||||
setting.appendChild(tdLeft);
|
|
||||||
setting.appendChild(tdRight);
|
|
||||||
break;
|
|
||||||
case "buffer.size.kb":
|
|
||||||
var tdLeft = document.createElement("TD");
|
|
||||||
tdLeft.innerHTML = "{{.settings.bufferSize.title}}" + ":";
|
|
||||||
var tdRight = document.createElement("TD");
|
|
||||||
var text = ["0.5 MB", "1 MB", "2 MB", "3 MB", "4 MB", "5 MB", "6 MB", "7 MB", "8 MB"];
|
|
||||||
var values = ["512", "1024", "2048", "3072", "4096", "5120", "6144", "7168", "8192"];
|
|
||||||
var select = content.createSelect(text, values, data, settingsKey);
|
|
||||||
select.setAttribute("onchange", "javascript: this.className = 'changed'");
|
|
||||||
tdRight.appendChild(select);
|
|
||||||
setting.appendChild(tdLeft);
|
|
||||||
setting.appendChild(tdRight);
|
|
||||||
break;
|
|
||||||
case "buffer":
|
|
||||||
var tdLeft = document.createElement("TD");
|
|
||||||
tdLeft.innerHTML = "{{.settings.streamBuffering.title}}" + ":";
|
|
||||||
var tdRight = document.createElement("TD");
|
|
||||||
var text = ["{{.settings.streamBuffering.info_false}}", "xTeVe: ({{.settings.streamBuffering.info_xteve}})", "FFmpeg: ({{.settings.streamBuffering.info_ffmpeg}})", "VLC: ({{.settings.streamBuffering.info_vlc}})"];
|
|
||||||
var values = ["-", "xteve", "ffmpeg", "vlc"];
|
|
||||||
var select = content.createSelect(text, values, data, settingsKey);
|
|
||||||
select.setAttribute("onchange", "javascript: this.className = 'changed'");
|
|
||||||
tdRight.appendChild(select);
|
|
||||||
setting.appendChild(tdLeft);
|
|
||||||
setting.appendChild(tdRight);
|
|
||||||
break;
|
|
||||||
case "udpxy":
|
|
||||||
var tdLeft = document.createElement("TD");
|
|
||||||
tdLeft.innerHTML = "{{.settings.udpxy.title}}" + ":";
|
|
||||||
var tdRight = document.createElement("TD");
|
|
||||||
var input = content.createInput("text", "udpxy", data);
|
|
||||||
input.setAttribute("placeholder", "{{.settings.udpxy.placeholder}}");
|
|
||||||
input.setAttribute("onchange", "javascript: this.className = 'changed'");
|
|
||||||
tdRight.appendChild(input);
|
|
||||||
setting.appendChild(tdLeft);
|
|
||||||
setting.appendChild(tdRight);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return setting;
|
|
||||||
};
|
|
||||||
SettingsCategory.prototype.createDescription = function (settingsKey) {
|
|
||||||
var description = document.createElement("TR");
|
|
||||||
var text;
|
|
||||||
switch (settingsKey) {
|
|
||||||
case "authentication.web":
|
|
||||||
text = "{{.settings.authenticationWEB.description}}";
|
|
||||||
break;
|
|
||||||
case "authentication.m3u":
|
|
||||||
text = "{{.settings.authenticationM3U.description}}";
|
|
||||||
break;
|
|
||||||
case "authentication.pms":
|
|
||||||
text = "{{.settings.authenticationPMS.description}}";
|
|
||||||
break;
|
|
||||||
case "authentication.xml":
|
|
||||||
text = "{{.settings.authenticationXML.description}}";
|
|
||||||
break;
|
|
||||||
case "authentication.api":
|
|
||||||
if (SERVER["settings"]["authentication.web"] == true) {
|
|
||||||
text = "{{.settings.authenticationAPI.description}}";
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "backup.keep":
|
|
||||||
text = "{{.settings.backupKeep.description}}";
|
|
||||||
break;
|
|
||||||
case "backup.path":
|
|
||||||
text = "{{.settings.backupPath.description}}";
|
|
||||||
break;
|
|
||||||
case "temp.path":
|
|
||||||
text = "{{.settings.tempPath.description}}";
|
|
||||||
break;
|
|
||||||
case "buffer":
|
|
||||||
text = "{{.settings.streamBuffering.description}}";
|
|
||||||
break;
|
|
||||||
case "buffer.size.kb":
|
|
||||||
text = "{{.settings.bufferSize.description}}";
|
|
||||||
break;
|
|
||||||
case "buffer.timeout":
|
|
||||||
text = "{{.settings.bufferTimeout.description}}";
|
|
||||||
break;
|
|
||||||
case "user.agent":
|
|
||||||
text = "{{.settings.userAgent.description}}";
|
|
||||||
break;
|
|
||||||
case "plex.url":
|
|
||||||
text = "{{.settings.plexURL.description}}";
|
|
||||||
break;
|
|
||||||
case "plex.token":
|
|
||||||
text = "{{.settings.plexToken.description}}";
|
|
||||||
break;
|
|
||||||
case "ffmpeg.path":
|
|
||||||
text = "{{.settings.ffmpegPath.description}}";
|
|
||||||
break;
|
|
||||||
case "ffmpeg.options":
|
|
||||||
text = "{{.settings.ffmpegOptions.description}}";
|
|
||||||
break;
|
|
||||||
case "vlc.path":
|
|
||||||
text = "{{.settings.vlcPath.description}}";
|
|
||||||
break;
|
|
||||||
case "vlc.options":
|
|
||||||
text = "{{.settings.vlcOptions.description}}";
|
|
||||||
break;
|
|
||||||
case "epgSource":
|
|
||||||
text = "{{.settings.epgSource.description}}";
|
|
||||||
break;
|
|
||||||
case "tuner":
|
|
||||||
text = "{{.settings.tuner.description}}";
|
|
||||||
break;
|
|
||||||
case "update":
|
|
||||||
text = "{{.settings.update.description}}";
|
|
||||||
break;
|
|
||||||
case "api":
|
|
||||||
text = "{{.settings.api.description}}";
|
|
||||||
break;
|
|
||||||
case "use_plexAPI":
|
|
||||||
text = "{{.settings.usePlexAPI.description}}";
|
|
||||||
break;
|
|
||||||
case "files.update":
|
|
||||||
text = "{{.settings.filesUpdate.description}}";
|
|
||||||
break;
|
|
||||||
case "cache.images":
|
|
||||||
text = "{{.settings.cacheImages.description}}";
|
|
||||||
break;
|
|
||||||
case "xepg.replace.missing.images":
|
|
||||||
text = "{{.settings.replaceEmptyImages.description}}";
|
|
||||||
break;
|
|
||||||
case "xepg.missing.epg.mode":
|
|
||||||
text = "{{.settings.xepgMissingEPGMode.description}}";
|
|
||||||
break;
|
|
||||||
case "udpxy":
|
|
||||||
text = "{{.settings.udpxy.description}}";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
text = "";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
var tdLeft = document.createElement("TD");
|
|
||||||
tdLeft.innerHTML = "";
|
|
||||||
var tdRight = document.createElement("TD");
|
|
||||||
var pre = document.createElement("PRE");
|
|
||||||
pre.innerHTML = text;
|
|
||||||
tdRight.appendChild(pre);
|
|
||||||
description.appendChild(tdLeft);
|
|
||||||
description.appendChild(tdRight);
|
|
||||||
return description;
|
|
||||||
};
|
|
||||||
return SettingsCategory;
|
|
||||||
}());
|
|
||||||
var SettingsCategoryItem = /** @class */ (function (_super) {
|
|
||||||
__extends(SettingsCategoryItem, _super);
|
|
||||||
function SettingsCategoryItem(headline, settingsKeys) {
|
|
||||||
var _this = _super.call(this) || this;
|
|
||||||
_this.headline = headline;
|
|
||||||
_this.settingsKeys = settingsKeys;
|
|
||||||
return _this;
|
|
||||||
}
|
|
||||||
SettingsCategoryItem.prototype.createCategory = function () {
|
|
||||||
var _this = this;
|
|
||||||
var headline = this.createCategoryHeadline(this.headline);
|
|
||||||
var settingsKeys = this.settingsKeys;
|
|
||||||
var doc = document.getElementById(this.DocumentID);
|
|
||||||
doc.appendChild(headline);
|
|
||||||
// Tabelle für die Kategorie erstellen
|
|
||||||
var table = document.createElement("TABLE");
|
|
||||||
var keys = settingsKeys.split(",");
|
|
||||||
keys.forEach(function (settingsKey) {
|
|
||||||
switch (settingsKey) {
|
|
||||||
case "authentication.pms":
|
|
||||||
case "authentication.m3u":
|
|
||||||
case "authentication.xml":
|
|
||||||
case "authentication.api":
|
|
||||||
if (SERVER["settings"]["authentication.web"] == false) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
var item = _this.createSettings(settingsKey);
|
|
||||||
var description = _this.createDescription(settingsKey);
|
|
||||||
table.appendChild(item);
|
|
||||||
table.appendChild(description);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
doc.appendChild(table);
|
|
||||||
doc.appendChild(this.createHR());
|
|
||||||
};
|
|
||||||
return SettingsCategoryItem;
|
|
||||||
}(SettingsCategory));
|
|
||||||
function showSettings() {
|
|
||||||
console.log("SETTINGS");
|
|
||||||
for (var i = 0; i < settingsCategory.length; i++) {
|
|
||||||
settingsCategory[i].createCategory();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function saveSettings() {
|
|
||||||
console.log("Save Settings");
|
|
||||||
var cmd = "saveSettings";
|
|
||||||
var div = document.getElementById("content_settings");
|
|
||||||
var settings = div.getElementsByClassName("changed");
|
|
||||||
var newSettings = new Object();
|
|
||||||
for (var i = 0; i < settings.length; i++) {
|
|
||||||
var name;
|
|
||||||
var value;
|
|
||||||
switch (settings[i].tagName) {
|
|
||||||
case "INPUT":
|
|
||||||
switch (settings[i].type) {
|
|
||||||
case "checkbox":
|
|
||||||
name = settings[i].name;
|
|
||||||
value = settings[i].checked;
|
|
||||||
newSettings[name] = value;
|
|
||||||
break;
|
|
||||||
case "text":
|
|
||||||
name = settings[i].name;
|
|
||||||
value = settings[i].value;
|
|
||||||
switch (name) {
|
|
||||||
case "update":
|
|
||||||
value = value.split(",");
|
|
||||||
value = value.filter(function (e) { return e; });
|
|
||||||
break;
|
|
||||||
case "buffer.timeout":
|
|
||||||
value = parseFloat(value);
|
|
||||||
}
|
|
||||||
newSettings[name] = value;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "SELECT":
|
|
||||||
name = settings[i].name;
|
|
||||||
value = settings[i].value;
|
|
||||||
// Wenn der Wert eine Zahl ist, wird dieser als Zahl gespeichert
|
|
||||||
if (isNaN(value)) {
|
|
||||||
newSettings[name] = value;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
newSettings[name] = parseInt(value);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var data = new Object();
|
|
||||||
data["settings"] = newSettings;
|
|
||||||
var server = new Server(cmd);
|
|
||||||
server.request(data);
|
|
||||||
}
|
|
||||||
@@ -1,341 +0,0 @@
|
|||||||
function openUsers(elm) {
|
|
||||||
colomnSort = 0;
|
|
||||||
|
|
||||||
var newDiv = document.getElementById("settings");
|
|
||||||
|
|
||||||
var newEntry = new Object();
|
|
||||||
newEntry["_element"] = "HR";
|
|
||||||
newDiv.appendChild(createElement(newEntry));
|
|
||||||
|
|
||||||
var newEntry = new Object();
|
|
||||||
newEntry["_element"] = "INPUT";
|
|
||||||
newEntry["type"] = "button";
|
|
||||||
newEntry["class"] = "button";
|
|
||||||
newEntry["value"] = "New";
|
|
||||||
newEntry["onclick"] = "userDetail(0)";
|
|
||||||
newDiv.appendChild(createElement(newEntry));
|
|
||||||
|
|
||||||
var div = document.getElementById("settings");
|
|
||||||
|
|
||||||
// Build table
|
|
||||||
var newTable = new Object();
|
|
||||||
newTable["_element"] = "TABLE";
|
|
||||||
newTable["id"] = "id_mapping";
|
|
||||||
newTable["class"] = "table-mapping";
|
|
||||||
div.appendChild(createElement(newTable));
|
|
||||||
|
|
||||||
setTimeout(function(){
|
|
||||||
createUsersTable();
|
|
||||||
}, 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
function createUsersTable() {
|
|
||||||
var table = document.getElementById("id_mapping");
|
|
||||||
table.innerHTML = "";
|
|
||||||
var newTR = new Object();
|
|
||||||
newTR["_element"] = "TR";
|
|
||||||
newTR["class"] = "table-mapping-header";
|
|
||||||
table.appendChild(createElement(newTR));
|
|
||||||
|
|
||||||
var tr = table.lastChild;
|
|
||||||
var trHeadlines = new Array("Username", "Password", "WEB", "PMS", "M3U", "XML", "API")
|
|
||||||
|
|
||||||
for (var i = 0; i < trHeadlines.length; i++) {
|
|
||||||
var newTD = new Object();
|
|
||||||
newTD["_element"] = "TD";
|
|
||||||
newTD["_text"] = trHeadlines[i];
|
|
||||||
tr.appendChild(createElement(newTD));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Sort users
|
|
||||||
var userIds = getObjKeys(users);
|
|
||||||
|
|
||||||
var userObj = new Object();
|
|
||||||
|
|
||||||
for (var i = 0; i < userIds.length; i++) {
|
|
||||||
var username = users[userIds[i]]["data"]["username"];
|
|
||||||
userObj[username] = userIds[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
var allUsers = getObjKeys(userObj);
|
|
||||||
allUsers.sort();
|
|
||||||
// --
|
|
||||||
|
|
||||||
for (var i = 0; i < allUsers.length; i++) {
|
|
||||||
var table = document.getElementById("id_mapping");
|
|
||||||
var userID = userObj[allUsers[i]];
|
|
||||||
var username = allUsers[i];
|
|
||||||
var item = users[userID]["data"];
|
|
||||||
|
|
||||||
// Create TR
|
|
||||||
var newTR = new Object();
|
|
||||||
newTR["_element"] = "TR";
|
|
||||||
newTR["class"] = "";
|
|
||||||
newTR["id"] = userID;
|
|
||||||
newTR["onclick"] = 'javascript: userDetail("' + userID + '");';
|
|
||||||
table.appendChild(createElement(newTR));
|
|
||||||
|
|
||||||
var tr = table.lastChild;
|
|
||||||
|
|
||||||
// Create username TD
|
|
||||||
var newTD = new Object();
|
|
||||||
newTD["_element"] = "P";
|
|
||||||
newTD["_text"] = username;
|
|
||||||
createNewTD(newTD, tr);
|
|
||||||
|
|
||||||
// Create password TD
|
|
||||||
var newTD = new Object();
|
|
||||||
newTD["_element"] = "P";
|
|
||||||
newTD["_text"] = ".....";
|
|
||||||
createNewTD(newTD, tr);
|
|
||||||
|
|
||||||
// Create web access
|
|
||||||
var newTD = new Object();
|
|
||||||
newTD["_element"] = "P";
|
|
||||||
switch(item["authentication.web"]){
|
|
||||||
case true: newTD["_text"] = "✓"; break;
|
|
||||||
default: newTD["_text"] = "-"; break;
|
|
||||||
}
|
|
||||||
createNewTD(newTD, tr);
|
|
||||||
|
|
||||||
// Create PMS access
|
|
||||||
var newTD = new Object();
|
|
||||||
newTD["_element"] = "P";
|
|
||||||
switch(item["authentication.pms"]){
|
|
||||||
case true: newTD["_text"] = "✓"; break;
|
|
||||||
default: newTD["_text"] = "-"; break;
|
|
||||||
}
|
|
||||||
createNewTD(newTD, tr);
|
|
||||||
|
|
||||||
// Create M3U access
|
|
||||||
var newTD = new Object();
|
|
||||||
newTD["_element"] = "P";
|
|
||||||
switch(item["authentication.m3u"]){
|
|
||||||
case true: newTD["_text"] = "✓"; break;
|
|
||||||
default: newTD["_text"] = "-"; break;
|
|
||||||
}
|
|
||||||
createNewTD(newTD, tr);
|
|
||||||
|
|
||||||
// Create XMLTV access
|
|
||||||
var newTD = new Object();
|
|
||||||
newTD["_element"] = "P";
|
|
||||||
switch(item["authentication.xml"]){
|
|
||||||
case true: newTD["_text"] = "✓"; break;
|
|
||||||
default: newTD["_text"] = "-"; break;
|
|
||||||
}
|
|
||||||
createNewTD(newTD, tr);
|
|
||||||
|
|
||||||
// Create API access
|
|
||||||
var newTD = new Object();
|
|
||||||
newTD["_element"] = "P";
|
|
||||||
|
|
||||||
switch(item["authentication.api"]){
|
|
||||||
case true: newTD["_text"] = "✓"; break;
|
|
||||||
default: newTD["_text"] = "-"; break;
|
|
||||||
}
|
|
||||||
createNewTD(newTD, tr);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// usage Info
|
|
||||||
var div = document.getElementById("settings");
|
|
||||||
switch(menu[activeMenu.id].hasOwnProperty("_usage")) {
|
|
||||||
case true:
|
|
||||||
var usageItem = new Object();
|
|
||||||
usageItem["_element"] = "PRE"
|
|
||||||
usageItem["_text"] = menu[activeMenu.id]["_usage"];
|
|
||||||
|
|
||||||
var newHR = new Object();
|
|
||||||
newHR["_element"] = "HR"
|
|
||||||
div.appendChild(createElement(newHR));
|
|
||||||
div.appendChild(createElement(usageItem));
|
|
||||||
}
|
|
||||||
|
|
||||||
sortTable(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
function userDetail(userID) {
|
|
||||||
showPopUpElement('user-detail');
|
|
||||||
setTimeout(function(){
|
|
||||||
showElement("popup", true);
|
|
||||||
}, 10);
|
|
||||||
var defaultUser;
|
|
||||||
|
|
||||||
document.getElementById("saveUserDetail").setAttribute("onclick", 'javascript: saveUserDetail("' + userID + '", false)');
|
|
||||||
document.getElementById("deleteUserDetail").setAttribute("onclick", 'javascript: saveUserDetail("' + userID + '", true)');
|
|
||||||
|
|
||||||
var data = new Object();
|
|
||||||
|
|
||||||
switch(userID) {
|
|
||||||
case 0: // New User
|
|
||||||
data["username"] = "";
|
|
||||||
data["authentication.web"] = false;
|
|
||||||
data["authentication.pms"] = true;
|
|
||||||
data["authentication.xml"] = true;
|
|
||||||
data["authentication.m3u"] = false;
|
|
||||||
data["authentication.api"] = false;
|
|
||||||
data["defaultUser"] = false;
|
|
||||||
setTimeout(function(){
|
|
||||||
showElement("deleteUserDetail", false)
|
|
||||||
}, 1);
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
data = users[userID]["data"];
|
|
||||||
showElement("deleteUserDetail", true)
|
|
||||||
document.getElementById("deleteUserDetail").className = "delete";
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
var username = data["username"];
|
|
||||||
data["password"] = "";
|
|
||||||
data["confirm"] = "";
|
|
||||||
|
|
||||||
var keys = getObjKeys(data);
|
|
||||||
defaultUser = data["defaultUser"];
|
|
||||||
if (data.hasOwnProperty("defaultUser")) {
|
|
||||||
defaultUser = JSON.parse(data["defaultUser"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var i = 0; i < keys.length; i++) {
|
|
||||||
|
|
||||||
if(document.getElementById(keys[i])){
|
|
||||||
var td = document.getElementById(keys[i])
|
|
||||||
} else {
|
|
||||||
var td = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
var newItem = new Object();
|
|
||||||
|
|
||||||
newItem["_element"] = "INPUT";
|
|
||||||
|
|
||||||
newItem["value"] = data[keys[i]];
|
|
||||||
newItem["name"] = keys[i];
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
switch(keys[i].indexOf("authentication")) {
|
|
||||||
case -1:
|
|
||||||
if (keys[i] == "password" || keys[i] == "confirm") {
|
|
||||||
newItem["type"] = "password";
|
|
||||||
} else {
|
|
||||||
newItem["type"] = "text";
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
newItem["type"] = "checkbox";
|
|
||||||
|
|
||||||
if (keys[i] == "authentication.web" && defaultUser == true) {
|
|
||||||
newItem["onclick"] = "return false";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data[keys[i]] == true) {
|
|
||||||
newItem["checked"] = data[keys[i]];
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(keys[i]) {
|
|
||||||
case "defaultUser":
|
|
||||||
//if (data[keys[i]] == true) {
|
|
||||||
newItem["type"] = "hidden";
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (td != undefined) {
|
|
||||||
td.innerHTML = "";
|
|
||||||
var element = createNewElement(newItem)
|
|
||||||
//console.log(element);
|
|
||||||
td.appendChild(element);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (defaultUser == true) {
|
|
||||||
showElement("deleteUserDetail", false)
|
|
||||||
} else {
|
|
||||||
showElement("deleteUserDetail", true)
|
|
||||||
document.getElementById("deleteUserDetail").className = "delete";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function saveUserDetail(userID, deleteUser) {
|
|
||||||
|
|
||||||
var inputs = document.getElementById("user-detail-table").getElementsByTagName("INPUT");
|
|
||||||
|
|
||||||
var newUserData = new Object();
|
|
||||||
for (var i = 0; i < inputs.length; i++) {
|
|
||||||
switch(inputs[i].type) {
|
|
||||||
case "checkbox": newUserData[inputs[i].name] = inputs[i].checked; break;
|
|
||||||
default: newUserData[inputs[i].name] = inputs[i].value; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inputs["username"].value.length == 0) {
|
|
||||||
inputs["username"].style.border = "solid 1px red";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(userID) {
|
|
||||||
case "0":
|
|
||||||
if (inputs["password"].value.length == 0) {
|
|
||||||
console.log(inputs["password"].value.length);
|
|
||||||
inputs["password"].style.border = "solid 1px red";
|
|
||||||
return
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inputs["password"].value.length > 0) {
|
|
||||||
if (inputs["password"].value != inputs["confirm"].value) {
|
|
||||||
inputs["password"].style.border = "solid 1px red";
|
|
||||||
inputs["confirm"].style.border = "solid 1px red";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
var data = new Object();
|
|
||||||
|
|
||||||
switch(userID) {
|
|
||||||
case "0":
|
|
||||||
//data = newUserData
|
|
||||||
data["userData"] = newUserData
|
|
||||||
data["cmd"] = "saveNewUser"; break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
var thisUser = new Object();
|
|
||||||
|
|
||||||
if (deleteUser == true) {
|
|
||||||
if (confirm('Delete the selected user?')) {
|
|
||||||
data["deleteUser"] = true;
|
|
||||||
} else {
|
|
||||||
showElement("popup", false);
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
thisUser[userID] = newUserData;
|
|
||||||
|
|
||||||
data["userData"] = thisUser;
|
|
||||||
data["cmd"] = "saveUserData"; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
xTeVe(data);
|
|
||||||
//createUsersTable()
|
|
||||||
showElement("popup", false);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,569 +0,0 @@
|
|||||||
{
|
|
||||||
"mainMenu":
|
|
||||||
{
|
|
||||||
"item":
|
|
||||||
{
|
|
||||||
"playlist": "Playlist",
|
|
||||||
"pmsID": "PMS ID",
|
|
||||||
"filter": "Filter",
|
|
||||||
"xmltv": "XMLTV",
|
|
||||||
"mapping": "Mapping",
|
|
||||||
"users": "Users",
|
|
||||||
"settings": "Settings",
|
|
||||||
"log": "Log",
|
|
||||||
"logout": "Logout"
|
|
||||||
},
|
|
||||||
"headline":
|
|
||||||
{
|
|
||||||
"playlist": "Local or remote playlists",
|
|
||||||
"filter": "Filter playlist",
|
|
||||||
"xmltv": "Local or remote XMLTV files",
|
|
||||||
"mapping": "Map playlist channels to EPG channels",
|
|
||||||
"users": "User management",
|
|
||||||
"settings": "Settings",
|
|
||||||
"log": "Log",
|
|
||||||
"logout": "Logout"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"confirm":
|
|
||||||
{
|
|
||||||
"restore": "All data will be replaced with those from the backup. Should the files be restored?"
|
|
||||||
},
|
|
||||||
"alert":
|
|
||||||
{
|
|
||||||
"fileLoadingError": "File couldn't be loaded",
|
|
||||||
"invalidChannelNumber": "Invalid channel number",
|
|
||||||
"missingInput": "Missing input"
|
|
||||||
},
|
|
||||||
"button":
|
|
||||||
{
|
|
||||||
"back": "Back",
|
|
||||||
"backup": "Backup",
|
|
||||||
"bulkEdit": "Bulk Edit",
|
|
||||||
"cancel": "Cancel",
|
|
||||||
"edit": "Edit",
|
|
||||||
"delete": "Delete",
|
|
||||||
"done": "Done",
|
|
||||||
"login": "Login",
|
|
||||||
"new": "New",
|
|
||||||
"next": "Next",
|
|
||||||
"restore": "Restore",
|
|
||||||
"save": "Save",
|
|
||||||
"search": "Search",
|
|
||||||
"update": "Update",
|
|
||||||
"craeteAccount": "Create Account",
|
|
||||||
"resetLogs": "Reset Logs",
|
|
||||||
"uploadLogo": "Upload Logo"
|
|
||||||
},
|
|
||||||
"filter":
|
|
||||||
{
|
|
||||||
"table":
|
|
||||||
{
|
|
||||||
"name": "Filter Name",
|
|
||||||
"type": "Filter Type",
|
|
||||||
"filter": "Filter"
|
|
||||||
},
|
|
||||||
"custom": "Custom",
|
|
||||||
"group": "Group",
|
|
||||||
"name":
|
|
||||||
{
|
|
||||||
"title": "Filter Name",
|
|
||||||
"placeholder": "Filter name",
|
|
||||||
"description": ""
|
|
||||||
},
|
|
||||||
"description":
|
|
||||||
{
|
|
||||||
"title": "Description",
|
|
||||||
"placeholder": "Description",
|
|
||||||
"description": ""
|
|
||||||
},
|
|
||||||
"type":
|
|
||||||
{
|
|
||||||
"title": "Type",
|
|
||||||
"groupTitle": "Group Title",
|
|
||||||
"customFilter": "Custom Filter"
|
|
||||||
},
|
|
||||||
"caseSensitive":
|
|
||||||
{
|
|
||||||
"title": "Case Sensitive",
|
|
||||||
"placeholder": "",
|
|
||||||
"description": ""
|
|
||||||
},
|
|
||||||
"filterRule":
|
|
||||||
{
|
|
||||||
"title": "Filter Rule",
|
|
||||||
"placeholder": "Sport {HD} !{ES,IT}",
|
|
||||||
"description": ""
|
|
||||||
},
|
|
||||||
"filterGroup":
|
|
||||||
{
|
|
||||||
"title": "Group Title",
|
|
||||||
"placeholder": "",
|
|
||||||
"description": "Select a M3U group. (Counter)<br>Changing the group title in the M3U invalidates the filter."
|
|
||||||
},
|
|
||||||
"include":
|
|
||||||
{
|
|
||||||
"title": "Include",
|
|
||||||
"placeholder": "FHD,UHD",
|
|
||||||
"description": "Channel name must include.<br>(Comma separated) Comma means or"
|
|
||||||
},
|
|
||||||
"exclude":
|
|
||||||
{
|
|
||||||
"title": "Exclude",
|
|
||||||
"placeholder": "ES,IT",
|
|
||||||
"description": "Channel name must not contain.<br>(Comma separated) Comma means or"
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
|
||||||
"playlist":
|
|
||||||
{
|
|
||||||
"table":
|
|
||||||
{
|
|
||||||
"playlist": "Playlist",
|
|
||||||
"tuner": "Tuner",
|
|
||||||
"lastUpdate": "Last Update",
|
|
||||||
"availability": "Availability",
|
|
||||||
"type": "Type",
|
|
||||||
"streams": "Streams",
|
|
||||||
"groupTitle": "group-title",
|
|
||||||
"tvgID": "tvg-id",
|
|
||||||
"uniqueID": "Unique ID"
|
|
||||||
},
|
|
||||||
"playlistType":
|
|
||||||
{
|
|
||||||
"title": "Playlist type",
|
|
||||||
"placeholder": "",
|
|
||||||
"description": ""
|
|
||||||
},
|
|
||||||
"type":
|
|
||||||
{
|
|
||||||
"title": "Type",
|
|
||||||
"placeholder": "",
|
|
||||||
"description": ""
|
|
||||||
},
|
|
||||||
"name":
|
|
||||||
{
|
|
||||||
"title": "Name",
|
|
||||||
"placeholder": "Playlist name",
|
|
||||||
"description": ""
|
|
||||||
},
|
|
||||||
"description":
|
|
||||||
{
|
|
||||||
"title": "Description",
|
|
||||||
"placeholder": "Description",
|
|
||||||
"description": ""
|
|
||||||
},
|
|
||||||
"fileM3U":
|
|
||||||
{
|
|
||||||
"title": "M3U File",
|
|
||||||
"placeholder": "File path or URL of the M3U",
|
|
||||||
"description": ""
|
|
||||||
},
|
|
||||||
"fileHDHR":
|
|
||||||
{
|
|
||||||
"title": "HDHomeRun IP",
|
|
||||||
"placeholder": "IP address and port (192.168.1.10:5004)",
|
|
||||||
"description": ""
|
|
||||||
},
|
|
||||||
"tuner":
|
|
||||||
{
|
|
||||||
"title": "Tuner / Streams",
|
|
||||||
"placeholder": "",
|
|
||||||
"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."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"xmltv":
|
|
||||||
{
|
|
||||||
"table":
|
|
||||||
{
|
|
||||||
"guide": "Guide",
|
|
||||||
"lastUpdate": "Last Update",
|
|
||||||
"availability": "Availability",
|
|
||||||
"channels": "Channels",
|
|
||||||
"programs": "Programs"
|
|
||||||
},
|
|
||||||
"name":
|
|
||||||
{
|
|
||||||
"title": "Name",
|
|
||||||
"placeholder": "Guide name",
|
|
||||||
"description": ""
|
|
||||||
},
|
|
||||||
"description":
|
|
||||||
{
|
|
||||||
"title": "Description",
|
|
||||||
"placeholder": "Description",
|
|
||||||
"description": ""
|
|
||||||
},
|
|
||||||
"fileXMLTV":
|
|
||||||
{
|
|
||||||
"title": "XMLTV File",
|
|
||||||
"placeholder": "File path or URL of the XMLTV",
|
|
||||||
"description": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"mapping":
|
|
||||||
{
|
|
||||||
"table":
|
|
||||||
{
|
|
||||||
"chNo": "Ch. No.",
|
|
||||||
"logo": "Logo",
|
|
||||||
"channelName": "Channel Name",
|
|
||||||
"playlist": "Playlist",
|
|
||||||
"groupTitle": "Group Title",
|
|
||||||
"xmltvFile": "XMLTV File",
|
|
||||||
"xmltvID": "XMLTV ID",
|
|
||||||
"edit": "Edit"
|
|
||||||
},
|
|
||||||
"active":
|
|
||||||
{
|
|
||||||
"title": "Active",
|
|
||||||
"placeholder": "",
|
|
||||||
"description": ""
|
|
||||||
},
|
|
||||||
"channelName":
|
|
||||||
{
|
|
||||||
"title": "Channel Name",
|
|
||||||
"placeholder": "",
|
|
||||||
"description": ""
|
|
||||||
},
|
|
||||||
"description":
|
|
||||||
{
|
|
||||||
"title": "Channel Description",
|
|
||||||
"placeholder": "Used by the Dummy as an XML description",
|
|
||||||
"description": ""
|
|
||||||
},
|
|
||||||
"updateChannelName":
|
|
||||||
{
|
|
||||||
"title": "Update Channel Name",
|
|
||||||
"placeholder": "",
|
|
||||||
"description": ""
|
|
||||||
},
|
|
||||||
"channelLogo":
|
|
||||||
{
|
|
||||||
"title": "Logo URL",
|
|
||||||
"placeholder": "",
|
|
||||||
"description": ""
|
|
||||||
},
|
|
||||||
"updateChannelLogo":
|
|
||||||
{
|
|
||||||
"title": "Update Channel Logo",
|
|
||||||
"placeholder": "",
|
|
||||||
"description": ""
|
|
||||||
},
|
|
||||||
"epgCategory":
|
|
||||||
{
|
|
||||||
"title": "EPG Category",
|
|
||||||
"placeholder": "",
|
|
||||||
"description": ""
|
|
||||||
},
|
|
||||||
"m3uGroupTitle":
|
|
||||||
{
|
|
||||||
"title": "Group Title (xteve.m3u)",
|
|
||||||
"placeholder": "",
|
|
||||||
"description": ""
|
|
||||||
},
|
|
||||||
"xmltvFile":
|
|
||||||
{
|
|
||||||
"title": "XMLTV File",
|
|
||||||
"placeholder": "",
|
|
||||||
"description": ""
|
|
||||||
},
|
|
||||||
"xmltvChannel":
|
|
||||||
{
|
|
||||||
"title": "XMLTV Channel",
|
|
||||||
"placeholder": "",
|
|
||||||
"description": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"users":
|
|
||||||
{
|
|
||||||
"table":
|
|
||||||
{
|
|
||||||
"username": "Username",
|
|
||||||
"password": "Password",
|
|
||||||
"web": "WEB",
|
|
||||||
"pms": "PMS",
|
|
||||||
"m3u": "M3U",
|
|
||||||
"xml": "XML",
|
|
||||||
"api": "API"
|
|
||||||
},
|
|
||||||
"username":
|
|
||||||
{
|
|
||||||
"title": "Username",
|
|
||||||
"placeholder": "Username",
|
|
||||||
"description": ""
|
|
||||||
},
|
|
||||||
"password":
|
|
||||||
{
|
|
||||||
"title": "Password",
|
|
||||||
"placeholder": "Password",
|
|
||||||
"description": ""
|
|
||||||
},
|
|
||||||
"confirm":
|
|
||||||
{
|
|
||||||
"title": "Confirm",
|
|
||||||
"placeholder": "Password confirm",
|
|
||||||
"description": ""
|
|
||||||
},
|
|
||||||
"web":
|
|
||||||
{
|
|
||||||
"title": "Web Access",
|
|
||||||
"placeholder": "",
|
|
||||||
"description": ""
|
|
||||||
},
|
|
||||||
"pms":
|
|
||||||
{
|
|
||||||
"title": "PMS Access",
|
|
||||||
"placeholder": "",
|
|
||||||
"description": ""
|
|
||||||
},
|
|
||||||
"m3u":
|
|
||||||
{
|
|
||||||
"title": "M3U Access",
|
|
||||||
"placeholder": "",
|
|
||||||
"description": ""
|
|
||||||
},
|
|
||||||
"xml":
|
|
||||||
{
|
|
||||||
"title": "XML Access",
|
|
||||||
"placeholder": "",
|
|
||||||
"description": ""
|
|
||||||
},
|
|
||||||
"api":
|
|
||||||
{
|
|
||||||
"title": "API Access",
|
|
||||||
"placeholder": "",
|
|
||||||
"description": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"settings":
|
|
||||||
{
|
|
||||||
"category":
|
|
||||||
{
|
|
||||||
"general": "General",
|
|
||||||
"files": "Files",
|
|
||||||
"streaming": "Streaming",
|
|
||||||
"backup": "Backup",
|
|
||||||
"authentication": "Authentication"
|
|
||||||
},
|
|
||||||
"update":
|
|
||||||
{
|
|
||||||
"title": "Schedule for updating (Playlist, XMLTV, Backup)",
|
|
||||||
"placeholder": "0000,1000,2000",
|
|
||||||
"description": "Time in 24 hour format (0800 = 8:00 am). More times can be entered comma separated. Leave this field empty if no updates are to be carried out."
|
|
||||||
},
|
|
||||||
"api":
|
|
||||||
{
|
|
||||||
"title": "API Interface",
|
|
||||||
"description": "Via API interface it is possible to send commands to xTeVe. API documentation is <a href='https://github.com/xteve-project/xTeVe-Documentation/blob/master/en/configuration.md#api'>here</a>"
|
|
||||||
},
|
|
||||||
"usePlexAPI":
|
|
||||||
{
|
|
||||||
"title": "Use Plex API Refresh",
|
|
||||||
"description": "When enabled, xTeVe calls Plex directly to refresh DVR guide data after lineup or XEPG updates."
|
|
||||||
},
|
|
||||||
"plexURL":
|
|
||||||
{
|
|
||||||
"title": "Plex Server URL",
|
|
||||||
"description": "Base URL of your Plex server. Example: http://192.168.1.10:32400",
|
|
||||||
"placeholder": "http://plex-host:32400"
|
|
||||||
},
|
|
||||||
"plexToken":
|
|
||||||
{
|
|
||||||
"title": "Plex API Token",
|
|
||||||
"description": "Plex token used for authenticated API calls to refresh your DVR guide.",
|
|
||||||
"placeholder": "Plex X-Plex-Token"
|
|
||||||
},
|
|
||||||
"epgSource":
|
|
||||||
{
|
|
||||||
"title": "EPG Source",
|
|
||||||
"description": "PMS:<br>- Use EPG data from Plex or Emby <br><br>XEPG:<br>- Use of one or more XMLTV files<br>- Channel management<br>- M3U / XMLTV export (HTTP link for IPTV apps)"
|
|
||||||
},
|
|
||||||
"tuner":
|
|
||||||
{
|
|
||||||
"title": "Number of Tuners",
|
|
||||||
"description": "Number of parallel connections that can be established to the provider.<br>Available for: Plex, Emby (HDHR), M3U (with active buffer).<br>After a change, xTeVe must be delete in the Plex / Emby DVR settings and set up again."
|
|
||||||
},
|
|
||||||
"filesUpdate":
|
|
||||||
{
|
|
||||||
"title": "Updates all files at startup",
|
|
||||||
"description": "Updates all playlists, tuner and XMLTV files at startup."
|
|
||||||
},
|
|
||||||
"cacheImages":
|
|
||||||
{
|
|
||||||
"title": "Image Caching",
|
|
||||||
"description": "All images from the XMLTV file are cached, allowing faster rendering of the grid in the client.<br>Downloading the images may take a while and will be done in the background."
|
|
||||||
},
|
|
||||||
"replaceEmptyImages":
|
|
||||||
{
|
|
||||||
"title": "Replace missing program images",
|
|
||||||
"description": "If the poster in the XMLTV program is missing, the channel logo will be used."
|
|
||||||
},
|
|
||||||
"xepgMissingEPGMode":
|
|
||||||
{
|
|
||||||
"title": "Missing EPG Handling",
|
|
||||||
"description": "Strict: channels are deactivated when XMLTV mappings disappear.<br>Relaxed: channels stay active with last-known mappings and fall back to a dummy guide whenever guide data cannot be resolved.",
|
|
||||||
"info_strict": "Strict (deactivate channels)",
|
|
||||||
"info_relaxed": "Relaxed (keep active / dummy guide)"
|
|
||||||
},
|
|
||||||
"streamBuffering":
|
|
||||||
{
|
|
||||||
"title": "Stream Buffer",
|
|
||||||
"description": "Functions of the buffer:<br>- The stream is passed from xTeVe, FFmpeg or VLC to Plex, Emby or M3U Player<br>- Small jerking of the streams can be compensated<br>- HLS / M3U8 support<br>- RTP / RTPS support (only FFmpeg or VLC)<br>- Re-streaming<br>- Separate tuner limit for each playlist",
|
|
||||||
"info_false": "No Buffer (Client connects to the streaming server)",
|
|
||||||
"info_xteve": "xTeVe connects to the streaming server",
|
|
||||||
"info_ffmpeg": "FFmpeg connects to the streaming server",
|
|
||||||
"info_vlc": "VLC connects to the streaming server"
|
|
||||||
|
|
||||||
},
|
|
||||||
"udpxy":
|
|
||||||
{
|
|
||||||
"title": "UDPxy address",
|
|
||||||
"description": "The address of your UDPxy server. If set, and the channel URLs in the m3u is multicast, xTeVe will rewrite it so that it is accessed via the UDPxy service.",
|
|
||||||
"placeholder": "host:port"
|
|
||||||
},
|
|
||||||
"ffmpegPath":
|
|
||||||
{
|
|
||||||
"title": "FFmpeg Binary Path",
|
|
||||||
"description": "Path to FFmpeg binary.",
|
|
||||||
"placeholder": "/path/to/ffmpeg"
|
|
||||||
},
|
|
||||||
"ffmpegOptions":
|
|
||||||
{
|
|
||||||
"title": "FFmpeg Options",
|
|
||||||
"description": "FFmpeg options.<br>Only change if you know what you are doing.<br>Leave blank to set default settings.",
|
|
||||||
"placeholder": "Leave blank to set default settings"
|
|
||||||
},
|
|
||||||
"vlcPath":
|
|
||||||
{
|
|
||||||
"title": "VLC / CVLC Binary Path",
|
|
||||||
"description": "Path to VLC / CVLC binary.",
|
|
||||||
"placeholder": "/path/to/cvlc"
|
|
||||||
},
|
|
||||||
"vlcOptions":
|
|
||||||
{
|
|
||||||
"title": "VLC / CVLC Options",
|
|
||||||
"description": "VLC / CVLC options.<br>Only change if you know what you are doing.<br>Leave blank to set default settings.",
|
|
||||||
"placeholder": "Leave blank to set default settings"
|
|
||||||
},
|
|
||||||
"bufferSize":
|
|
||||||
{
|
|
||||||
"title": "Buffer Size",
|
|
||||||
"description": "Buffer size in MB.<br>M3U8: If the TS segment smaller then the buffer size, the file size of the segment is used."
|
|
||||||
},
|
|
||||||
"bufferTimeout":
|
|
||||||
{
|
|
||||||
"title": "Timeout for new client connections",
|
|
||||||
"description": "The xTeVe buffer waits until new client connections are established. Helpful for fast channel switching. Value in milliseconds.",
|
|
||||||
"placeholder": "100"
|
|
||||||
},
|
|
||||||
"userAgent":
|
|
||||||
{
|
|
||||||
"title": "User Agent",
|
|
||||||
"description": "User Agent for HTTP requests. For every HTTP connection, this value is used for the user agent. Should only be changed if xTeVe is blocked.",
|
|
||||||
"placeholder": "xTeVe"
|
|
||||||
},
|
|
||||||
"backupPath":
|
|
||||||
{
|
|
||||||
"title": "Location for automatic backups",
|
|
||||||
"placeholder": "/mnt/data/backup/xteve/",
|
|
||||||
"description": "Before any update of the provider data by the schedule, xTeVe creates a backup. The path for the automatic backups can be changed. xTeVe requires write permission for this folder."
|
|
||||||
},
|
|
||||||
"tempPath":
|
|
||||||
{
|
|
||||||
"title": "Location for the temporary files",
|
|
||||||
"placeholder": "/tmp/xteve/",
|
|
||||||
"description": "Location for the buffer files."
|
|
||||||
},
|
|
||||||
"backupKeep":
|
|
||||||
{
|
|
||||||
"title": "Number of backups to keep",
|
|
||||||
"description": "Number of backups to keep. Older backups are automatically deleted."
|
|
||||||
},
|
|
||||||
"authenticationWEB":
|
|
||||||
{
|
|
||||||
"title": "WEB Authentication",
|
|
||||||
"description": "Access to the web interface only possible with credentials."
|
|
||||||
},
|
|
||||||
"authenticationPMS":
|
|
||||||
{
|
|
||||||
"title": "PMS Authentication",
|
|
||||||
"description": "Plex requests are only possible with authentication. <br><b>Warning!!!</b> After activating this function xTeVe must be delete in the PMS DVR settings and set up again."
|
|
||||||
},
|
|
||||||
"authenticationM3U":
|
|
||||||
{
|
|
||||||
"title": "M3U Authentication",
|
|
||||||
"description": "Downloading the xteve.m3u file via an HTTP request is only possible with authentication."
|
|
||||||
},
|
|
||||||
"authenticationXML":
|
|
||||||
{
|
|
||||||
"title": "XML Authentication",
|
|
||||||
"description": "Downloading the xteve.xml file via an HTTP request is only possible with authentication"
|
|
||||||
},
|
|
||||||
"authenticationAPI":
|
|
||||||
{
|
|
||||||
"title": "API Authentication",
|
|
||||||
"description": "Access to the API interface is only possible with authentication."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"wizard":
|
|
||||||
{
|
|
||||||
"epgSource":
|
|
||||||
{
|
|
||||||
"title": "EPG Source",
|
|
||||||
"description": "PMS:<br>- Use EPG data from Plex or Emby <br><br>XEPG:<br>- Use of one or more XMLTV files<br>- Channel management<br>- M3U / XMLTV export (HTTP link for IPTV apps)"
|
|
||||||
},
|
|
||||||
"tuner":
|
|
||||||
{
|
|
||||||
"title": "Number of tuners",
|
|
||||||
"description": "Number of parallel connections that can be established to the provider.<br>Available for: Plex, Emby (HDHR), M3U (with active buffer).<br>After a change, xTeVe must be delete in the Plex / Emby DVR settings and set up again."
|
|
||||||
},
|
|
||||||
"m3u":
|
|
||||||
{
|
|
||||||
"title": "M3U Playlist",
|
|
||||||
"placeholder": "File path or URL of the M3U",
|
|
||||||
"description": "Local or remote playlists"
|
|
||||||
},
|
|
||||||
"xmltv":
|
|
||||||
{
|
|
||||||
"title": "XMLTV File",
|
|
||||||
"placeholder": "File path or URL of the XMLTV",
|
|
||||||
"description": "Local or remote XMLTV file"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"login":
|
|
||||||
{
|
|
||||||
"failed": "User authentication failed",
|
|
||||||
"headline": "Login",
|
|
||||||
"username":
|
|
||||||
{
|
|
||||||
"title": "Username",
|
|
||||||
"placeholder": "Username"
|
|
||||||
},
|
|
||||||
"password":
|
|
||||||
{
|
|
||||||
"title": "Password",
|
|
||||||
"placeholder": "Password"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"account":
|
|
||||||
{
|
|
||||||
"failed": "Password does not match",
|
|
||||||
"headline": "Create user account",
|
|
||||||
"username":
|
|
||||||
{
|
|
||||||
"title": "Username",
|
|
||||||
"placeholder": "Username"
|
|
||||||
},
|
|
||||||
"password":
|
|
||||||
{
|
|
||||||
"title": "Password",
|
|
||||||
"placeholder": "Password"
|
|
||||||
},
|
|
||||||
"confirm":
|
|
||||||
{
|
|
||||||
"title": "Confirm",
|
|
||||||
"placeholder": "Confirm"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+5
-6
@@ -6,8 +6,7 @@
|
|||||||
<title>xTeVe</title>
|
<title>xTeVe</title>
|
||||||
<link rel="stylesheet" href="css/screen.css" type="text/css">
|
<link rel="stylesheet" href="css/screen.css" type="text/css">
|
||||||
<link rel="stylesheet" href="css/base.css" type="text/css">
|
<link rel="stylesheet" href="css/base.css" type="text/css">
|
||||||
<script language="javascript" type="text/javascript" src="js/network_ts.js"></script>
|
<script src="js/app.js"></script>
|
||||||
<script language="javascript" type="text/javascript" src="js/authentication_ts.js"></script>
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="auth-screen">
|
<body class="auth-screen">
|
||||||
@@ -17,7 +16,7 @@
|
|||||||
<main id="box" role="main" aria-labelledby="head-text">
|
<main id="box" role="main" aria-labelledby="head-text">
|
||||||
|
|
||||||
<div id="headline">
|
<div id="headline">
|
||||||
<h1 id="head-text" class="center">{{.login.headline}}</h1>
|
<h1 id="head-text" class="center">Login</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p id="err" class="errorMsg center" role="alert" aria-live="assertive" aria-atomic="true">{{.authenticationErr}}</p>
|
<p id="err" class="errorMsg center" role="alert" aria-live="assertive" aria-atomic="true">{{.authenticationErr}}</p>
|
||||||
@@ -26,9 +25,9 @@
|
|||||||
|
|
||||||
<form id="authentication" action="" method="post" aria-describedby="err" novalidate>
|
<form id="authentication" action="" method="post" aria-describedby="err" novalidate>
|
||||||
|
|
||||||
<label for="username">{{.login.username.title}}:</label>
|
<label for="username">Username:</label>
|
||||||
<input id="username" type="text" name="username" placeholder="Username" value="" autocomplete="username">
|
<input id="username" type="text" name="username" placeholder="Username" value="" autocomplete="username">
|
||||||
<label for="password">{{.login.password.title}}:</label>
|
<label for="password">Password:</label>
|
||||||
<input id="password" type="password" name="password" placeholder="Password" value="" autocomplete="current-password">
|
<input id="password" type="password" name="password" placeholder="Password" value="" autocomplete="current-password">
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
@@ -36,7 +35,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="box-footer">
|
<div id="box-footer">
|
||||||
<input id="submit" class="" type="button" value="{{.button.login}}" aria-label="{{.button.login}}" onclick="javascript: login();">
|
<input id="submit" class="" type="button" value="Login" aria-label="Login" onclick="javascript: login();">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
Generated
+27
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"name": "xteve-webui",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "xteve-webui",
|
||||||
|
"devDependencies": {
|
||||||
|
"typescript": "5.9.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/typescript": {
|
||||||
|
"version": "5.9.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
||||||
|
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"bin": {
|
||||||
|
"tsc": "bin/tsc",
|
||||||
|
"tsserver": "bin/tsserver"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.17"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"name": "xteve-webui",
|
||||||
|
"private": true,
|
||||||
|
"description": "Build tooling for the xTeVe web interface (TypeScript -> html/js/app.js)",
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc -p ts/tsconfig.json",
|
||||||
|
"check": "tsc -p ts/tsconfig.json --noEmit"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"typescript": "5.9.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package src
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/hex"
|
||||||
|
"io/fs"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
webui "xteve/html"
|
||||||
|
)
|
||||||
|
|
||||||
|
// webFS serves the web interface. It is the embedded copy by default and the
|
||||||
|
// local html/ directory when xTeVe runs with -dev.
|
||||||
|
var webFS fs.FS = webui.FS
|
||||||
|
|
||||||
|
var webETags sync.Map // path -> ETag, only used when not in dev mode
|
||||||
|
|
||||||
|
// initWebFS : selects embedded or on-disk web files.
|
||||||
|
func initWebFS(dev bool) {
|
||||||
|
if dev {
|
||||||
|
webFS = os.DirFS("html")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
webFS = webui.FS
|
||||||
|
}
|
||||||
|
|
||||||
|
// readWebFile : reads a file from the web interface, name relative to html/.
|
||||||
|
func readWebFile(name string) ([]byte, error) {
|
||||||
|
name = strings.TrimPrefix(path.Clean("/"+name), "/")
|
||||||
|
if name == "" || name == "." {
|
||||||
|
return nil, fs.ErrNotExist
|
||||||
|
}
|
||||||
|
return fs.ReadFile(webFS, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// webFileETag : content hash used for HTTP caching of static web assets.
|
||||||
|
func webFileETag(name string, content []byte) string {
|
||||||
|
if !System.Dev {
|
||||||
|
if v, ok := webETags.Load(name); ok {
|
||||||
|
return v.(string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sum := sha256.Sum256(content)
|
||||||
|
etag := `"` + hex.EncodeToString(sum[:16]) + `"`
|
||||||
|
if !System.Dev {
|
||||||
|
webETags.Store(name, etag)
|
||||||
|
}
|
||||||
|
return etag
|
||||||
|
}
|
||||||
+1
-4
@@ -153,10 +153,7 @@ func bufferingStream(playlistID, streamingURL, channelName string, w http.Respon
|
|||||||
|
|
||||||
showInfo(fmt.Sprintf("Streaming Status:Playlist: %s - No new connections available. Tuner = %d", playlist.PlaylistName, playlist.Tuner))
|
showInfo(fmt.Sprintf("Streaming Status:Playlist: %s - No new connections available. Tuner = %d", playlist.PlaylistName, playlist.Tuner))
|
||||||
|
|
||||||
if value, ok := webUI["html/video/stream-limit.ts"]; ok {
|
if content, err := readWebFile("video/stream-limit.ts"); err == nil {
|
||||||
|
|
||||||
var content string
|
|
||||||
content = GetHTMLString(value.(string))
|
|
||||||
|
|
||||||
w.WriteHeader(200)
|
w.WriteHeader(200)
|
||||||
w.Header().Set("Content-type", "video/mpeg")
|
w.Header().Set("Content-type", "video/mpeg")
|
||||||
|
|||||||
+2
-13
@@ -188,16 +188,8 @@ func Init() (err error) {
|
|||||||
|
|
||||||
System.URLBase = fmt.Sprintf("%s://%s:%s", System.ServerProtocol.WEB, System.IPAddress, Settings.Port)
|
System.URLBase = fmt.Sprintf("%s://%s:%s", System.ServerProtocol.WEB, System.IPAddress, Settings.Port)
|
||||||
|
|
||||||
// HTML Dateien erstellen, mit dev == true werden die lokalen HTML Dateien verwendet
|
// Mit dev == true werden die lokalen HTML Dateien verwendet, sonst die eingebetteten
|
||||||
if System.Dev == true {
|
initWebFS(System.Dev)
|
||||||
|
|
||||||
HTMLInit("webUI", "src", "html"+string(os.PathSeparator), "src"+string(os.PathSeparator)+"webUI.go")
|
|
||||||
err = BuildGoFile()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// DLNA Server starten
|
// DLNA Server starten
|
||||||
err = SSDP()
|
err = SSDP()
|
||||||
@@ -205,9 +197,6 @@ func Init() (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// HTML Datein laden
|
|
||||||
loadHTMLMap()
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,148 +0,0 @@
|
|||||||
package src
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"encoding/base64"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
var htmlFolder string
|
|
||||||
var goFile string
|
|
||||||
var mapName string
|
|
||||||
var packageName string
|
|
||||||
|
|
||||||
var blankMap = make(map[string]any)
|
|
||||||
|
|
||||||
// HTMLInit : Dateipfade festlegen
|
|
||||||
// mapName = Name der zu erstellenden map
|
|
||||||
// htmlFolder: Ordner der HTML Dateien
|
|
||||||
// packageName: Name des package
|
|
||||||
func HTMLInit(name, pkg, folder, file string) {
|
|
||||||
|
|
||||||
htmlFolder = folder
|
|
||||||
goFile = file
|
|
||||||
mapName = name
|
|
||||||
packageName = pkg
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// BuildGoFile : Erstellt das GO Dokument
|
|
||||||
func BuildGoFile() error {
|
|
||||||
|
|
||||||
var err = checkHTMLFile(htmlFolder)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var content string
|
|
||||||
content += `package ` + packageName + "\n\n"
|
|
||||||
content += `var ` + mapName + ` = make(map[string]interface{})` + "\n\n"
|
|
||||||
content += "func loadHTMLMap() {" + "\n\n"
|
|
||||||
|
|
||||||
content += createMapFromFiles(htmlFolder) + "\n"
|
|
||||||
|
|
||||||
content += "}" + "\n\n"
|
|
||||||
writeStringToFile(goFile, content)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetHTMLString : base64 -> string
|
|
||||||
func GetHTMLString(base string) string {
|
|
||||||
content, _ := base64.StdEncoding.DecodeString(base)
|
|
||||||
return string(content)
|
|
||||||
}
|
|
||||||
|
|
||||||
func createMapFromFiles(folder string) string {
|
|
||||||
|
|
||||||
var path = getLocalPath(folder)
|
|
||||||
|
|
||||||
err := filepath.Walk(path, readFilesToMap)
|
|
||||||
if err != nil {
|
|
||||||
checkErr(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var content strings.Builder
|
|
||||||
|
|
||||||
for key := range blankMap {
|
|
||||||
var newKey = key
|
|
||||||
content.WriteString(` ` + mapName + `["` + newKey + `"` + `] = "` + blankMap[key].(string) + `"` + "\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
return content.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func readFilesToMap(path string, info os.FileInfo, err error) error {
|
|
||||||
|
|
||||||
if info.IsDir() == false {
|
|
||||||
var base64Str = fileToBase64(getLocalPath(path))
|
|
||||||
blankMap[path] = base64Str
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func fileToBase64(file string) string {
|
|
||||||
|
|
||||||
imgFile, _ := os.Open(file)
|
|
||||||
defer imgFile.Close()
|
|
||||||
|
|
||||||
// create a new buffer base on file size
|
|
||||||
fInfo, _ := imgFile.Stat()
|
|
||||||
var size = fInfo.Size()
|
|
||||||
buf := make([]byte, int64(size))
|
|
||||||
|
|
||||||
// read file content into buffer
|
|
||||||
fReader := bufio.NewReader(imgFile)
|
|
||||||
fReader.Read(buf)
|
|
||||||
|
|
||||||
imgBase64Str := base64.StdEncoding.EncodeToString(buf)
|
|
||||||
|
|
||||||
return imgBase64Str
|
|
||||||
}
|
|
||||||
|
|
||||||
func getLocalPath(filename string) string {
|
|
||||||
|
|
||||||
path, file := filepath.Split(filename)
|
|
||||||
var newPath = filepath.Dir(path)
|
|
||||||
|
|
||||||
var newFileName = newPath + "/" + file
|
|
||||||
|
|
||||||
return newFileName
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeStringToFile(filename, content string) error {
|
|
||||||
|
|
||||||
err := ioutil.WriteFile(getPlatformFile(filename), []byte(content), 0644)
|
|
||||||
if err != nil {
|
|
||||||
checkErr(err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkHTMLFile(filename string) error {
|
|
||||||
|
|
||||||
if _, err := os.Stat(getLocalPath(filename)); os.IsNotExist(err) {
|
|
||||||
fmt.Println(filename)
|
|
||||||
checkErr(err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkErr(err error) {
|
|
||||||
if err != nil {
|
|
||||||
_, file, line, _ := runtime.Caller(1)
|
|
||||||
log.Println("ERROR: [", err, "] in ", file, line)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -267,7 +267,6 @@ type SettingsStruct struct {
|
|||||||
FilesUpdate bool `json:"files.update"`
|
FilesUpdate bool `json:"files.update"`
|
||||||
Filter map[int64]any `json:"filter"`
|
Filter map[int64]any `json:"filter"`
|
||||||
Key string `json:"key,omitempty"`
|
Key string `json:"key,omitempty"`
|
||||||
Language string `json:"language"`
|
|
||||||
LogEntriesRAM int `json:"log.entries.ram"`
|
LogEntriesRAM int `json:"log.entries.ram"`
|
||||||
M3U8AdaptiveBandwidthMBPS int `json:"m3u8.adaptive.bandwidth.mbps"`
|
M3U8AdaptiveBandwidthMBPS int `json:"m3u8.adaptive.bandwidth.mbps"`
|
||||||
MappingFirstChannel float64 `json:"mapping.first.channel"`
|
MappingFirstChannel float64 `json:"mapping.first.channel"`
|
||||||
@@ -285,9 +284,3 @@ type SettingsStruct struct {
|
|||||||
WizardCompleted bool `json:"wizard.completed"`
|
WizardCompleted bool `json:"wizard.completed"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// LanguageUI : Sprache für das WebUI
|
|
||||||
type LanguageUI struct {
|
|
||||||
Login struct {
|
|
||||||
Failed string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -138,7 +138,6 @@ func loadSettings() (settings SettingsStruct, err error) {
|
|||||||
defaults["files.update"] = true
|
defaults["files.update"] = true
|
||||||
defaults["filter"] = make(map[string]any)
|
defaults["filter"] = make(map[string]any)
|
||||||
defaults["git.branch"] = System.Branch
|
defaults["git.branch"] = System.Branch
|
||||||
defaults["language"] = "en"
|
|
||||||
defaults["log.entries.ram"] = 500
|
defaults["log.entries.ram"] = 500
|
||||||
defaults["mapping.first.channel"] = 1000
|
defaults["mapping.first.channel"] = 1000
|
||||||
defaults["xepg.missing.epg.mode"] = "strict"
|
defaults["xepg.missing.epg.mode"] = "strict"
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
+57
-76
@@ -38,6 +38,7 @@ func StartWebserver() (err error) {
|
|||||||
http.HandleFunc("/api/", API)
|
http.HandleFunc("/api/", API)
|
||||||
http.HandleFunc("/images/", Images)
|
http.HandleFunc("/images/", Images)
|
||||||
http.HandleFunc("/data_images/", DataImages)
|
http.HandleFunc("/data_images/", DataImages)
|
||||||
|
http.HandleFunc("/healthz", Healthz)
|
||||||
|
|
||||||
//http.HandleFunc("/auto/", Auto)
|
//http.HandleFunc("/auto/", Auto)
|
||||||
|
|
||||||
@@ -595,44 +596,36 @@ func WS(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Healthz : Liveness endpoint for container healthchecks and monitoring.
|
||||||
|
// Unauthenticated on purpose; it exposes only the version and whether a scan is running.
|
||||||
|
func Healthz(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
var status = map[string]any{
|
||||||
|
"status": "ok",
|
||||||
|
"version": System.Version + "." + System.Build,
|
||||||
|
"scanInProgress": System.ScanInProgress == 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.Header().Set("Cache-Control", "no-store")
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
w.Write([]byte(mapToJSON(status)))
|
||||||
|
}
|
||||||
|
|
||||||
// Web : Web Server /web/
|
// Web : Web Server /web/
|
||||||
func Web(w http.ResponseWriter, r *http.Request) {
|
func Web(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
var lang = make(map[string]any)
|
var requestFile = strings.TrimPrefix(r.URL.Path, "/web")
|
||||||
var err error
|
requestFile = strings.TrimPrefix(requestFile, "/")
|
||||||
|
|
||||||
var requestFile = strings.Replace(r.URL.Path, "/web", "html", -1)
|
// Values substituted into the HTML pages (JS and CSS are served verbatim).
|
||||||
var content, contentType, file string
|
var tmpl = map[string]any{"authenticationErr": ""}
|
||||||
|
|
||||||
var language LanguageUI
|
|
||||||
|
|
||||||
setGlobalDomain(r.Host)
|
setGlobalDomain(r.Host)
|
||||||
|
|
||||||
if System.Dev == true {
|
if requestFile == "" {
|
||||||
|
|
||||||
lang, err = loadJSONFileToMap(fmt.Sprintf("html/lang/%s.json", Settings.Language))
|
var file string
|
||||||
if err != nil {
|
|
||||||
ShowError(err, 000)
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
var languageFile = "html/lang/en.json"
|
|
||||||
|
|
||||||
if value, ok := webUI[languageFile].(string); ok {
|
|
||||||
content = GetHTMLString(value)
|
|
||||||
lang = jsonToMap(content)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
err = json.Unmarshal([]byte(mapToJSON(lang)), &language)
|
|
||||||
if err != nil {
|
|
||||||
ShowError(err, 000)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if getFilenameFromPath(requestFile) == "html" {
|
|
||||||
|
|
||||||
if System.ScanInProgress == 0 {
|
if System.ScanInProgress == 0 {
|
||||||
|
|
||||||
@@ -645,16 +638,16 @@ func Web(w http.ResponseWriter, r *http.Request) {
|
|||||||
switch System.ConfigurationWizard {
|
switch System.ConfigurationWizard {
|
||||||
|
|
||||||
case true:
|
case true:
|
||||||
file = requestFile + "configuration.html"
|
file = "configuration.html"
|
||||||
Settings.AuthenticationWEB = false
|
Settings.AuthenticationWEB = false
|
||||||
|
|
||||||
case false:
|
case false:
|
||||||
file = requestFile + "index.html"
|
file = "index.html"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if System.ScanInProgress == 1 {
|
if System.ScanInProgress == 1 {
|
||||||
file = requestFile + "maintenance.html"
|
file = "maintenance.html"
|
||||||
}
|
}
|
||||||
|
|
||||||
switch Settings.AuthenticationWEB {
|
switch Settings.AuthenticationWEB {
|
||||||
@@ -692,8 +685,8 @@ func Web(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
var token, err = authentication.UserAuthentication(username, password)
|
var token, err = authentication.UserAuthentication(username, password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
file = requestFile + "login.html"
|
file = "login.html"
|
||||||
lang["authenticationErr"] = language.Login.Failed
|
tmpl["authenticationErr"] = "User authentication failed"
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -708,17 +701,16 @@ func Web(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
|
|
||||||
case "GET":
|
case "GET":
|
||||||
lang["authenticationErr"] = ""
|
|
||||||
_, token, err := authentication.CheckTheValidityOfTheTokenFromHTTPHeader(w, r)
|
_, token, err := authentication.CheckTheValidityOfTheTokenFromHTTPHeader(w, r)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
file = requestFile + "login.html"
|
file = "login.html"
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
err = checkAuthorizationLevel(token, "authentication.web")
|
err = checkAuthorizationLevel(token, "authentication.web")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
file = requestFile + "login.html"
|
file = "login.html"
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -732,58 +724,47 @@ func Web(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(allUserData) == 0 && Settings.AuthenticationWEB == true {
|
if len(allUserData) == 0 && Settings.AuthenticationWEB == true {
|
||||||
file = requestFile + "create-first-user.html"
|
file = "create-first-user.html"
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
requestFile = file
|
requestFile = file
|
||||||
|
|
||||||
if value, ok := webUI[requestFile]; ok {
|
|
||||||
|
|
||||||
content = GetHTMLString(value.(string))
|
|
||||||
|
|
||||||
if contentType == "text/plain" {
|
|
||||||
w.Header().Set("Content-Disposition", "attachment; filename="+getFilenameFromPath(requestFile))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
content, err := readWebFile(requestFile)
|
||||||
|
if err != nil {
|
||||||
httpStatusError(w, r, 404)
|
httpStatusError(w, r, 404)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var contentType = getContentType(requestFile)
|
||||||
|
|
||||||
|
switch contentType {
|
||||||
|
|
||||||
|
case "text/html":
|
||||||
|
content = []byte(parseTemplate(string(content), tmpl))
|
||||||
|
w.Header().Set("Cache-Control", "no-store")
|
||||||
|
|
||||||
|
case "text/plain":
|
||||||
|
w.Header().Set("Content-Disposition", "attachment; filename="+getFilenameFromPath(requestFile))
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Static asset: let the browser revalidate cheaply with an ETag.
|
||||||
|
var etag = webFileETag(requestFile, content)
|
||||||
|
w.Header().Set("ETag", etag)
|
||||||
|
w.Header().Set("Cache-Control", "no-cache")
|
||||||
|
if r.Header.Get("If-None-Match") == etag {
|
||||||
|
w.WriteHeader(http.StatusNotModified)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if value, ok := webUI[requestFile].(string); ok {
|
w.Header().Set("Content-Type", contentType)
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
content = GetHTMLString(value)
|
w.Write(content)
|
||||||
contentType = getContentType(requestFile)
|
|
||||||
|
|
||||||
if contentType == "text/plain" {
|
|
||||||
w.Header().Set("Content-Disposition", "attachment; filename="+getFilenameFromPath(requestFile))
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
httpStatusError(w, r, 404)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
contentType = getContentType(requestFile)
|
|
||||||
|
|
||||||
if System.Dev == true {
|
|
||||||
// Lokale Webserver Dateien werden geladen, nur für die Entwicklung
|
|
||||||
content, _ = readStringFromFile(requestFile)
|
|
||||||
}
|
|
||||||
|
|
||||||
w.Header().Add("Content-Type", contentType)
|
|
||||||
w.WriteHeader(200)
|
|
||||||
|
|
||||||
if contentType == "text/html" || contentType == "application/javascript" {
|
|
||||||
content = parseTemplate(content, lang)
|
|
||||||
}
|
|
||||||
|
|
||||||
w.Write([]byte(content))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// API : API request /api/
|
// API : API request /api/
|
||||||
|
|||||||
+6
-6
@@ -43,14 +43,14 @@ Status: Phase 0 committed on branch `improvements` 2026-09-26.
|
|||||||
- [ ] Error hygiene: no-op closures, `os.Exit`/`panic` outside main, ignored results
|
- [ ] Error hygiene: no-op closures, `os.Exit`/`panic` outside main, ignored results
|
||||||
|
|
||||||
## Phase 3: Build, embed, CI, Docker
|
## Phase 3: Build, embed, CI, Docker
|
||||||
- [ ] `//go:embed html`, delete `webUI.go`, `html-build.go`, `cmd/webui-gen`; cache headers
|
- [x] `//go:embed` via `html/embed.go`; deleted `webUI.go`, `html-build.go`, `cmd/webui-gen`; ETag + Cache-Control on static assets
|
||||||
- [ ] Drop i18n: inline English strings into TS, delete `en.json`, template only HTML pages
|
- [x] i18n dropped: 254 placeholders inlined, `en.json` deleted, only HTML pages templated (`authenticationErr`)
|
||||||
- [ ] `package.json` + `tsconfig.json` + esbuild bundle (committed, CI-verified); delete 10 dead JS files
|
- [x] `package.json` + `ts/tsconfig.json` (tsc 5.9.3, ES2020, single outFile `html/js/app.js`, committed, CI-verified); 10 dead JS files deleted; 6 tsc errors fixed incl. a real ASI bug in the search shortcut
|
||||||
- [ ] CI: vet, lint, gofmt, `test -race`, `tsc --noEmit`, generated-output check
|
- [ ] CI: vet (done), lint, gofmt, `test -race`; bundle freshness check (done)
|
||||||
- [ ] Version-tagged images (amd64 only)
|
- [ ] Version-tagged images (amd64 only)
|
||||||
- [ ] PUID/PGID via su-exec; pin static-ffmpeg; `VOLUME`; fix `LEGACY_CONFIG_DIRS`
|
- [ ] PUID/PGID via su-exec; pin static-ffmpeg; `VOLUME`; fix `LEGACY_CONFIG_DIRS`
|
||||||
- [ ] `/healthz` endpoint
|
- [x] `/healthz` endpoint; Dockerfile healthcheck uses it
|
||||||
- [ ] `README-DEV.md` and fork section in `README.md`
|
- [ ] `README-DEV.md` (done) and fork section in `README.md`
|
||||||
- [ ] Deduplicate and delete dead code; drop `ioutil`, `osext`, `rand.Seed` (after go:embed)
|
- [ ] Deduplicate and delete dead code; drop `ioutil`, `osext`, `rand.Seed` (after go:embed)
|
||||||
- [ ] Resolve missing `docs/design-system/` referenced by `agent.md`
|
- [ ] Resolve missing `docs/design-system/` referenced by `agent.md`
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ function login() {
|
|||||||
document.getElementById('password').style.borderColor = "red"
|
document.getElementById('password').style.borderColor = "red"
|
||||||
document.getElementById('confirm').style.borderColor = "red"
|
document.getElementById('confirm').style.borderColor = "red"
|
||||||
|
|
||||||
document.getElementById("err").innerHTML = "{{.account.failed}}"
|
document.getElementById("err").innerHTML = "Password does not match"
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+16
-16
@@ -11,22 +11,22 @@ var LAST_BULK_CHECKBOX:HTMLInputElement = null
|
|||||||
|
|
||||||
// Menü
|
// Menü
|
||||||
var menuItems = new Array()
|
var menuItems = new Array()
|
||||||
menuItems.push(new MainMenuItem("playlist", "{{.mainMenu.item.playlist}}", "m3u.png", "{{.mainMenu.headline.playlist}}"))
|
menuItems.push(new MainMenuItem("playlist", "Playlist", "m3u.png", "Local or remote playlists"))
|
||||||
//menuItems.push(new MainMenuItem("pmsID", "{{.mainMenu.item.pmsID}}", "number.png", "{{.mainMenu.headline.pmsID}}"))
|
//menuItems.push(new MainMenuItem("pmsID", "PMS ID", "number.png", ""))
|
||||||
menuItems.push(new MainMenuItem("filter", "{{.mainMenu.item.filter}}", "filter.png", "{{.mainMenu.headline.filter}}"))
|
menuItems.push(new MainMenuItem("filter", "Filter", "filter.png", "Filter playlist"))
|
||||||
menuItems.push(new MainMenuItem("xmltv", "{{.mainMenu.item.xmltv}}", "xmltv.png", "{{.mainMenu.headline.xmltv}}"))
|
menuItems.push(new MainMenuItem("xmltv", "XMLTV", "xmltv.png", "Local or remote XMLTV files"))
|
||||||
menuItems.push(new MainMenuItem("mapping", "{{.mainMenu.item.mapping}}", "mapping.png", "{{.mainMenu.headline.mapping}}"))
|
menuItems.push(new MainMenuItem("mapping", "Mapping", "mapping.png", "Map playlist channels to EPG channels"))
|
||||||
menuItems.push(new MainMenuItem("users", "{{.mainMenu.item.users}}", "users.png", "{{.mainMenu.headline.users}}"))
|
menuItems.push(new MainMenuItem("users", "Users", "users.png", "User management"))
|
||||||
menuItems.push(new MainMenuItem("settings", "{{.mainMenu.item.settings}}", "settings.png", "{{.mainMenu.headline.settings}}"))
|
menuItems.push(new MainMenuItem("settings", "Settings", "settings.png", "Settings"))
|
||||||
menuItems.push(new MainMenuItem("log", "{{.mainMenu.item.log}}", "log.png", "{{.mainMenu.headline.log}}"))
|
menuItems.push(new MainMenuItem("log", "Log", "log.png", "Log"))
|
||||||
menuItems.push(new MainMenuItem("logout", "{{.mainMenu.item.logout}}", "logout.png", "{{.mainMenu.headline.logout}}"))
|
menuItems.push(new MainMenuItem("logout", "Logout", "logout.png", "Logout"))
|
||||||
|
|
||||||
// Kategorien für die Einstellungen
|
// Kategorien für die Einstellungen
|
||||||
var settingsCategory = new Array()
|
var settingsCategory = new Array()
|
||||||
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.general}}", "tuner,epgSource,api,use_plexAPI,plex.url,plex.token"));settingsCategory.push(new SettingsCategoryItem("{{.settings.category.files}}", "update,files.update,temp.path,cache.images,xepg.missing.epg.mode,xepg.replace.missing.images"))
|
settingsCategory.push(new SettingsCategoryItem("General", "tuner,epgSource,api,use_plexAPI,plex.url,plex.token"));settingsCategory.push(new SettingsCategoryItem("Files", "update,files.update,temp.path,cache.images,xepg.missing.epg.mode,xepg.replace.missing.images"))
|
||||||
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.streaming}}", "buffer,udpxy,buffer.size.kb,buffer.timeout,user.agent,ffmpeg.path,ffmpeg.options,vlc.path,vlc.options"))
|
settingsCategory.push(new SettingsCategoryItem("Streaming", "buffer,udpxy,buffer.size.kb,buffer.timeout,user.agent,ffmpeg.path,ffmpeg.options,vlc.path,vlc.options"))
|
||||||
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.backup}}", "backup.path,backup.keep"))
|
settingsCategory.push(new SettingsCategoryItem("Backup", "backup.path,backup.keep"))
|
||||||
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.authentication}}", "authentication.web,authentication.pms,authentication.m3u,authentication.xml,authentication.api"))
|
settingsCategory.push(new SettingsCategoryItem("Authentication", "authentication.web,authentication.pms,authentication.m3u,authentication.xml,authentication.api"))
|
||||||
|
|
||||||
function showPopUpElement(elm) {
|
function showPopUpElement(elm) {
|
||||||
|
|
||||||
@@ -342,7 +342,7 @@ function sortTable(column) {
|
|||||||
default: console.log(x.childNodes[0].tagName);
|
default: console.log(x.childNodes[0].tagName);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (xValue == "" || xValue == NaN) {
|
if (xValue == "" || Number.isNaN(xValue)) {
|
||||||
|
|
||||||
xValue = i
|
xValue = i
|
||||||
sortObj[i] = rows[i];
|
sortObj[i] = rows[i];
|
||||||
@@ -502,7 +502,7 @@ function changeChannelNumber(element) {
|
|||||||
var channels = getObjKeys(data)
|
var channels = getObjKeys(data)
|
||||||
|
|
||||||
if (isNaN(newNumber)) {
|
if (isNaN(newNumber)) {
|
||||||
alert("{{.alert.invalidChannelNumber}}")
|
alert("Invalid channel number")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -628,7 +628,7 @@ function restore() {
|
|||||||
restore.onchange = function() {
|
restore.onchange = function() {
|
||||||
|
|
||||||
var filename = (restore as HTMLInputElement).files[0].name
|
var filename = (restore as HTMLInputElement).files[0].name
|
||||||
var check = confirm("File: " + filename + "\n{{.confirm.restore}}");
|
var check = confirm("File: " + filename + "\nAll data will be replaced with those from the backup. Should the files be restored?");
|
||||||
|
|
||||||
if (check == true) {
|
if (check == true) {
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
tsc *.ts --outDir ../html/js/
|
|
||||||
+11
-11
@@ -43,7 +43,7 @@ class WizardItem extends WizardCategory {
|
|||||||
select.id = key
|
select.id = key
|
||||||
doc.appendChild(select)
|
doc.appendChild(select)
|
||||||
|
|
||||||
description = "{{.wizard.tuner.description}}"
|
description = "Number of parallel connections that can be established to the provider.<br>Available for: Plex, Emby (HDHR), M3U (with active buffer).<br>After a change, xTeVe must be delete in the Plex / Emby DVR settings and set up again."
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -56,29 +56,29 @@ class WizardItem extends WizardCategory {
|
|||||||
select.id = key
|
select.id = key
|
||||||
doc.appendChild(select)
|
doc.appendChild(select)
|
||||||
|
|
||||||
description = "{{.wizard.epgSource.description}}"
|
description = "PMS:<br>- Use EPG data from Plex or Emby <br><br>XEPG:<br>- Use of one or more XMLTV files<br>- Channel management<br>- M3U / XMLTV export (HTTP link for IPTV apps)"
|
||||||
|
|
||||||
break
|
break
|
||||||
|
|
||||||
case "m3u":
|
case "m3u":
|
||||||
var input = content.createInput("text", key, "")
|
var input = content.createInput("text", key, "")
|
||||||
input.setAttribute("placeholder", "{{.wizard.m3u.placeholder}}")
|
input.setAttribute("placeholder", "File path or URL of the M3U")
|
||||||
input.setAttribute("class", "wizard")
|
input.setAttribute("class", "wizard")
|
||||||
input.id = key
|
input.id = key
|
||||||
doc.appendChild(input)
|
doc.appendChild(input)
|
||||||
|
|
||||||
description = "{{.wizard.m3u.description}}"
|
description = "Local or remote playlists"
|
||||||
|
|
||||||
break
|
break
|
||||||
|
|
||||||
case "xmltv":
|
case "xmltv":
|
||||||
var input = content.createInput("text", key, "")
|
var input = content.createInput("text", key, "")
|
||||||
input.setAttribute("placeholder", "{{.wizard.xmltv.placeholder}}")
|
input.setAttribute("placeholder", "File path or URL of the XMLTV")
|
||||||
input.setAttribute("class", "wizard")
|
input.setAttribute("class", "wizard")
|
||||||
input.id = key
|
input.id = key
|
||||||
doc.appendChild(input)
|
doc.appendChild(input)
|
||||||
|
|
||||||
description = "{{.wizard.xmltv.description}}"
|
description = "Local or remote XMLTV file"
|
||||||
|
|
||||||
break
|
break
|
||||||
|
|
||||||
@@ -143,7 +143,7 @@ function saveWizard() {
|
|||||||
value = (config[i] as HTMLInputElement).value
|
value = (config[i] as HTMLInputElement).value
|
||||||
|
|
||||||
if (value.length == 0) {
|
if (value.length == 0) {
|
||||||
var msg = name.toUpperCase() + ": " + "{{.alert.missingInput}}"
|
var msg = name.toUpperCase() + ": " + "Missing input"
|
||||||
alert(msg)
|
alert(msg)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -171,7 +171,7 @@ function saveWizard() {
|
|||||||
|
|
||||||
// Wizard
|
// Wizard
|
||||||
var configurationWizard = new Array()
|
var configurationWizard = new Array()
|
||||||
configurationWizard.push(new WizardItem("tuner", "{{.wizard.tuner.title}}"))
|
configurationWizard.push(new WizardItem("tuner", "Number of tuners"))
|
||||||
configurationWizard.push(new WizardItem("epgSource", "{{.wizard.epgSource.title}}"))
|
configurationWizard.push(new WizardItem("epgSource", "EPG Source"))
|
||||||
configurationWizard.push(new WizardItem("m3u", "{{.wizard.m3u.title}}"))
|
configurationWizard.push(new WizardItem("m3u", "M3U Playlist"))
|
||||||
configurationWizard.push(new WizardItem("xmltv", "{{.wizard.xmltv.title}}"))
|
configurationWizard.push(new WizardItem("xmltv", "XMLTV File"))
|
||||||
+123
-123
@@ -49,23 +49,23 @@ class MainMenuItem extends MainMenu {
|
|||||||
|
|
||||||
switch(this.menuKey) {
|
switch(this.menuKey) {
|
||||||
case "playlist":
|
case "playlist":
|
||||||
this.tableHeader = ["{{.playlist.table.playlist}}", "{{.playlist.table.tuner}}", "{{.playlist.table.lastUpdate}}", "{{.playlist.table.availability}} %", "{{.playlist.table.type}}", "{{.playlist.table.streams}}", "{{.playlist.table.groupTitle}} %", "{{.playlist.table.tvgID}} %", "{{.playlist.table.uniqueID}} %"]
|
this.tableHeader = ["Playlist", "Tuner", "Last Update", "Availability %", "Type", "Streams", "group-title %", "tvg-id %", "Unique ID %"]
|
||||||
break
|
break
|
||||||
|
|
||||||
case "xmltv":
|
case "xmltv":
|
||||||
this.tableHeader = ["{{.xmltv.table.guide}}", "{{.xmltv.table.lastUpdate}}", "{{.xmltv.table.availability}} %", "{{.xmltv.table.channels}}", "{{.xmltv.table.programs}}"]
|
this.tableHeader = ["Guide", "Last Update", "Availability %", "Channels", "Programs"]
|
||||||
break
|
break
|
||||||
|
|
||||||
case "filter":
|
case "filter":
|
||||||
this.tableHeader = ["{{.filter.table.name}}", "{{.filter.table.type}}", "{{.filter.table.filter}}"]
|
this.tableHeader = ["Filter Name", "Filter Type", "Filter"]
|
||||||
break
|
break
|
||||||
|
|
||||||
case "users":
|
case "users":
|
||||||
this.tableHeader = ["{{.users.table.username}}", "{{.users.table.password}}", "{{.users.table.web}}", "{{.users.table.pms}}", "{{.users.table.m3u}}", "{{.users.table.xml}}", "{{.users.table.api}}"]
|
this.tableHeader = ["Username", "Password", "WEB", "PMS", "M3U", "XML", "API"]
|
||||||
break
|
break
|
||||||
|
|
||||||
case "mapping":
|
case "mapping":
|
||||||
this.tableHeader = ["BULK", "{{.mapping.table.chNo}}", "{{.mapping.table.logo}}", "{{.mapping.table.channelName}}", "{{.mapping.table.playlist}}", "{{.mapping.table.groupTitle}}", "{{.mapping.table.xmltvFile}}", "{{.mapping.table.xmltvID}}", "{{.mapping.table.edit}}"]
|
this.tableHeader = ["BULK", "Ch. No.", "Logo", "Channel Name", "Playlist", "Group Title", "XMLTV File", "XMLTV ID", "Edit"]
|
||||||
break
|
break
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -225,11 +225,11 @@ class Content {
|
|||||||
cell.childType = "P"
|
cell.childType = "P"
|
||||||
switch (data[key]["type"]) {
|
switch (data[key]["type"]) {
|
||||||
case "custom-filter":
|
case "custom-filter":
|
||||||
cell.value = "{{.filter.custom}}"
|
cell.value = "Custom"
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "group-title":
|
case "group-title":
|
||||||
cell.value = "{{.filter.group}}"
|
cell.value = "Group"
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -480,7 +480,7 @@ class Content {
|
|||||||
var cell:Cell = new Cell()
|
var cell:Cell = new Cell()
|
||||||
cell.child = true
|
cell.child = true
|
||||||
cell.childType = "EDIT"
|
cell.childType = "EDIT"
|
||||||
cell.value = "{{.button.edit}}"
|
cell.value = "Edit"
|
||||||
var editTd = cell.createCell()
|
var editTd = cell.createCell()
|
||||||
var editButton = (editTd.firstChild as HTMLInputElement)
|
var editButton = (editTd.firstChild as HTMLInputElement)
|
||||||
editButton.setAttribute('onclick', 'javascript: openPopUp("mapping", this)')
|
editButton.setAttribute('onclick', 'javascript: openPopUp("mapping", this)')
|
||||||
@@ -644,14 +644,14 @@ class ShowContent extends Content {
|
|||||||
var interaction = document.getElementById(this.interactionID)
|
var interaction = document.getElementById(this.interactionID)
|
||||||
switch (menuKey) {
|
switch (menuKey) {
|
||||||
case "playlist":
|
case "playlist":
|
||||||
var input = this.createInput("button", menuKey, "{{.button.new}}")
|
var input = this.createInput("button", menuKey, "New")
|
||||||
input.setAttribute("id", "-")
|
input.setAttribute("id", "-")
|
||||||
input.setAttribute("onclick", 'javascript: openPopUp("playlist")')
|
input.setAttribute("onclick", 'javascript: openPopUp("playlist")')
|
||||||
interaction.appendChild(input)
|
interaction.appendChild(input)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "filter":
|
case "filter":
|
||||||
var input = this.createInput("button", menuKey, "{{.button.new}}")
|
var input = this.createInput("button", menuKey, "New")
|
||||||
input.setAttribute("id", -1)
|
input.setAttribute("id", -1)
|
||||||
input.setAttribute("onclick", 'javascript: openPopUp("filter", this)')
|
input.setAttribute("onclick", 'javascript: openPopUp("filter", this)')
|
||||||
interaction.appendChild(input)
|
interaction.appendChild(input)
|
||||||
@@ -659,14 +659,14 @@ class ShowContent extends Content {
|
|||||||
|
|
||||||
|
|
||||||
case "xmltv":
|
case "xmltv":
|
||||||
var input = this.createInput("button", menuKey, "{{.button.new}}")
|
var input = this.createInput("button", menuKey, "New")
|
||||||
input.setAttribute("id", "xmltv")
|
input.setAttribute("id", "xmltv")
|
||||||
input.setAttribute("onclick", 'javascript: openPopUp("xmltv")')
|
input.setAttribute("onclick", 'javascript: openPopUp("xmltv")')
|
||||||
interaction.appendChild(input)
|
interaction.appendChild(input)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "users":
|
case "users":
|
||||||
var input = this.createInput("button", menuKey, "{{.button.new}}")
|
var input = this.createInput("button", menuKey, "New")
|
||||||
input.setAttribute("id", "users")
|
input.setAttribute("id", "users")
|
||||||
input.setAttribute("onclick", 'javascript: openPopUp("users")')
|
input.setAttribute("onclick", 'javascript: openPopUp("users")')
|
||||||
interaction.appendChild(input)
|
interaction.appendChild(input)
|
||||||
@@ -674,32 +674,32 @@ class ShowContent extends Content {
|
|||||||
|
|
||||||
case "mapping":
|
case "mapping":
|
||||||
showElement("loading", true)
|
showElement("loading", true)
|
||||||
var input = this.createInput("button", menuKey, "{{.button.save}}")
|
var input = this.createInput("button", menuKey, "Save")
|
||||||
input.setAttribute("onclick", 'javascript: savePopupData("mapping", "", "")')
|
input.setAttribute("onclick", 'javascript: savePopupData("mapping", "", "")')
|
||||||
interaction.appendChild(input)
|
interaction.appendChild(input)
|
||||||
|
|
||||||
var input = this.createInput("button", menuKey, "{{.button.bulkEdit}}")
|
var input = this.createInput("button", menuKey, "Bulk Edit")
|
||||||
input.setAttribute("onclick", 'javascript: bulkEdit()')
|
input.setAttribute("onclick", 'javascript: bulkEdit()')
|
||||||
interaction.appendChild(input)
|
interaction.appendChild(input)
|
||||||
|
|
||||||
var input = this.createInput("search", "search", "")
|
var input = this.createInput("search", "search", "")
|
||||||
input.setAttribute("id", "searchMapping")
|
input.setAttribute("id", "searchMapping")
|
||||||
input.setAttribute("placeholder", "{{.button.search}}")
|
input.setAttribute("placeholder", "Search")
|
||||||
input.className = "search"
|
input.className = "search"
|
||||||
input.setAttribute("oninput", 'javascript: searchInMapping()')
|
input.setAttribute("oninput", 'javascript: searchInMapping()')
|
||||||
interaction.appendChild(input)
|
interaction.appendChild(input)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "settings":
|
case "settings":
|
||||||
var input = this.createInput("button", menuKey, "{{.button.save}}")
|
var input = this.createInput("button", menuKey, "Save")
|
||||||
input.setAttribute("onclick", 'javascript: saveSettings();')
|
input.setAttribute("onclick", 'javascript: saveSettings();')
|
||||||
interaction.appendChild(input)
|
interaction.appendChild(input)
|
||||||
|
|
||||||
var input = this.createInput("button", menuKey, "{{.button.backup}}")
|
var input = this.createInput("button", menuKey, "Backup")
|
||||||
input.setAttribute("onclick", 'javascript: backup();')
|
input.setAttribute("onclick", 'javascript: backup();')
|
||||||
interaction.appendChild(input)
|
interaction.appendChild(input)
|
||||||
|
|
||||||
var input = this.createInput("button", menuKey, "{{.button.restore}}")
|
var input = this.createInput("button", menuKey, "Restore")
|
||||||
input.setAttribute("onclick", 'javascript: restore();')
|
input.setAttribute("onclick", 'javascript: restore();')
|
||||||
interaction.appendChild(input)
|
interaction.appendChild(input)
|
||||||
|
|
||||||
@@ -717,7 +717,7 @@ class ShowContent extends Content {
|
|||||||
break
|
break
|
||||||
|
|
||||||
case "log":
|
case "log":
|
||||||
var input = this.createInput("button", menuKey, "{{.button.resetLogs}}")
|
var input = this.createInput("button", menuKey, "Reset Logs")
|
||||||
input.setAttribute("onclick", 'javascript: resetLogs();')
|
input.setAttribute("onclick", 'javascript: resetLogs();')
|
||||||
interaction.appendChild(input)
|
interaction.appendChild(input)
|
||||||
|
|
||||||
@@ -770,23 +770,23 @@ class ShowContent extends Content {
|
|||||||
|
|
||||||
if (menuKey == "mapping") {
|
if (menuKey == "mapping") {
|
||||||
|
|
||||||
if (element == "{{.mapping.table.chNo}}") {
|
if (element == "Ch. No.") {
|
||||||
cell.onclick = true
|
cell.onclick = true
|
||||||
cell.onclickFunktion = "javascript: sortTable(1);"
|
cell.onclickFunktion = "javascript: sortTable(1);"
|
||||||
cell.tdClassName = "sortThis"
|
cell.tdClassName = "sortThis"
|
||||||
}
|
}
|
||||||
|
|
||||||
if (element == "{{.mapping.table.channelName}}") {
|
if (element == "Channel Name") {
|
||||||
cell.onclick = true
|
cell.onclick = true
|
||||||
cell.onclickFunktion = "javascript: sortTable(3);"
|
cell.onclickFunktion = "javascript: sortTable(3);"
|
||||||
}
|
}
|
||||||
|
|
||||||
if (element == "{{.mapping.table.playlist}}") {
|
if (element == "Playlist") {
|
||||||
cell.onclick = true
|
cell.onclick = true
|
||||||
cell.onclickFunktion = "javascript: sortTable(4);"
|
cell.onclickFunktion = "javascript: sortTable(4);"
|
||||||
}
|
}
|
||||||
|
|
||||||
if (element == "{{.mapping.table.groupTitle}}") {
|
if (element == "Group Title") {
|
||||||
cell.onclick = true
|
cell.onclick = true
|
||||||
cell.onclickFunktion = "javascript: sortTable(5);"
|
cell.onclickFunktion = "javascript: sortTable(5);"
|
||||||
}
|
}
|
||||||
@@ -975,7 +975,7 @@ function initShellLayout() {
|
|||||||
|
|
||||||
var search = document.getElementById("searchMapping")
|
var search = document.getElementById("searchMapping")
|
||||||
if (search != null) {
|
if (search != null) {
|
||||||
event.preventDefault()
|
event.preventDefault();
|
||||||
(search as HTMLInputElement).focus()
|
(search as HTMLInputElement).focus()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1072,7 +1072,7 @@ function setClientInfoValue(key:string, value:any) {
|
|||||||
|
|
||||||
if ((key == "m3u-url" || key == "xepg-url") && isClientInfoHttpURL(textValue)) {
|
if ((key == "m3u-url" || key == "xepg-url") && isClientInfoHttpURL(textValue)) {
|
||||||
element.innerHTML = ""
|
element.innerHTML = ""
|
||||||
var anchor = document.createElement("A")
|
var anchor = document.createElement("a")
|
||||||
anchor.href = textValue
|
anchor.href = textValue
|
||||||
anchor.target = "_blank"
|
anchor.target = "_blank"
|
||||||
anchor.rel = "noopener noreferrer"
|
anchor.rel = "noopener noreferrer"
|
||||||
@@ -1140,7 +1140,7 @@ function createLayout() {
|
|||||||
if (ACTIVE_MENU_ID.length == 0 && content != null && menu != null) {
|
if (ACTIVE_MENU_ID.length == 0 && content != null && menu != null) {
|
||||||
|
|
||||||
if (content.innerHTML.replace(/\\s/g, "").length == 0) {
|
if (content.innerHTML.replace(/\\s/g, "").length == 0) {
|
||||||
var firstItem = menu.getElementsByTagName("LI")[0]
|
var firstItem = menu.getElementsByTagName("LI")[0] as HTMLElement
|
||||||
if (firstItem != undefined) {
|
if (firstItem != undefined) {
|
||||||
firstItem.click()
|
firstItem.click()
|
||||||
}
|
}
|
||||||
@@ -1330,24 +1330,24 @@ function openPopUp(dataType, element) {
|
|||||||
|
|
||||||
switch (dataType) {
|
switch (dataType) {
|
||||||
case "playlist":
|
case "playlist":
|
||||||
content.createHeadline("{{.playlist.playlistType.title}}")
|
content.createHeadline("Playlist type")
|
||||||
// Type
|
// Type
|
||||||
var text:string[] = ["M3U", "HDHomeRun"]
|
var text:string[] = ["M3U", "HDHomeRun"]
|
||||||
var values:string[] = ["javascript: openPopUp('m3u')", "javascript: openPopUp('hdhr')"]
|
var values:string[] = ["javascript: openPopUp('m3u')", "javascript: openPopUp('hdhr')"]
|
||||||
var select = content.createSelect(text, values, "", "type")
|
var select = content.createSelect(text, values, "", "type")
|
||||||
select.setAttribute("id", "type")
|
select.setAttribute("id", "type")
|
||||||
select.setAttribute("onchange", 'javascript: changeButtonAction(this, "next", "onclick")') // changeButtonAction
|
select.setAttribute("onchange", 'javascript: changeButtonAction(this, "next", "onclick")') // changeButtonAction
|
||||||
content.appendRow("{{.playlist.type.title}}", select)
|
content.appendRow("Type", select)
|
||||||
|
|
||||||
// Interaktion
|
// Interaktion
|
||||||
content.createInteraction()
|
content.createInteraction()
|
||||||
// Abbrechen
|
// Abbrechen
|
||||||
var input = content.createInput("button", "cancel", "{{.button.cancel}}")
|
var input = content.createInput("button", "cancel", "Cancel")
|
||||||
input.setAttribute("onclick", 'javascript: showElement("popup", false);')
|
input.setAttribute("onclick", 'javascript: showElement("popup", false);')
|
||||||
content.addInteraction(input)
|
content.addInteraction(input)
|
||||||
|
|
||||||
// Weiter
|
// Weiter
|
||||||
var input = content.createInput("button", "next", "{{.button.next}}")
|
var input = content.createInput("button", "next", "Next")
|
||||||
input.setAttribute("onclick", 'javascript: openPopUp("m3u")')
|
input.setAttribute("onclick", 'javascript: openPopUp("m3u")')
|
||||||
input.setAttribute("id", 'next')
|
input.setAttribute("id", 'next')
|
||||||
content.addInteraction(input)
|
content.addInteraction(input)
|
||||||
@@ -1358,20 +1358,20 @@ function openPopUp(dataType, element) {
|
|||||||
// Name
|
// Name
|
||||||
var dbKey:string = "name"
|
var dbKey:string = "name"
|
||||||
var input = content.createInput("text", dbKey, data[dbKey])
|
var input = content.createInput("text", dbKey, data[dbKey])
|
||||||
input.setAttribute("placeholder", "{{.playlist.name.placeholder}}")
|
input.setAttribute("placeholder", "Playlist name")
|
||||||
content.appendRow("{{.playlist.name.title}}", input)
|
content.appendRow("Name", input)
|
||||||
|
|
||||||
// Beschreibung
|
// Beschreibung
|
||||||
var dbKey:string = "description"
|
var dbKey:string = "description"
|
||||||
var input = content.createInput("text", dbKey, data[dbKey])
|
var input = content.createInput("text", dbKey, data[dbKey])
|
||||||
input.setAttribute("placeholder", "{{.playlist.description.placeholder}}")
|
input.setAttribute("placeholder", "Description")
|
||||||
content.appendRow("{{.playlist.description.title}}", input)
|
content.appendRow("Description", input)
|
||||||
|
|
||||||
// URL
|
// URL
|
||||||
var dbKey:string = "file.source"
|
var dbKey:string = "file.source"
|
||||||
var input = content.createInput("text", dbKey, data[dbKey])
|
var input = content.createInput("text", dbKey, data[dbKey])
|
||||||
input.setAttribute("placeholder", "{{.playlist.fileM3U.placeholder}}")
|
input.setAttribute("placeholder", "File path or URL of the M3U")
|
||||||
content.appendRow("{{.playlist.fileM3U.title}}", input)
|
content.appendRow("M3U File", input)
|
||||||
|
|
||||||
// Tuner
|
// Tuner
|
||||||
if (SERVER["settings"]["buffer"] != "-") {
|
if (SERVER["settings"]["buffer"] != "-") {
|
||||||
@@ -1386,7 +1386,7 @@ function openPopUp(dataType, element) {
|
|||||||
var dbKey:string = "tuner"
|
var dbKey:string = "tuner"
|
||||||
var select = content.createSelect(text, values, data[dbKey], dbKey)
|
var select = content.createSelect(text, values, data[dbKey], dbKey)
|
||||||
select.setAttribute("onfocus", "javascript: return;")
|
select.setAttribute("onfocus", "javascript: return;")
|
||||||
content.appendRow("{{.playlist.tuner.title}}", select)
|
content.appendRow("Tuner / Streams", select)
|
||||||
} else {
|
} else {
|
||||||
var dbKey:string = "tuner"
|
var dbKey:string = "tuner"
|
||||||
if (data[dbKey] == undefined) {
|
if (data[dbKey] == undefined) {
|
||||||
@@ -1395,39 +1395,39 @@ function openPopUp(dataType, element) {
|
|||||||
var input = content.createInput("text", dbKey, data[dbKey])
|
var input = content.createInput("text", dbKey, data[dbKey])
|
||||||
input.setAttribute("readonly", "true")
|
input.setAttribute("readonly", "true")
|
||||||
input.className = "notAvailable"
|
input.className = "notAvailable"
|
||||||
content.appendRow("{{.playlist.tuner.title}}", input)
|
content.appendRow("Tuner / Streams", input)
|
||||||
}
|
}
|
||||||
|
|
||||||
content.description("{{.playlist.tuner.description}}")
|
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.")
|
||||||
|
|
||||||
// Interaktion
|
// Interaktion
|
||||||
content.createInteraction()
|
content.createInteraction()
|
||||||
// Löschen
|
// Löschen
|
||||||
if (data["id.provider"]!= "-") {
|
if (data["id.provider"]!= "-") {
|
||||||
var input = content.createInput("button", "delete", "{{.button.delete}}")
|
var input = content.createInput("button", "delete", "Delete")
|
||||||
input.className = "delete"
|
input.className = "delete"
|
||||||
input.setAttribute('onclick', 'javascript: savePopupData("m3u", "' + id + '", true, 0)')
|
input.setAttribute('onclick', 'javascript: savePopupData("m3u", "' + id + '", true, 0)')
|
||||||
content.addInteraction(input)
|
content.addInteraction(input)
|
||||||
} else {
|
} else {
|
||||||
var input = content.createInput("button", "back", "{{.button.back}}")
|
var input = content.createInput("button", "back", "Back")
|
||||||
input.setAttribute("onclick", 'javascript: openPopUp("playlist")')
|
input.setAttribute("onclick", 'javascript: openPopUp("playlist")')
|
||||||
content.addInteraction(input)
|
content.addInteraction(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Abbrechen
|
// Abbrechen
|
||||||
var input = content.createInput("button", "cancel", "{{.button.cancel}}")
|
var input = content.createInput("button", "cancel", "Cancel")
|
||||||
input.setAttribute("onclick", 'javascript: showElement("popup", false);')
|
input.setAttribute("onclick", 'javascript: showElement("popup", false);')
|
||||||
content.addInteraction(input)
|
content.addInteraction(input)
|
||||||
|
|
||||||
// Aktualisieren
|
// Aktualisieren
|
||||||
if (data["id.provider"]!= "-") {
|
if (data["id.provider"]!= "-") {
|
||||||
var input = content.createInput("button", "update", "{{.button.update}}")
|
var input = content.createInput("button", "update", "Update")
|
||||||
input.setAttribute('onclick', 'javascript: savePopupData("m3u", "' + id + '", false, 1)')
|
input.setAttribute('onclick', 'javascript: savePopupData("m3u", "' + id + '", false, 1)')
|
||||||
content.addInteraction(input)
|
content.addInteraction(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Speichern
|
// Speichern
|
||||||
var input = content.createInput("button", "save", "{{.button.save}}")
|
var input = content.createInput("button", "save", "Save")
|
||||||
input.setAttribute('onclick', 'javascript: savePopupData("m3u", "' + id + '", false, 0)')
|
input.setAttribute('onclick', 'javascript: savePopupData("m3u", "' + id + '", false, 0)')
|
||||||
content.addInteraction(input)
|
content.addInteraction(input)
|
||||||
break
|
break
|
||||||
@@ -1437,20 +1437,20 @@ function openPopUp(dataType, element) {
|
|||||||
// Name
|
// Name
|
||||||
var dbKey:string = "name"
|
var dbKey:string = "name"
|
||||||
var input = content.createInput("text", dbKey, data[dbKey])
|
var input = content.createInput("text", dbKey, data[dbKey])
|
||||||
input.setAttribute("placeholder", "{{.playlist.name.placeholder}}")
|
input.setAttribute("placeholder", "Playlist name")
|
||||||
content.appendRow("{{.playlist.name.title}}", input)
|
content.appendRow("Name", input)
|
||||||
|
|
||||||
// Beschreibung
|
// Beschreibung
|
||||||
var dbKey:string = "description"
|
var dbKey:string = "description"
|
||||||
var input = content.createInput("text", dbKey, data[dbKey])
|
var input = content.createInput("text", dbKey, data[dbKey])
|
||||||
input.setAttribute("placeholder", "{{.playlist.description.placeholder}}")
|
input.setAttribute("placeholder", "Description")
|
||||||
content.appendRow("{{.playlist.description.placeholder}}", input)
|
content.appendRow("Description", input)
|
||||||
|
|
||||||
// URL
|
// URL
|
||||||
var dbKey:string = "file.source"
|
var dbKey:string = "file.source"
|
||||||
var input = content.createInput("text", dbKey, data[dbKey])
|
var input = content.createInput("text", dbKey, data[dbKey])
|
||||||
input.setAttribute("placeholder", "{{.playlist.fileHDHR.placeholder}}")
|
input.setAttribute("placeholder", "IP address and port (192.168.1.10:5004)")
|
||||||
content.appendRow("{{.playlist.fileHDHR.title}}", input)
|
content.appendRow("HDHomeRun IP", input)
|
||||||
|
|
||||||
// Tuner
|
// Tuner
|
||||||
if (SERVER["settings"]["buffer"] != "-") {
|
if (SERVER["settings"]["buffer"] != "-") {
|
||||||
@@ -1465,7 +1465,7 @@ function openPopUp(dataType, element) {
|
|||||||
var dbKey:string = "tuner"
|
var dbKey:string = "tuner"
|
||||||
var select = content.createSelect(text, values, data[dbKey], dbKey)
|
var select = content.createSelect(text, values, data[dbKey], dbKey)
|
||||||
select.setAttribute("onfocus", "javascript: return;")
|
select.setAttribute("onfocus", "javascript: return;")
|
||||||
content.appendRow("{{.playlist.tuner.title}}", select)
|
content.appendRow("Tuner / Streams", select)
|
||||||
} else {
|
} else {
|
||||||
var dbKey:string = "tuner"
|
var dbKey:string = "tuner"
|
||||||
if (data[dbKey] == undefined) {
|
if (data[dbKey] == undefined) {
|
||||||
@@ -1474,39 +1474,39 @@ function openPopUp(dataType, element) {
|
|||||||
var input = content.createInput("text", dbKey, data[dbKey])
|
var input = content.createInput("text", dbKey, data[dbKey])
|
||||||
input.setAttribute("readonly", "true")
|
input.setAttribute("readonly", "true")
|
||||||
input.className = "notAvailable"
|
input.className = "notAvailable"
|
||||||
content.appendRow("{{.playlist.tuner.title}}", input)
|
content.appendRow("Tuner / Streams", input)
|
||||||
}
|
}
|
||||||
|
|
||||||
content.description("{{.playlist.tuner.description}}")
|
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.")
|
||||||
|
|
||||||
// Interaktion
|
// Interaktion
|
||||||
content.createInteraction()
|
content.createInteraction()
|
||||||
// Löschen
|
// Löschen
|
||||||
if (data["id.provider"]!= "-") {
|
if (data["id.provider"]!= "-") {
|
||||||
var input = content.createInput("button", "delete", "{{.button.delete}}")
|
var input = content.createInput("button", "delete", "Delete")
|
||||||
input.setAttribute('onclick', 'javascript: savePopupData("hdhr", "' + id + '", true, 0)')
|
input.setAttribute('onclick', 'javascript: savePopupData("hdhr", "' + id + '", true, 0)')
|
||||||
input.className = "delete"
|
input.className = "delete"
|
||||||
content.addInteraction(input)
|
content.addInteraction(input)
|
||||||
} else {
|
} else {
|
||||||
var input = content.createInput("button", "back", "{{.button.back}}")
|
var input = content.createInput("button", "back", "Back")
|
||||||
input.setAttribute("onclick", 'javascript: openPopUp("playlist")')
|
input.setAttribute("onclick", 'javascript: openPopUp("playlist")')
|
||||||
content.addInteraction(input)
|
content.addInteraction(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Abbrechen
|
// Abbrechen
|
||||||
var input = content.createInput("button", "cancel", "{{.button.cancel}}")
|
var input = content.createInput("button", "cancel", "Cancel")
|
||||||
input.setAttribute("onclick", 'javascript: showElement("popup", false);')
|
input.setAttribute("onclick", 'javascript: showElement("popup", false);')
|
||||||
content.addInteraction(input)
|
content.addInteraction(input)
|
||||||
|
|
||||||
// Aktualisieren
|
// Aktualisieren
|
||||||
if (data["id.provider"]!= "-") {
|
if (data["id.provider"]!= "-") {
|
||||||
var input = content.createInput("button", "update", "{{.button.update}}")
|
var input = content.createInput("button", "update", "Update")
|
||||||
input.setAttribute('onclick', 'javascript: savePopupData("hdhr", "' + id + '", false, 1)')
|
input.setAttribute('onclick', 'javascript: savePopupData("hdhr", "' + id + '", false, 1)')
|
||||||
content.addInteraction(input)
|
content.addInteraction(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Speichern
|
// Speichern
|
||||||
var input = content.createInput("button", "save", "{{.button.save}}")
|
var input = content.createInput("button", "save", "Save")
|
||||||
input.setAttribute('onclick', 'javascript: savePopupData("hdhr", "' + id + '", false, 0)')
|
input.setAttribute('onclick', 'javascript: savePopupData("hdhr", "' + id + '", false, 0)')
|
||||||
content.addInteraction(input)
|
content.addInteraction(input)
|
||||||
break
|
break
|
||||||
@@ -1516,22 +1516,22 @@ function openPopUp(dataType, element) {
|
|||||||
|
|
||||||
// Type
|
// Type
|
||||||
var dbKey:string = "type"
|
var dbKey:string = "type"
|
||||||
var text:string[] = ["M3U: " + "{{.filter.type.groupTitle}}", "xTeVe: " + "{{.filter.type.customFilter}}"]
|
var text:string[] = ["M3U: " + "Group Title", "xTeVe: " + "Custom Filter"]
|
||||||
var values:string[] = ["javascript: openPopUp('group-title')", "javascript: openPopUp('custom-filter')"]
|
var values:string[] = ["javascript: openPopUp('group-title')", "javascript: openPopUp('custom-filter')"]
|
||||||
var select = content.createSelect(text, values, "javascript: openPopUp('group-title')", dbKey)
|
var select = content.createSelect(text, values, "javascript: openPopUp('group-title')", dbKey)
|
||||||
select.setAttribute("id", id)
|
select.setAttribute("id", id)
|
||||||
select.setAttribute("onchange", 'javascript: changeButtonAction(this, "next", "onclick");') // changeButtonAction
|
select.setAttribute("onchange", 'javascript: changeButtonAction(this, "next", "onclick");') // changeButtonAction
|
||||||
content.appendRow("{{.filter.type.title}}", select)
|
content.appendRow("Type", select)
|
||||||
|
|
||||||
// Interaktion
|
// Interaktion
|
||||||
content.createInteraction()
|
content.createInteraction()
|
||||||
// Abbrechen
|
// Abbrechen
|
||||||
var input = content.createInput("button", "cancel", "{{.button.cancel}}")
|
var input = content.createInput("button", "cancel", "Cancel")
|
||||||
input.setAttribute("onclick", 'javascript: showElement("popup", false);')
|
input.setAttribute("onclick", 'javascript: showElement("popup", false);')
|
||||||
content.addInteraction(input)
|
content.addInteraction(input)
|
||||||
|
|
||||||
// Weiter
|
// Weiter
|
||||||
var input = content.createInput("button", "next", "{{.button.next}}")
|
var input = content.createInput("button", "next", "Next")
|
||||||
input.setAttribute("onclick", 'javascript: openPopUp("group-title")')
|
input.setAttribute("onclick", 'javascript: openPopUp("group-title")')
|
||||||
input.setAttribute("id", 'next')
|
input.setAttribute("id", 'next')
|
||||||
content.addInteraction(input)
|
content.addInteraction(input)
|
||||||
@@ -1542,25 +1542,25 @@ function openPopUp(dataType, element) {
|
|||||||
|
|
||||||
switch (dataType) {
|
switch (dataType) {
|
||||||
case "custom-filter":
|
case "custom-filter":
|
||||||
content.createHeadline("{{.filter.custom}}")
|
content.createHeadline("Custom")
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "group-title":
|
case "group-title":
|
||||||
content.createHeadline("{{.filter.group}}")
|
content.createHeadline("Group")
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Name
|
// Name
|
||||||
var dbKey:string = "name"
|
var dbKey:string = "name"
|
||||||
var input = content.createInput("text", dbKey, data[dbKey])
|
var input = content.createInput("text", dbKey, data[dbKey])
|
||||||
input.setAttribute("placeholder", "{{.filter.name.placeholder}}")
|
input.setAttribute("placeholder", "Filter name")
|
||||||
content.appendRow("{{.filter.name.title}}", input)
|
content.appendRow("Filter Name", input)
|
||||||
|
|
||||||
// Beschreibung
|
// Beschreibung
|
||||||
var dbKey:string = "description"
|
var dbKey:string = "description"
|
||||||
var input = content.createInput("text", dbKey, data[dbKey])
|
var input = content.createInput("text", dbKey, data[dbKey])
|
||||||
input.setAttribute("placeholder", "{{.filter.description.placeholder}}")
|
input.setAttribute("placeholder", "Description")
|
||||||
content.appendRow("{{.filter.description.title}}", input)
|
content.appendRow("Description", input)
|
||||||
|
|
||||||
// Typ
|
// Typ
|
||||||
var dbKey:string = "type"
|
var dbKey:string = "type"
|
||||||
@@ -1576,13 +1576,13 @@ function openPopUp(dataType, element) {
|
|||||||
var dbKey:string = "caseSensitive"
|
var dbKey:string = "caseSensitive"
|
||||||
var input = content.createCheckbox(dbKey)
|
var input = content.createCheckbox(dbKey)
|
||||||
input.checked = data[dbKey]
|
input.checked = data[dbKey]
|
||||||
content.appendRow("{{.filter.caseSensitive.title}}", input)
|
content.appendRow("Case Sensitive", input)
|
||||||
|
|
||||||
// Filterregel (Benutzerdefiniert)
|
// Filterregel (Benutzerdefiniert)
|
||||||
var dbKey:string = "filter"
|
var dbKey:string = "filter"
|
||||||
var input = content.createInput("text", dbKey, data[dbKey])
|
var input = content.createInput("text", dbKey, data[dbKey])
|
||||||
input.setAttribute("placeholder", "{{.filter.filterRule.placeholder}}")
|
input.setAttribute("placeholder", "Sport {HD} !{ES,IT}")
|
||||||
content.appendRow("{{.filter.filterRule.title}}", input)
|
content.appendRow("Filter Rule", input)
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -1596,28 +1596,28 @@ function openPopUp(dataType, element) {
|
|||||||
|
|
||||||
var select = content.createSelect(text, values, data[dbKey], dbKey)
|
var select = content.createSelect(text, values, data[dbKey], dbKey)
|
||||||
select.setAttribute("onchange", "javascript: this.className = 'changed'")
|
select.setAttribute("onchange", "javascript: this.className = 'changed'")
|
||||||
content.appendRow("{{.filter.filterGroup.title}}", select)
|
content.appendRow("Group Title", select)
|
||||||
content.description("{{.filter.filterGroup.description}}")
|
content.description("Select a M3U group. (Counter)<br>Changing the group title in the M3U invalidates the filter.")
|
||||||
|
|
||||||
// Groß- Kleinschreibung beachten
|
// Groß- Kleinschreibung beachten
|
||||||
var dbKey:string = "caseSensitive"
|
var dbKey:string = "caseSensitive"
|
||||||
var input = content.createCheckbox(dbKey)
|
var input = content.createCheckbox(dbKey)
|
||||||
input.checked = data[dbKey]
|
input.checked = data[dbKey]
|
||||||
content.appendRow("{{.filter.caseSensitive.title}}", input)
|
content.appendRow("Case Sensitive", input)
|
||||||
|
|
||||||
|
|
||||||
var dbKey:string = "include"
|
var dbKey:string = "include"
|
||||||
var input = content.createInput("text", dbKey, data[dbKey])
|
var input = content.createInput("text", dbKey, data[dbKey])
|
||||||
input.setAttribute("placeholder", "{{.filter.include.placeholder}}")
|
input.setAttribute("placeholder", "FHD,UHD")
|
||||||
|
|
||||||
content.appendRow("{{.filter.include.title}}", input)
|
content.appendRow("Include", input)
|
||||||
content.description("{{.filter.include.description}}")
|
content.description("Channel name must include.<br>(Comma separated) Comma means or")
|
||||||
|
|
||||||
var dbKey:string = "exclude"
|
var dbKey:string = "exclude"
|
||||||
var input = content.createInput("text", dbKey, data[dbKey])
|
var input = content.createInput("text", dbKey, data[dbKey])
|
||||||
input.setAttribute("placeholder", "{{.filter.exclude.placeholder}}")
|
input.setAttribute("placeholder", "ES,IT")
|
||||||
content.appendRow("{{.filter.exclude.title}}", input)
|
content.appendRow("Exclude", input)
|
||||||
content.description("{{.filter.exclude.description}}")
|
content.description("Channel name must not contain.<br>(Comma separated) Comma means or")
|
||||||
|
|
||||||
break
|
break
|
||||||
|
|
||||||
@@ -1629,18 +1629,18 @@ function openPopUp(dataType, element) {
|
|||||||
content.createInteraction()
|
content.createInteraction()
|
||||||
|
|
||||||
// Löschen
|
// Löschen
|
||||||
var input = content.createInput("button", "delete", "{{.button.delete}}")
|
var input = content.createInput("button", "delete", "Delete")
|
||||||
input.setAttribute('onclick', 'javascript: savePopupData("filter", "' + id + '", true, 0)')
|
input.setAttribute('onclick', 'javascript: savePopupData("filter", "' + id + '", true, 0)')
|
||||||
input.className = "delete"
|
input.className = "delete"
|
||||||
content.addInteraction(input)
|
content.addInteraction(input)
|
||||||
|
|
||||||
// Abbrechen
|
// Abbrechen
|
||||||
var input = content.createInput("button", "cancel", "{{.button.cancel}}")
|
var input = content.createInput("button", "cancel", "Cancel")
|
||||||
input.setAttribute("onclick", 'javascript: showElement("popup", false);')
|
input.setAttribute("onclick", 'javascript: showElement("popup", false);')
|
||||||
content.addInteraction(input)
|
content.addInteraction(input)
|
||||||
|
|
||||||
// Speichern
|
// Speichern
|
||||||
var input = content.createInput("button", "save", "{{.button.save}}")
|
var input = content.createInput("button", "save", "Save")
|
||||||
input.setAttribute('onclick', 'javascript: savePopupData("filter", "' + id + '", false, 0)')
|
input.setAttribute('onclick', 'javascript: savePopupData("filter", "' + id + '", false, 0)')
|
||||||
content.addInteraction(input)
|
content.addInteraction(input)
|
||||||
|
|
||||||
@@ -1651,68 +1651,68 @@ function openPopUp(dataType, element) {
|
|||||||
// Name
|
// Name
|
||||||
var dbKey:string = "name"
|
var dbKey:string = "name"
|
||||||
var input = content.createInput("text", dbKey, data[dbKey])
|
var input = content.createInput("text", dbKey, data[dbKey])
|
||||||
input.setAttribute("placeholder", "{{.xmltv.name.placeholder}}")
|
input.setAttribute("placeholder", "Guide name")
|
||||||
content.appendRow("{{.xmltv.name.title}}", input)
|
content.appendRow("Name", input)
|
||||||
|
|
||||||
// Beschreibung
|
// Beschreibung
|
||||||
var dbKey:string = "description"
|
var dbKey:string = "description"
|
||||||
var input = content.createInput("text", dbKey, data[dbKey])
|
var input = content.createInput("text", dbKey, data[dbKey])
|
||||||
input.setAttribute("placeholder", "{{.xmltv.description.placeholder}}")
|
input.setAttribute("placeholder", "Description")
|
||||||
content.appendRow("{{.xmltv.description.title}}", input)
|
content.appendRow("Description", input)
|
||||||
|
|
||||||
// URL
|
// URL
|
||||||
var dbKey:string = "file.source"
|
var dbKey:string = "file.source"
|
||||||
var input = content.createInput("text", dbKey, data[dbKey])
|
var input = content.createInput("text", dbKey, data[dbKey])
|
||||||
input.setAttribute("placeholder", "{{.xmltv.fileXMLTV.placeholder}}")
|
input.setAttribute("placeholder", "File path or URL of the XMLTV")
|
||||||
content.appendRow("{{.xmltv.fileXMLTV.title}}", input)
|
content.appendRow("XMLTV File", input)
|
||||||
|
|
||||||
// Interaktion
|
// Interaktion
|
||||||
content.createInteraction()
|
content.createInteraction()
|
||||||
// Löschen
|
// Löschen
|
||||||
if (data["id.provider"]!= "-") {
|
if (data["id.provider"]!= "-") {
|
||||||
var input = content.createInput("button", "delete", "{{.button.delete}}")
|
var input = content.createInput("button", "delete", "Delete")
|
||||||
input.setAttribute('onclick', 'javascript: savePopupData("xmltv", "' + id + '", true, 0)')
|
input.setAttribute('onclick', 'javascript: savePopupData("xmltv", "' + id + '", true, 0)')
|
||||||
input.className = "delete"
|
input.className = "delete"
|
||||||
content.addInteraction(input)
|
content.addInteraction(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Abbrechen
|
// Abbrechen
|
||||||
var input = content.createInput("button", "cancel", "{{.button.cancel}}")
|
var input = content.createInput("button", "cancel", "Cancel")
|
||||||
input.setAttribute("onclick", 'javascript: showElement("popup", false);')
|
input.setAttribute("onclick", 'javascript: showElement("popup", false);')
|
||||||
content.addInteraction(input)
|
content.addInteraction(input)
|
||||||
|
|
||||||
// Aktualisieren
|
// Aktualisieren
|
||||||
if (data["id.provider"]!= "-") {
|
if (data["id.provider"]!= "-") {
|
||||||
var input = content.createInput("button", "update", "{{.button.update}}")
|
var input = content.createInput("button", "update", "Update")
|
||||||
input.setAttribute('onclick', 'javascript: savePopupData("xmltv", "' + id + '", false, 1)')
|
input.setAttribute('onclick', 'javascript: savePopupData("xmltv", "' + id + '", false, 1)')
|
||||||
content.addInteraction(input)
|
content.addInteraction(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Speichern
|
// Speichern
|
||||||
var input = content.createInput("button", "save", "{{.button.save}}")
|
var input = content.createInput("button", "save", "Save")
|
||||||
input.setAttribute('onclick', 'javascript: savePopupData("xmltv", "' + id + '", false, 0)')
|
input.setAttribute('onclick', 'javascript: savePopupData("xmltv", "' + id + '", false, 0)')
|
||||||
content.addInteraction(input)
|
content.addInteraction(input)
|
||||||
break
|
break
|
||||||
|
|
||||||
case "users":
|
case "users":
|
||||||
content.createHeadline("{{.mainMenu.item.users}}")
|
content.createHeadline("Users")
|
||||||
// Benutzername
|
// Benutzername
|
||||||
var dbKey:string = "username"
|
var dbKey:string = "username"
|
||||||
var input = content.createInput("text", dbKey, data[dbKey])
|
var input = content.createInput("text", dbKey, data[dbKey])
|
||||||
input.setAttribute("placeholder", "{{.users.username.placeholder}}")
|
input.setAttribute("placeholder", "Username")
|
||||||
content.appendRow("{{.users.username.title}}", input)
|
content.appendRow("Username", input)
|
||||||
|
|
||||||
// Neues Passwort
|
// Neues Passwort
|
||||||
var dbKey:string = "password"
|
var dbKey:string = "password"
|
||||||
var input = content.createInput("password", dbKey, "")
|
var input = content.createInput("password", dbKey, "")
|
||||||
input.setAttribute("placeholder", "{{.users.password.placeholder}}")
|
input.setAttribute("placeholder", "Password")
|
||||||
content.appendRow("{{.users.password.title}}", input)
|
content.appendRow("Password", input)
|
||||||
|
|
||||||
// Bestätigung
|
// Bestätigung
|
||||||
var dbKey:string = "confirm"
|
var dbKey:string = "confirm"
|
||||||
var input = content.createInput("password", dbKey, "")
|
var input = content.createInput("password", dbKey, "")
|
||||||
input.setAttribute("placeholder", "{{.users.confirm.placeholder}}")
|
input.setAttribute("placeholder", "Password confirm")
|
||||||
content.appendRow("{{.users.confirm.title}}", input)
|
content.appendRow("Confirm", input)
|
||||||
|
|
||||||
// Berechtigung WEB
|
// Berechtigung WEB
|
||||||
var dbKey:string = "authentication.web"
|
var dbKey:string = "authentication.web"
|
||||||
@@ -1721,57 +1721,57 @@ function openPopUp(dataType, element) {
|
|||||||
if (data["defaultUser"] == true) {
|
if (data["defaultUser"] == true) {
|
||||||
input.setAttribute("onclick", "javascript: return false")
|
input.setAttribute("onclick", "javascript: return false")
|
||||||
}
|
}
|
||||||
content.appendRow("{{.users.web.title}}", input)
|
content.appendRow("Web Access", input)
|
||||||
|
|
||||||
// Berechtigung PMS
|
// Berechtigung PMS
|
||||||
var dbKey:string = "authentication.pms"
|
var dbKey:string = "authentication.pms"
|
||||||
var input = content.createCheckbox(dbKey)
|
var input = content.createCheckbox(dbKey)
|
||||||
input.checked = data[dbKey]
|
input.checked = data[dbKey]
|
||||||
content.appendRow("{{.users.pms.title}}", input)
|
content.appendRow("PMS Access", input)
|
||||||
|
|
||||||
// Berechtigung M3U
|
// Berechtigung M3U
|
||||||
var dbKey:string = "authentication.m3u"
|
var dbKey:string = "authentication.m3u"
|
||||||
var input = content.createCheckbox(dbKey)
|
var input = content.createCheckbox(dbKey)
|
||||||
input.checked = data[dbKey]
|
input.checked = data[dbKey]
|
||||||
content.appendRow("{{.users.m3u.title}}", input)
|
content.appendRow("M3U Access", input)
|
||||||
|
|
||||||
// Berechtigung XML
|
// Berechtigung XML
|
||||||
var dbKey:string = "authentication.xml"
|
var dbKey:string = "authentication.xml"
|
||||||
var input = content.createCheckbox(dbKey)
|
var input = content.createCheckbox(dbKey)
|
||||||
input.checked = data[dbKey]
|
input.checked = data[dbKey]
|
||||||
content.appendRow("{{.users.xml.title}}", input)
|
content.appendRow("XML Access", input)
|
||||||
|
|
||||||
// Berechtigung API
|
// Berechtigung API
|
||||||
var dbKey:string = "authentication.api"
|
var dbKey:string = "authentication.api"
|
||||||
var input = content.createCheckbox(dbKey)
|
var input = content.createCheckbox(dbKey)
|
||||||
input.checked = data[dbKey]
|
input.checked = data[dbKey]
|
||||||
content.appendRow("{{.users.api.title}}", input)
|
content.appendRow("API Access", input)
|
||||||
|
|
||||||
// Interaktion
|
// Interaktion
|
||||||
content.createInteraction()
|
content.createInteraction()
|
||||||
|
|
||||||
// Löschen
|
// Löschen
|
||||||
if (data["defaultUser"]!= true && id != "-") {
|
if (data["defaultUser"]!= true && id != "-") {
|
||||||
var input = content.createInput("button", "delete", "{{.button.delete}}")
|
var input = content.createInput("button", "delete", "Delete")
|
||||||
input.className = "delete"
|
input.className = "delete"
|
||||||
input.setAttribute('onclick', 'javascript: savePopupData("' + dataType + '", "' + id + '", true, 0)')
|
input.setAttribute('onclick', 'javascript: savePopupData("' + dataType + '", "' + id + '", true, 0)')
|
||||||
content.addInteraction(input)
|
content.addInteraction(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Abbrechen
|
// Abbrechen
|
||||||
var input = content.createInput("button", "cancel", "{{.button.cancel}}")
|
var input = content.createInput("button", "cancel", "Cancel")
|
||||||
input.setAttribute("onclick", 'javascript: showElement("popup", false);')
|
input.setAttribute("onclick", 'javascript: showElement("popup", false);')
|
||||||
content.addInteraction(input)
|
content.addInteraction(input)
|
||||||
|
|
||||||
// Speichern
|
// Speichern
|
||||||
var input = content.createInput("button", "save", "{{.button.save}}")
|
var input = content.createInput("button", "save", "Save")
|
||||||
input.setAttribute("onclick", 'javascript: savePopupData("' + dataType + '", "' + id + '", "false");')
|
input.setAttribute("onclick", 'javascript: savePopupData("' + dataType + '", "' + id + '", "false");')
|
||||||
content.addInteraction(input)
|
content.addInteraction(input)
|
||||||
|
|
||||||
break
|
break
|
||||||
|
|
||||||
case "mapping":
|
case "mapping":
|
||||||
content.createHeadline("{{.mainMenu.item.mapping}}")
|
content.createHeadline("Mapping")
|
||||||
// Aktiv
|
// Aktiv
|
||||||
var dbKey:string = "x-active"
|
var dbKey:string = "x-active"
|
||||||
var input = content.createCheckbox(dbKey)
|
var input = content.createCheckbox(dbKey)
|
||||||
@@ -1779,7 +1779,7 @@ function openPopUp(dataType, element) {
|
|||||||
input.id = "active"
|
input.id = "active"
|
||||||
//input.setAttribute("onchange", "javascript: this.className = 'changed'")
|
//input.setAttribute("onchange", "javascript: this.className = 'changed'")
|
||||||
input.setAttribute("onchange", "javascript: toggleChannelStatus('" + id + "', this)")
|
input.setAttribute("onchange", "javascript: toggleChannelStatus('" + id + "', this)")
|
||||||
content.appendRow("{{.mapping.active.title}}", input)
|
content.appendRow("Active", input)
|
||||||
|
|
||||||
// Kanalname
|
// Kanalname
|
||||||
var dbKey:string = "x-name"
|
var dbKey:string = "x-name"
|
||||||
@@ -1789,16 +1789,16 @@ function openPopUp(dataType, element) {
|
|||||||
input.style.border = "solid 1px red"
|
input.style.border = "solid 1px red"
|
||||||
input.setAttribute("readonly", "true")
|
input.setAttribute("readonly", "true")
|
||||||
}
|
}
|
||||||
content.appendRow("{{.mapping.channelName.title}}", input)
|
content.appendRow("Channel Name", input)
|
||||||
|
|
||||||
content.description(data["name"])
|
content.description(data["name"])
|
||||||
|
|
||||||
// Beschreibung
|
// Beschreibung
|
||||||
var dbKey:string = "x-description"
|
var dbKey:string = "x-description"
|
||||||
var input = content.createInput("text", dbKey, data[dbKey])
|
var input = content.createInput("text", dbKey, data[dbKey])
|
||||||
input.setAttribute("placeholder", "{{.mapping.description.placeholder}}")
|
input.setAttribute("placeholder", "Used by the Dummy as an XML description")
|
||||||
input.setAttribute("onchange", "javascript: this.className = 'changed'")
|
input.setAttribute("onchange", "javascript: this.className = 'changed'")
|
||||||
content.appendRow("{{.mapping.description.title}}", input)
|
content.appendRow("Channel Description", input)
|
||||||
|
|
||||||
// Aktualisierung des Kanalnamens
|
// Aktualisierung des Kanalnamens
|
||||||
if (data.hasOwnProperty("_uuid.key")) {
|
if (data.hasOwnProperty("_uuid.key")) {
|
||||||
@@ -1807,7 +1807,7 @@ function openPopUp(dataType, element) {
|
|||||||
var input = content.createCheckbox(dbKey)
|
var input = content.createCheckbox(dbKey)
|
||||||
input.setAttribute("onchange", "javascript: this.className = 'changed'")
|
input.setAttribute("onchange", "javascript: this.className = 'changed'")
|
||||||
input.checked = data[dbKey]
|
input.checked = data[dbKey]
|
||||||
content.appendRow("{{.mapping.updateChannelName.title}}", input)
|
content.appendRow("Update Channel Name", input)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1816,7 +1816,7 @@ function openPopUp(dataType, element) {
|
|||||||
var input = content.createInput("text", dbKey, data[dbKey])
|
var input = content.createInput("text", dbKey, data[dbKey])
|
||||||
input.setAttribute("onchange", "javascript: this.className = 'changed'")
|
input.setAttribute("onchange", "javascript: this.className = 'changed'")
|
||||||
input.setAttribute("id", "channel-icon")
|
input.setAttribute("id", "channel-icon")
|
||||||
content.appendRow("{{.mapping.channelLogo.title}}", input)
|
content.appendRow("Logo URL", input)
|
||||||
|
|
||||||
// Aktualisierung des Kanallogos
|
// Aktualisierung des Kanallogos
|
||||||
var dbKey:string = "x-update-channel-icon"
|
var dbKey:string = "x-update-channel-icon"
|
||||||
@@ -1824,7 +1824,7 @@ function openPopUp(dataType, element) {
|
|||||||
input.checked = data[dbKey]
|
input.checked = data[dbKey]
|
||||||
input.setAttribute("id", "update-icon")
|
input.setAttribute("id", "update-icon")
|
||||||
input.setAttribute("onchange", "javascript: this.className = 'changed'; changeChannelLogo('" + id + "');")
|
input.setAttribute("onchange", "javascript: this.className = 'changed'; changeChannelLogo('" + id + "');")
|
||||||
content.appendRow("{{.mapping.updateChannelLogo.title}}", input)
|
content.appendRow("Update Channel Logo", input)
|
||||||
|
|
||||||
// Erweitern der EPG Kategorie
|
// Erweitern der EPG Kategorie
|
||||||
var dbKey:string = "x-category"
|
var dbKey:string = "x-category"
|
||||||
@@ -1832,13 +1832,13 @@ function openPopUp(dataType, element) {
|
|||||||
var values:string[] = ["", "Kids", "News", "Movie", "Series", "Sports"]
|
var values:string[] = ["", "Kids", "News", "Movie", "Series", "Sports"]
|
||||||
var select = content.createSelect(text, values, data[dbKey], dbKey)
|
var select = content.createSelect(text, values, data[dbKey], dbKey)
|
||||||
select.setAttribute("onchange", "javascript: this.className = 'changed'")
|
select.setAttribute("onchange", "javascript: this.className = 'changed'")
|
||||||
content.appendRow("{{.mapping.epgCategory.title}}", select)
|
content.appendRow("EPG Category", select)
|
||||||
|
|
||||||
// M3U Gruppentitel
|
// M3U Gruppentitel
|
||||||
var dbKey:string = "x-group-title"
|
var dbKey:string = "x-group-title"
|
||||||
var input = content.createInput("text", dbKey, data[dbKey])
|
var input = content.createInput("text", dbKey, data[dbKey])
|
||||||
input.setAttribute("onchange", "javascript: this.className = 'changed'")
|
input.setAttribute("onchange", "javascript: this.className = 'changed'")
|
||||||
content.appendRow("{{.mapping.m3uGroupTitle.title}}", input)
|
content.appendRow("Group Title (xteve.m3u)", input)
|
||||||
|
|
||||||
if (data["group-title"] != undefined) {
|
if (data["group-title"] != undefined) {
|
||||||
content.description(data["group-title"])
|
content.description(data["group-title"])
|
||||||
@@ -1852,7 +1852,7 @@ function openPopUp(dataType, element) {
|
|||||||
select.setAttribute("name", dbKey)
|
select.setAttribute("name", dbKey)
|
||||||
select.setAttribute("id", "popup-xmltv")
|
select.setAttribute("id", "popup-xmltv")
|
||||||
select.setAttribute("onchange", "javascript: this.className = 'changed'; setXmltvChannel('" + id + "',this);")
|
select.setAttribute("onchange", "javascript: this.className = 'changed'; setXmltvChannel('" + id + "',this);")
|
||||||
content.appendRow("{{.mapping.xmltvFile.title}}", select)
|
content.appendRow("XMLTV File", select)
|
||||||
var file = data[dbKey]
|
var file = data[dbKey]
|
||||||
|
|
||||||
// XMLTV Mapping
|
// XMLTV Mapping
|
||||||
@@ -1864,18 +1864,18 @@ function openPopUp(dataType, element) {
|
|||||||
select.setAttribute("onchange", "javascript: this.className = 'changed'; checkXmltvChannel('" + id + "',this,'" + xmlFile + "');")
|
select.setAttribute("onchange", "javascript: this.className = 'changed'; checkXmltvChannel('" + id + "',this,'" + xmlFile + "');")
|
||||||
|
|
||||||
sortSelect(select)
|
sortSelect(select)
|
||||||
content.appendRow("{{.mapping.xmltvChannel.title}}", select)
|
content.appendRow("XMLTV Channel", select)
|
||||||
|
|
||||||
// Interaktion
|
// Interaktion
|
||||||
content.createInteraction()
|
content.createInteraction()
|
||||||
|
|
||||||
// Logo hochladen
|
// Logo hochladen
|
||||||
var input = content.createInput("button", "cancel", "{{.button.uploadLogo}}")
|
var input = content.createInput("button", "cancel", "Upload Logo")
|
||||||
input.setAttribute("onclick", 'javascript: uploadLogo();')
|
input.setAttribute("onclick", 'javascript: uploadLogo();')
|
||||||
content.addInteraction(input)
|
content.addInteraction(input)
|
||||||
|
|
||||||
// Abbrechen
|
// Abbrechen
|
||||||
var input = content.createInput("button", "cancel", "{{.button.cancel}}")
|
var input = content.createInput("button", "cancel", "Cancel")
|
||||||
input.setAttribute("onclick", 'javascript: showElement("popup", false);')
|
input.setAttribute("onclick", 'javascript: showElement("popup", false);')
|
||||||
content.addInteraction(input)
|
content.addInteraction(input)
|
||||||
|
|
||||||
@@ -1886,7 +1886,7 @@ function openPopUp(dataType, element) {
|
|||||||
ids.push(id)
|
ids.push(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
var input = content.createInput("button", "save", "{{.button.done}}")
|
var input = content.createInput("button", "save", "Done")
|
||||||
input.setAttribute("onclick", 'javascript: donePopupData("' + dataType + '", "' + ids + '", "false");')
|
input.setAttribute("onclick", 'javascript: donePopupData("' + dataType + '", "' + ids + '", "false");')
|
||||||
content.addInteraction(input)
|
content.addInteraction(input)
|
||||||
break
|
break
|
||||||
|
|||||||
+70
-70
@@ -21,11 +21,11 @@ class SettingsCategory {
|
|||||||
// Texteingaben
|
// Texteingaben
|
||||||
case "update":
|
case "update":
|
||||||
var tdLeft = document.createElement("TD")
|
var tdLeft = document.createElement("TD")
|
||||||
tdLeft.innerHTML = "{{.settings.update.title}}" + ":"
|
tdLeft.innerHTML = "Schedule for updating (Playlist, XMLTV, Backup)" + ":"
|
||||||
|
|
||||||
var tdRight = document.createElement("TD")
|
var tdRight = document.createElement("TD")
|
||||||
var input = content.createInput("text", "update", data.toString())
|
var input = content.createInput("text", "update", data.toString())
|
||||||
input.setAttribute("placeholder", "{{.settings.update.placeholder}}")
|
input.setAttribute("placeholder", "0000,1000,2000")
|
||||||
input.setAttribute("onchange", "javascript: this.className = 'changed'")
|
input.setAttribute("onchange", "javascript: this.className = 'changed'")
|
||||||
tdRight.appendChild(input)
|
tdRight.appendChild(input)
|
||||||
|
|
||||||
@@ -35,11 +35,11 @@ class SettingsCategory {
|
|||||||
|
|
||||||
case "backup.path":
|
case "backup.path":
|
||||||
var tdLeft = document.createElement("TD")
|
var tdLeft = document.createElement("TD")
|
||||||
tdLeft.innerHTML = "{{.settings.backupPath.title}}" + ":"
|
tdLeft.innerHTML = "Location for automatic backups" + ":"
|
||||||
|
|
||||||
var tdRight = document.createElement("TD")
|
var tdRight = document.createElement("TD")
|
||||||
var input = content.createInput("text", "backup.path", data)
|
var input = content.createInput("text", "backup.path", data)
|
||||||
input.setAttribute("placeholder", "{{.settings.backupPath.placeholder}}")
|
input.setAttribute("placeholder", "/mnt/data/backup/xteve/")
|
||||||
input.setAttribute("onchange", "javascript: this.className = 'changed'")
|
input.setAttribute("onchange", "javascript: this.className = 'changed'")
|
||||||
tdRight.appendChild(input)
|
tdRight.appendChild(input)
|
||||||
|
|
||||||
@@ -49,11 +49,11 @@ class SettingsCategory {
|
|||||||
|
|
||||||
case "temp.path":
|
case "temp.path":
|
||||||
var tdLeft = document.createElement("TD")
|
var tdLeft = document.createElement("TD")
|
||||||
tdLeft.innerHTML = "{{.settings.tempPath.title}}" + ":"
|
tdLeft.innerHTML = "Location for the temporary files" + ":"
|
||||||
|
|
||||||
var tdRight = document.createElement("TD")
|
var tdRight = document.createElement("TD")
|
||||||
var input = content.createInput("text", "temp.path", data)
|
var input = content.createInput("text", "temp.path", data)
|
||||||
input.setAttribute("placeholder", "{{.settings.tmpPath.placeholder}}")
|
input.setAttribute("placeholder", "/tmp/xteve/")
|
||||||
input.setAttribute("onchange", "javascript: this.className = 'changed'")
|
input.setAttribute("onchange", "javascript: this.className = 'changed'")
|
||||||
tdRight.appendChild(input)
|
tdRight.appendChild(input)
|
||||||
|
|
||||||
@@ -63,11 +63,11 @@ class SettingsCategory {
|
|||||||
|
|
||||||
case "user.agent":
|
case "user.agent":
|
||||||
var tdLeft = document.createElement("TD")
|
var tdLeft = document.createElement("TD")
|
||||||
tdLeft.innerHTML = "{{.settings.userAgent.title}}" + ":"
|
tdLeft.innerHTML = "User Agent" + ":"
|
||||||
|
|
||||||
var tdRight = document.createElement("TD")
|
var tdRight = document.createElement("TD")
|
||||||
var input = content.createInput("text", "user.agent", data)
|
var input = content.createInput("text", "user.agent", data)
|
||||||
input.setAttribute("placeholder", "{{.settings.userAgent.placeholder}}")
|
input.setAttribute("placeholder", "xTeVe")
|
||||||
input.setAttribute("onchange", "javascript: this.className = 'changed'")
|
input.setAttribute("onchange", "javascript: this.className = 'changed'")
|
||||||
tdRight.appendChild(input)
|
tdRight.appendChild(input)
|
||||||
|
|
||||||
@@ -77,11 +77,11 @@ class SettingsCategory {
|
|||||||
|
|
||||||
case "plex.url":
|
case "plex.url":
|
||||||
var tdLeft = document.createElement("TD")
|
var tdLeft = document.createElement("TD")
|
||||||
tdLeft.innerHTML = "{{.settings.plexURL.title}}" + ":"
|
tdLeft.innerHTML = "Plex Server URL" + ":"
|
||||||
|
|
||||||
var tdRight = document.createElement("TD")
|
var tdRight = document.createElement("TD")
|
||||||
var input = content.createInput("text", "plex.url", data)
|
var input = content.createInput("text", "plex.url", data)
|
||||||
input.setAttribute("placeholder", "{{.settings.plexURL.placeholder}}")
|
input.setAttribute("placeholder", "http://plex-host:32400")
|
||||||
input.setAttribute("onchange", "javascript: this.className = 'changed'")
|
input.setAttribute("onchange", "javascript: this.className = 'changed'")
|
||||||
tdRight.appendChild(input)
|
tdRight.appendChild(input)
|
||||||
|
|
||||||
@@ -91,11 +91,11 @@ class SettingsCategory {
|
|||||||
|
|
||||||
case "plex.token":
|
case "plex.token":
|
||||||
var tdLeft = document.createElement("TD")
|
var tdLeft = document.createElement("TD")
|
||||||
tdLeft.innerHTML = "{{.settings.plexToken.title}}" + ":"
|
tdLeft.innerHTML = "Plex API Token" + ":"
|
||||||
|
|
||||||
var tdRight = document.createElement("TD")
|
var tdRight = document.createElement("TD")
|
||||||
var input = content.createInput("password", "plex.token", data)
|
var input = content.createInput("password", "plex.token", data)
|
||||||
input.setAttribute("placeholder", "{{.settings.plexToken.placeholder}}")
|
input.setAttribute("placeholder", "Plex X-Plex-Token")
|
||||||
input.setAttribute("autocomplete", "off")
|
input.setAttribute("autocomplete", "off")
|
||||||
input.setAttribute("onchange", "javascript: this.className = 'changed'")
|
input.setAttribute("onchange", "javascript: this.className = 'changed'")
|
||||||
tdRight.appendChild(input)
|
tdRight.appendChild(input)
|
||||||
@@ -106,11 +106,11 @@ class SettingsCategory {
|
|||||||
|
|
||||||
case "buffer.timeout":
|
case "buffer.timeout":
|
||||||
var tdLeft = document.createElement("TD")
|
var tdLeft = document.createElement("TD")
|
||||||
tdLeft.innerHTML = "{{.settings.bufferTimeout.title}}" + ":"
|
tdLeft.innerHTML = "Timeout for new client connections" + ":"
|
||||||
|
|
||||||
var tdRight = document.createElement("TD")
|
var tdRight = document.createElement("TD")
|
||||||
var input = content.createInput("text", "buffer.timeout", data)
|
var input = content.createInput("text", "buffer.timeout", data)
|
||||||
input.setAttribute("placeholder", "{{.settings.bufferTimeout.placeholder}}")
|
input.setAttribute("placeholder", "100")
|
||||||
input.setAttribute("onchange", "javascript: this.className = 'changed'")
|
input.setAttribute("onchange", "javascript: this.className = 'changed'")
|
||||||
tdRight.appendChild(input)
|
tdRight.appendChild(input)
|
||||||
|
|
||||||
@@ -120,11 +120,11 @@ class SettingsCategory {
|
|||||||
|
|
||||||
case "ffmpeg.path":
|
case "ffmpeg.path":
|
||||||
var tdLeft = document.createElement("TD")
|
var tdLeft = document.createElement("TD")
|
||||||
tdLeft.innerHTML = "{{.settings.ffmpegPath.title}}" + ":"
|
tdLeft.innerHTML = "FFmpeg Binary Path" + ":"
|
||||||
|
|
||||||
var tdRight = document.createElement("TD")
|
var tdRight = document.createElement("TD")
|
||||||
var input = content.createInput("text", "ffmpeg.path", data)
|
var input = content.createInput("text", "ffmpeg.path", data)
|
||||||
input.setAttribute("placeholder", "{{.settings.ffmpegPath.placeholder}}")
|
input.setAttribute("placeholder", "/path/to/ffmpeg")
|
||||||
input.setAttribute("onchange", "javascript: this.className = 'changed'")
|
input.setAttribute("onchange", "javascript: this.className = 'changed'")
|
||||||
tdRight.appendChild(input)
|
tdRight.appendChild(input)
|
||||||
|
|
||||||
@@ -134,11 +134,11 @@ class SettingsCategory {
|
|||||||
|
|
||||||
case "ffmpeg.options":
|
case "ffmpeg.options":
|
||||||
var tdLeft = document.createElement("TD")
|
var tdLeft = document.createElement("TD")
|
||||||
tdLeft.innerHTML = "{{.settings.ffmpegOptions.title}}" + ":"
|
tdLeft.innerHTML = "FFmpeg Options" + ":"
|
||||||
|
|
||||||
var tdRight = document.createElement("TD")
|
var tdRight = document.createElement("TD")
|
||||||
var input = content.createInput("text", "ffmpeg.options", data)
|
var input = content.createInput("text", "ffmpeg.options", data)
|
||||||
input.setAttribute("placeholder", "{{.settings.ffmpegOptions.placeholder}}")
|
input.setAttribute("placeholder", "Leave blank to set default settings")
|
||||||
input.setAttribute("onchange", "javascript: this.className = 'changed'")
|
input.setAttribute("onchange", "javascript: this.className = 'changed'")
|
||||||
tdRight.appendChild(input)
|
tdRight.appendChild(input)
|
||||||
|
|
||||||
@@ -148,11 +148,11 @@ class SettingsCategory {
|
|||||||
|
|
||||||
case "vlc.path":
|
case "vlc.path":
|
||||||
var tdLeft = document.createElement("TD")
|
var tdLeft = document.createElement("TD")
|
||||||
tdLeft.innerHTML = "{{.settings.vlcPath.title}}" + ":"
|
tdLeft.innerHTML = "VLC / CVLC Binary Path" + ":"
|
||||||
|
|
||||||
var tdRight = document.createElement("TD")
|
var tdRight = document.createElement("TD")
|
||||||
var input = content.createInput("text", "vlc.path", data)
|
var input = content.createInput("text", "vlc.path", data)
|
||||||
input.setAttribute("placeholder", "{{.settings.vlcPath.placeholder}}")
|
input.setAttribute("placeholder", "/path/to/cvlc")
|
||||||
input.setAttribute("onchange", "javascript: this.className = 'changed'")
|
input.setAttribute("onchange", "javascript: this.className = 'changed'")
|
||||||
tdRight.appendChild(input)
|
tdRight.appendChild(input)
|
||||||
|
|
||||||
@@ -162,11 +162,11 @@ class SettingsCategory {
|
|||||||
|
|
||||||
case "vlc.options":
|
case "vlc.options":
|
||||||
var tdLeft = document.createElement("TD")
|
var tdLeft = document.createElement("TD")
|
||||||
tdLeft.innerHTML = "{{.settings.vlcOptions.title}}" + ":"
|
tdLeft.innerHTML = "VLC / CVLC Options" + ":"
|
||||||
|
|
||||||
var tdRight = document.createElement("TD")
|
var tdRight = document.createElement("TD")
|
||||||
var input = content.createInput("text", "vlc.options", data)
|
var input = content.createInput("text", "vlc.options", data)
|
||||||
input.setAttribute("placeholder", "{{.settings.vlcOptions.placeholder}}")
|
input.setAttribute("placeholder", "Leave blank to set default settings")
|
||||||
input.setAttribute("onchange", "javascript: this.className = 'changed'")
|
input.setAttribute("onchange", "javascript: this.className = 'changed'")
|
||||||
tdRight.appendChild(input)
|
tdRight.appendChild(input)
|
||||||
|
|
||||||
@@ -177,7 +177,7 @@ class SettingsCategory {
|
|||||||
// Checkboxen
|
// Checkboxen
|
||||||
case "authentication.web":
|
case "authentication.web":
|
||||||
var tdLeft = document.createElement("TD")
|
var tdLeft = document.createElement("TD")
|
||||||
tdLeft.innerHTML = "{{.settings.authenticationWEB.title}}" + ":"
|
tdLeft.innerHTML = "WEB Authentication" + ":"
|
||||||
|
|
||||||
var tdRight = document.createElement("TD")
|
var tdRight = document.createElement("TD")
|
||||||
var input = content.createCheckbox(settingsKey)
|
var input = content.createCheckbox(settingsKey)
|
||||||
@@ -191,7 +191,7 @@ class SettingsCategory {
|
|||||||
|
|
||||||
case "authentication.pms":
|
case "authentication.pms":
|
||||||
var tdLeft = document.createElement("TD")
|
var tdLeft = document.createElement("TD")
|
||||||
tdLeft.innerHTML = "{{.settings.authenticationPMS.title}}" + ":"
|
tdLeft.innerHTML = "PMS Authentication" + ":"
|
||||||
|
|
||||||
var tdRight = document.createElement("TD")
|
var tdRight = document.createElement("TD")
|
||||||
var input = content.createCheckbox(settingsKey)
|
var input = content.createCheckbox(settingsKey)
|
||||||
@@ -205,7 +205,7 @@ class SettingsCategory {
|
|||||||
|
|
||||||
case "authentication.m3u":
|
case "authentication.m3u":
|
||||||
var tdLeft = document.createElement("TD")
|
var tdLeft = document.createElement("TD")
|
||||||
tdLeft.innerHTML = "{{.settings.authenticationM3U.title}}" + ":"
|
tdLeft.innerHTML = "M3U Authentication" + ":"
|
||||||
|
|
||||||
var tdRight = document.createElement("TD")
|
var tdRight = document.createElement("TD")
|
||||||
var input = content.createCheckbox(settingsKey)
|
var input = content.createCheckbox(settingsKey)
|
||||||
@@ -219,7 +219,7 @@ class SettingsCategory {
|
|||||||
|
|
||||||
case "authentication.xml":
|
case "authentication.xml":
|
||||||
var tdLeft = document.createElement("TD")
|
var tdLeft = document.createElement("TD")
|
||||||
tdLeft.innerHTML = "{{.settings.authenticationXML.title}}" + ":"
|
tdLeft.innerHTML = "XML Authentication" + ":"
|
||||||
|
|
||||||
var tdRight = document.createElement("TD")
|
var tdRight = document.createElement("TD")
|
||||||
var input = content.createCheckbox(settingsKey)
|
var input = content.createCheckbox(settingsKey)
|
||||||
@@ -233,7 +233,7 @@ class SettingsCategory {
|
|||||||
|
|
||||||
case "authentication.api":
|
case "authentication.api":
|
||||||
var tdLeft = document.createElement("TD")
|
var tdLeft = document.createElement("TD")
|
||||||
tdLeft.innerHTML = "{{.settings.authenticationAPI.title}}" + ":"
|
tdLeft.innerHTML = "API Authentication" + ":"
|
||||||
|
|
||||||
var tdRight = document.createElement("TD")
|
var tdRight = document.createElement("TD")
|
||||||
var input = content.createCheckbox(settingsKey)
|
var input = content.createCheckbox(settingsKey)
|
||||||
@@ -247,7 +247,7 @@ class SettingsCategory {
|
|||||||
|
|
||||||
case "files.update":
|
case "files.update":
|
||||||
var tdLeft = document.createElement("TD")
|
var tdLeft = document.createElement("TD")
|
||||||
tdLeft.innerHTML = "{{.settings.filesUpdate.title}}" + ":"
|
tdLeft.innerHTML = "Updates all files at startup" + ":"
|
||||||
|
|
||||||
var tdRight = document.createElement("TD")
|
var tdRight = document.createElement("TD")
|
||||||
var input = content.createCheckbox(settingsKey)
|
var input = content.createCheckbox(settingsKey)
|
||||||
@@ -261,7 +261,7 @@ class SettingsCategory {
|
|||||||
|
|
||||||
case "cache.images":
|
case "cache.images":
|
||||||
var tdLeft = document.createElement("TD")
|
var tdLeft = document.createElement("TD")
|
||||||
tdLeft.innerHTML = "{{.settings.cacheImages.title}}" + ":"
|
tdLeft.innerHTML = "Image Caching" + ":"
|
||||||
|
|
||||||
var tdRight = document.createElement("TD")
|
var tdRight = document.createElement("TD")
|
||||||
var input = content.createCheckbox(settingsKey)
|
var input = content.createCheckbox(settingsKey)
|
||||||
@@ -275,7 +275,7 @@ class SettingsCategory {
|
|||||||
|
|
||||||
case "xepg.replace.missing.images":
|
case "xepg.replace.missing.images":
|
||||||
var tdLeft = document.createElement("TD")
|
var tdLeft = document.createElement("TD")
|
||||||
tdLeft.innerHTML = "{{.settings.replaceEmptyImages.title}}" + ":"
|
tdLeft.innerHTML = "Replace missing program images" + ":"
|
||||||
|
|
||||||
var tdRight = document.createElement("TD")
|
var tdRight = document.createElement("TD")
|
||||||
var input = content.createCheckbox(settingsKey)
|
var input = content.createCheckbox(settingsKey)
|
||||||
@@ -289,7 +289,7 @@ class SettingsCategory {
|
|||||||
|
|
||||||
case "api":
|
case "api":
|
||||||
var tdLeft = document.createElement("TD")
|
var tdLeft = document.createElement("TD")
|
||||||
tdLeft.innerHTML = "{{.settings.api.title}}" + ":"
|
tdLeft.innerHTML = "API Interface" + ":"
|
||||||
|
|
||||||
var tdRight = document.createElement("TD")
|
var tdRight = document.createElement("TD")
|
||||||
var input = content.createCheckbox(settingsKey)
|
var input = content.createCheckbox(settingsKey)
|
||||||
@@ -303,7 +303,7 @@ class SettingsCategory {
|
|||||||
|
|
||||||
case "use_plexAPI":
|
case "use_plexAPI":
|
||||||
var tdLeft = document.createElement("TD")
|
var tdLeft = document.createElement("TD")
|
||||||
tdLeft.innerHTML = "{{.settings.usePlexAPI.title}}" + ":"
|
tdLeft.innerHTML = "Use Plex API Refresh" + ":"
|
||||||
|
|
||||||
var tdRight = document.createElement("TD")
|
var tdRight = document.createElement("TD")
|
||||||
var input = content.createCheckbox(settingsKey)
|
var input = content.createCheckbox(settingsKey)
|
||||||
@@ -318,10 +318,10 @@ class SettingsCategory {
|
|||||||
// Select
|
// Select
|
||||||
case "xepg.missing.epg.mode":
|
case "xepg.missing.epg.mode":
|
||||||
var tdLeft = document.createElement("TD")
|
var tdLeft = document.createElement("TD")
|
||||||
tdLeft.innerHTML = "{{.settings.xepgMissingEPGMode.title}}" + ":"
|
tdLeft.innerHTML = "Missing EPG Handling" + ":"
|
||||||
|
|
||||||
var tdRight = document.createElement("TD")
|
var tdRight = document.createElement("TD")
|
||||||
var text:any[] = ["{{.settings.xepgMissingEPGMode.info_strict}}", "{{.settings.xepgMissingEPGMode.info_relaxed}}"]
|
var text:any[] = ["Strict (deactivate channels)", "Relaxed (keep active / dummy guide)"]
|
||||||
var values:any[] = ["strict", "relaxed"]
|
var values:any[] = ["strict", "relaxed"]
|
||||||
|
|
||||||
var select = content.createSelect(text, values, data, settingsKey)
|
var select = content.createSelect(text, values, data, settingsKey)
|
||||||
@@ -334,7 +334,7 @@ class SettingsCategory {
|
|||||||
|
|
||||||
case "tuner":
|
case "tuner":
|
||||||
var tdLeft = document.createElement("TD")
|
var tdLeft = document.createElement("TD")
|
||||||
tdLeft.innerHTML = "{{.settings.tuner.title}}" + ":"
|
tdLeft.innerHTML = "Number of Tuners" + ":"
|
||||||
|
|
||||||
var tdRight = document.createElement("TD")
|
var tdRight = document.createElement("TD")
|
||||||
var text = new Array()
|
var text = new Array()
|
||||||
@@ -355,7 +355,7 @@ class SettingsCategory {
|
|||||||
|
|
||||||
case "epgSource":
|
case "epgSource":
|
||||||
var tdLeft = document.createElement("TD")
|
var tdLeft = document.createElement("TD")
|
||||||
tdLeft.innerHTML = "{{.settings.epgSource.title}}" + ":"
|
tdLeft.innerHTML = "EPG Source" + ":"
|
||||||
|
|
||||||
var tdRight = document.createElement("TD")
|
var tdRight = document.createElement("TD")
|
||||||
var text:any[] = ["PMS", "XEPG"]
|
var text:any[] = ["PMS", "XEPG"]
|
||||||
@@ -371,7 +371,7 @@ class SettingsCategory {
|
|||||||
|
|
||||||
case "backup.keep":
|
case "backup.keep":
|
||||||
var tdLeft = document.createElement("TD")
|
var tdLeft = document.createElement("TD")
|
||||||
tdLeft.innerHTML = "{{.settings.backupKeep.title}}" + ":"
|
tdLeft.innerHTML = "Number of backups to keep" + ":"
|
||||||
|
|
||||||
var tdRight = document.createElement("TD")
|
var tdRight = document.createElement("TD")
|
||||||
var text:any[] = ["5", "10", "20", "30", "40", "50"]
|
var text:any[] = ["5", "10", "20", "30", "40", "50"]
|
||||||
@@ -387,7 +387,7 @@ class SettingsCategory {
|
|||||||
|
|
||||||
case "buffer.size.kb":
|
case "buffer.size.kb":
|
||||||
var tdLeft = document.createElement("TD")
|
var tdLeft = document.createElement("TD")
|
||||||
tdLeft.innerHTML = "{{.settings.bufferSize.title}}" + ":"
|
tdLeft.innerHTML = "Buffer Size" + ":"
|
||||||
|
|
||||||
var tdRight = document.createElement("TD")
|
var tdRight = document.createElement("TD")
|
||||||
var text:any[] = ["0.5 MB", "1 MB", "2 MB", "3 MB", "4 MB", "5 MB", "6 MB", "7 MB", "8 MB"]
|
var text:any[] = ["0.5 MB", "1 MB", "2 MB", "3 MB", "4 MB", "5 MB", "6 MB", "7 MB", "8 MB"]
|
||||||
@@ -403,10 +403,10 @@ class SettingsCategory {
|
|||||||
|
|
||||||
case "buffer":
|
case "buffer":
|
||||||
var tdLeft = document.createElement("TD")
|
var tdLeft = document.createElement("TD")
|
||||||
tdLeft.innerHTML = "{{.settings.streamBuffering.title}}" + ":"
|
tdLeft.innerHTML = "Stream Buffer" + ":"
|
||||||
|
|
||||||
var tdRight = document.createElement("TD")
|
var tdRight = document.createElement("TD")
|
||||||
var text:any[] = ["{{.settings.streamBuffering.info_false}}", "xTeVe: ({{.settings.streamBuffering.info_xteve}})", "FFmpeg: ({{.settings.streamBuffering.info_ffmpeg}})", "VLC: ({{.settings.streamBuffering.info_vlc}})"]
|
var text:any[] = ["No Buffer (Client connects to the streaming server)", "xTeVe: (xTeVe connects to the streaming server)", "FFmpeg: (FFmpeg connects to the streaming server)", "VLC: (VLC connects to the streaming server)"]
|
||||||
var values:any[] = ["-", "xteve", "ffmpeg", "vlc"]
|
var values:any[] = ["-", "xteve", "ffmpeg", "vlc"]
|
||||||
|
|
||||||
var select = content.createSelect(text, values, data, settingsKey)
|
var select = content.createSelect(text, values, data, settingsKey)
|
||||||
@@ -420,11 +420,11 @@ class SettingsCategory {
|
|||||||
case "udpxy":
|
case "udpxy":
|
||||||
|
|
||||||
var tdLeft = document.createElement("TD");
|
var tdLeft = document.createElement("TD");
|
||||||
tdLeft.innerHTML = "{{.settings.udpxy.title}}" + ":"
|
tdLeft.innerHTML = "UDPxy address" + ":"
|
||||||
|
|
||||||
var tdRight = document.createElement("TD")
|
var tdRight = document.createElement("TD")
|
||||||
var input = content.createInput("text", "udpxy", data)
|
var input = content.createInput("text", "udpxy", data)
|
||||||
input.setAttribute("placeholder", "{{.settings.udpxy.placeholder}}")
|
input.setAttribute("placeholder", "host:port")
|
||||||
input.setAttribute("onchange", "javascript: this.className = 'changed'")
|
input.setAttribute("onchange", "javascript: this.className = 'changed'")
|
||||||
tdRight.appendChild(input)
|
tdRight.appendChild(input)
|
||||||
|
|
||||||
@@ -446,117 +446,117 @@ class SettingsCategory {
|
|||||||
switch (settingsKey) {
|
switch (settingsKey) {
|
||||||
|
|
||||||
case "authentication.web":
|
case "authentication.web":
|
||||||
text = "{{.settings.authenticationWEB.description}}"
|
text = "Access to the web interface only possible with credentials."
|
||||||
break
|
break
|
||||||
|
|
||||||
case "authentication.m3u":
|
case "authentication.m3u":
|
||||||
text = "{{.settings.authenticationM3U.description}}"
|
text = "Downloading the xteve.m3u file via an HTTP request is only possible with authentication."
|
||||||
break
|
break
|
||||||
|
|
||||||
case "authentication.pms":
|
case "authentication.pms":
|
||||||
text = "{{.settings.authenticationPMS.description}}"
|
text = "Plex requests are only possible with authentication. <br><b>Warning!!!</b> After activating this function xTeVe must be delete in the PMS DVR settings and set up again."
|
||||||
break
|
break
|
||||||
|
|
||||||
case "authentication.xml":
|
case "authentication.xml":
|
||||||
text = "{{.settings.authenticationXML.description}}"
|
text = "Downloading the xteve.xml file via an HTTP request is only possible with authentication"
|
||||||
break
|
break
|
||||||
|
|
||||||
case "authentication.api":
|
case "authentication.api":
|
||||||
if (SERVER["settings"]["authentication.web"] == true) {
|
if (SERVER["settings"]["authentication.web"] == true) {
|
||||||
text = "{{.settings.authenticationAPI.description}}"
|
text = "Access to the API interface is only possible with authentication."
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
|
||||||
case "backup.keep":
|
case "backup.keep":
|
||||||
text = "{{.settings.backupKeep.description}}"
|
text = "Number of backups to keep. Older backups are automatically deleted."
|
||||||
break
|
break
|
||||||
|
|
||||||
case "backup.path":
|
case "backup.path":
|
||||||
text = "{{.settings.backupPath.description}}"
|
text = "Before any update of the provider data by the schedule, xTeVe creates a backup. The path for the automatic backups can be changed. xTeVe requires write permission for this folder."
|
||||||
break
|
break
|
||||||
|
|
||||||
case "temp.path":
|
case "temp.path":
|
||||||
text = "{{.settings.tempPath.description}}"
|
text = "Location for the buffer files."
|
||||||
break
|
break
|
||||||
|
|
||||||
case "buffer":
|
case "buffer":
|
||||||
text = "{{.settings.streamBuffering.description}}"
|
text = "Functions of the buffer:<br>- The stream is passed from xTeVe, FFmpeg or VLC to Plex, Emby or M3U Player<br>- Small jerking of the streams can be compensated<br>- HLS / M3U8 support<br>- RTP / RTPS support (only FFmpeg or VLC)<br>- Re-streaming<br>- Separate tuner limit for each playlist"
|
||||||
break
|
break
|
||||||
|
|
||||||
case "buffer.size.kb":
|
case "buffer.size.kb":
|
||||||
text = "{{.settings.bufferSize.description}}"
|
text = "Buffer size in MB.<br>M3U8: If the TS segment smaller then the buffer size, the file size of the segment is used."
|
||||||
break
|
break
|
||||||
|
|
||||||
case "buffer.timeout":
|
case "buffer.timeout":
|
||||||
text = "{{.settings.bufferTimeout.description}}"
|
text = "The xTeVe buffer waits until new client connections are established. Helpful for fast channel switching. Value in milliseconds."
|
||||||
break
|
break
|
||||||
|
|
||||||
case "user.agent":
|
case "user.agent":
|
||||||
text = "{{.settings.userAgent.description}}"
|
text = "User Agent for HTTP requests. For every HTTP connection, this value is used for the user agent. Should only be changed if xTeVe is blocked."
|
||||||
break
|
break
|
||||||
|
|
||||||
case "plex.url":
|
case "plex.url":
|
||||||
text = "{{.settings.plexURL.description}}"
|
text = "Base URL of your Plex server. Example: http://192.168.1.10:32400"
|
||||||
break
|
break
|
||||||
|
|
||||||
case "plex.token":
|
case "plex.token":
|
||||||
text = "{{.settings.plexToken.description}}"
|
text = "Plex token used for authenticated API calls to refresh your DVR guide."
|
||||||
break
|
break
|
||||||
|
|
||||||
case "ffmpeg.path":
|
case "ffmpeg.path":
|
||||||
text = "{{.settings.ffmpegPath.description}}"
|
text = "Path to FFmpeg binary."
|
||||||
break
|
break
|
||||||
|
|
||||||
case "ffmpeg.options":
|
case "ffmpeg.options":
|
||||||
text = "{{.settings.ffmpegOptions.description}}"
|
text = "FFmpeg options.<br>Only change if you know what you are doing.<br>Leave blank to set default settings."
|
||||||
break
|
break
|
||||||
|
|
||||||
case "vlc.path":
|
case "vlc.path":
|
||||||
text = "{{.settings.vlcPath.description}}"
|
text = "Path to VLC / CVLC binary."
|
||||||
break
|
break
|
||||||
|
|
||||||
case "vlc.options":
|
case "vlc.options":
|
||||||
text = "{{.settings.vlcOptions.description}}"
|
text = "VLC / CVLC options.<br>Only change if you know what you are doing.<br>Leave blank to set default settings."
|
||||||
break
|
break
|
||||||
|
|
||||||
case "epgSource":
|
case "epgSource":
|
||||||
text = "{{.settings.epgSource.description}}"
|
text = "PMS:<br>- Use EPG data from Plex or Emby <br><br>XEPG:<br>- Use of one or more XMLTV files<br>- Channel management<br>- M3U / XMLTV export (HTTP link for IPTV apps)"
|
||||||
break
|
break
|
||||||
|
|
||||||
case "tuner":
|
case "tuner":
|
||||||
text = "{{.settings.tuner.description}}"
|
text = "Number of parallel connections that can be established to the provider.<br>Available for: Plex, Emby (HDHR), M3U (with active buffer).<br>After a change, xTeVe must be delete in the Plex / Emby DVR settings and set up again."
|
||||||
break
|
break
|
||||||
|
|
||||||
case "update":
|
case "update":
|
||||||
text = "{{.settings.update.description}}"
|
text = "Time in 24 hour format (0800 = 8:00 am). More times can be entered comma separated. Leave this field empty if no updates are to be carried out."
|
||||||
break
|
break
|
||||||
|
|
||||||
case "api":
|
case "api":
|
||||||
text = "{{.settings.api.description}}"
|
text = "Via API interface it is possible to send commands to xTeVe. API documentation is <a href='https://github.com/xteve-project/xTeVe-Documentation/blob/master/en/configuration.md#api'>here</a>"
|
||||||
break
|
break
|
||||||
|
|
||||||
case "use_plexAPI":
|
case "use_plexAPI":
|
||||||
text = "{{.settings.usePlexAPI.description}}"
|
text = "When enabled, xTeVe calls Plex directly to refresh DVR guide data after lineup or XEPG updates."
|
||||||
break
|
break
|
||||||
|
|
||||||
case "files.update":
|
case "files.update":
|
||||||
text = "{{.settings.filesUpdate.description}}"
|
text = "Updates all playlists, tuner and XMLTV files at startup."
|
||||||
break
|
break
|
||||||
|
|
||||||
case "cache.images":
|
case "cache.images":
|
||||||
text = "{{.settings.cacheImages.description}}"
|
text = "All images from the XMLTV file are cached, allowing faster rendering of the grid in the client.<br>Downloading the images may take a while and will be done in the background."
|
||||||
break
|
break
|
||||||
|
|
||||||
case "xepg.replace.missing.images":
|
case "xepg.replace.missing.images":
|
||||||
text = "{{.settings.replaceEmptyImages.description}}"
|
text = "If the poster in the XMLTV program is missing, the channel logo will be used."
|
||||||
break
|
break
|
||||||
|
|
||||||
case "xepg.missing.epg.mode":
|
case "xepg.missing.epg.mode":
|
||||||
text = "{{.settings.xepgMissingEPGMode.description}}"
|
text = "Strict: channels are deactivated when XMLTV mappings disappear.<br>Relaxed: channels stay active with last-known mappings and fall back to a dummy guide whenever guide data cannot be resolved."
|
||||||
break
|
break
|
||||||
|
|
||||||
case "udpxy":
|
case "udpxy":
|
||||||
text = "{{.settings.udpxy.description}}"
|
text = "The address of your UDPxy server. If set, and the channel URLs in the m3u is multicast, xTeVe will rewrite it so that it is accessed via the UDPxy service."
|
||||||
break
|
break
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2020",
|
||||||
|
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||||
|
"module": "None",
|
||||||
|
"outFile": "../html/js/app.js",
|
||||||
|
"strict": false,
|
||||||
|
"noEmitOnError": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"newLine": "lf",
|
||||||
|
"removeComments": false
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"network_ts.ts",
|
||||||
|
"menu_ts.ts",
|
||||||
|
"settings_ts.ts",
|
||||||
|
"logs_ts.ts",
|
||||||
|
"base_ts.ts",
|
||||||
|
"configuration_ts.ts",
|
||||||
|
"authentication_ts.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user