v2.0.0.0000
No files matched your search
@@ -0,0 +1,7 @@
|
|||||||
|
.DS_Store
|
||||||
|
compiler
|
||||||
|
files
|
||||||
|
update_xteve*.sh
|
||||||
|
xteve
|
||||||
|
xteve.exe
|
||||||
|
de.json
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2019 xteve-project
|
Copyright (c) 2019 marmei@xteve-project
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
# Information for the developers will come soon
|
||||||
@@ -1 +1,127 @@
|
|||||||
|
<div align="center" style="background-color: #111; padding: 100;">
|
||||||
|
<a href="https://github.com/xteve-project/xTeVe"><img width="600" height="200" src="html/img/logo_w_600x200.png" alt="xTeVe" /></a>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
|
||||||
# xTeVe
|
# xTeVe
|
||||||
|
## M3U Proxy for Plex DVR and Emby Live TV.
|
||||||
|
|
||||||
|
Documentation for setup and configuration is [here](https://github.com/xteve-project/xTeVe-Documentation/blob/master/en/configuration.md).
|
||||||
|
|
||||||
|
#### Donation
|
||||||
|
* **Bitcoin:** 1c1iCe4CJPfNUXtqxKBbW2Qd2EtqRPWme
|
||||||
|

|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
### Plex
|
||||||
|
* Plex Media Server (1.11.1.4730 or newer)
|
||||||
|
* Plex Client with DVR support
|
||||||
|
* Plex Pass
|
||||||
|
|
||||||
|
### Emby
|
||||||
|
* Emby Server (3.5.3.0 or newer)
|
||||||
|
* Emby Client with Live-TV support
|
||||||
|
* Emby Premiere
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
#### Files
|
||||||
|
* Merge external M3U files
|
||||||
|
* Merge external XMLTV files
|
||||||
|
* Automatic M3U and XMLTV update
|
||||||
|
* M3U und XMLTV export
|
||||||
|
|
||||||
|
#### Channel management
|
||||||
|
* Filtering streams
|
||||||
|
* Channel mapping
|
||||||
|
* Channel order
|
||||||
|
* Channel logos
|
||||||
|
* Channel categories
|
||||||
|
|
||||||
|
#### Streaming
|
||||||
|
* Buffer with HLS / M3U8 support
|
||||||
|
* Re-streaming
|
||||||
|
* Number of tuners adjustable
|
||||||
|
* Compatible with Plex / Emby EPG
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Downloads v2 | 64 Bit only
|
||||||
|
#### 64 Bit Intel / AMD
|
||||||
|
|
||||||
|
* [Windows](https://github.com/xteve-project/xTeVe-Downloads/blob/master/xteve_windows_amd64.zip?raw=true)
|
||||||
|
* [OS X](https://github.com/xteve-project/xTeVe-Downloads/blob/master/xteve_darwin_amd64.zip?raw=true)
|
||||||
|
* [Linux](https://github.com/xteve-project/xTeVe-Downloads/blob/master/xteve_linux_amd64.zip?raw=true)
|
||||||
|
* [FreeBSD](https://github.com/xteve-project/xTeVe-Downloads/blob/master/xteve_freebsd_amd64.zip?raw=true)
|
||||||
|
|
||||||
|
#### 64 Bit ARM
|
||||||
|
* [Linux](https://github.com/xteve-project/xTeVe-Downloads/blob/master/xteve_linux_arm64.zip?raw=true)
|
||||||
|
|
||||||
|
#### Recommended Docker Image (Linux 64 Bit)
|
||||||
|
Thanks to @alturismo and @LeeD for creating the Docker Images.
|
||||||
|
|
||||||
|
**Created by alturismo:**
|
||||||
|
[xTeVe](https://hub.docker.com/r/alturismo/xteve)
|
||||||
|
[xTeVe / Guide2go](https://hub.docker.com/r/alturismo/xteve_guide2go)
|
||||||
|
|
||||||
|
Including:
|
||||||
|
- Guide2go: XMLTV grabber for Schedules Direct
|
||||||
|
|
||||||
|
|
||||||
|
**Created by LeeD:**
|
||||||
|
[xTeVe / Guide2go / Zap2XML](https://hub.docker.com/r/dnsforge/xteve)
|
||||||
|
|
||||||
|
Including:
|
||||||
|
- Guide2go: XMLTV grabber for Schedules Direct
|
||||||
|
- Zap2XML: Perl based zap2it XMLTV grabber
|
||||||
|
- Bash: A Unix / Linux shell
|
||||||
|
- Crond: Daemon to execute scheduled commands
|
||||||
|
- Perl: Programming language
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Build from source code [Go / Golang]
|
||||||
|
|
||||||
|
#### Requirements
|
||||||
|
* Go (go1.12.4 or newer)
|
||||||
|
|
||||||
|
#### Dependancys
|
||||||
|
* [go-ssdp](https://github.com/koron/go-ssdp)
|
||||||
|
* [websocket](https://github.com/gorilla/websocket)
|
||||||
|
* [osext](https://github.com/kardianos/osext)
|
||||||
|
|
||||||
|
#### Build
|
||||||
|
1. Download source code
|
||||||
|
2. Install dependancys
|
||||||
|
```
|
||||||
|
go get github.com/koron/go-ssdp
|
||||||
|
go get github.com/gorilla/websocket
|
||||||
|
go get github.com/kardianos/osext
|
||||||
|
```
|
||||||
|
3. Build xTeVe
|
||||||
|
```
|
||||||
|
go build xteve.go
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Fork without pull request :mega:
|
||||||
|
When creating a fork, the xTeVe GitHub account must be changed from the sorce code or the update function disabled.
|
||||||
|
Future updates of the xteve-project would update your fork. :wink:
|
||||||
|
|
||||||
|
xteve.go - Line: 29
|
||||||
|
```Go
|
||||||
|
var GitHub = GitHubStruct{Branch: "master", User: "xteve-project", Repo: "xTeVe-Downloads", Update: true}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Branch: GitHub Branch
|
||||||
|
User: GitHub Username
|
||||||
|
Repo: GitHub Repository
|
||||||
|
Update: Automatic updates from the GitHub repository [true|false]
|
||||||
|
*/
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>xTeVe</title>
|
||||||
|
<link rel="stylesheet" href="css/screen.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 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>
|
||||||
|
|
||||||
|
<body onload="javascript: readyForConfiguration(0);">
|
||||||
|
|
||||||
|
<div id="loading" class="block">
|
||||||
|
<div class="loader"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="header" class="imgCenter"></div>
|
||||||
|
<div id="box">
|
||||||
|
|
||||||
|
<table id="clientInfo" class="visible">
|
||||||
|
<tr>
|
||||||
|
<td class="tdKey">Version:</td>
|
||||||
|
<td id="version" class="tdVal"> </td>
|
||||||
|
<td class="tdKey">OS:</td>
|
||||||
|
<td id="os" class="tdVal"> </td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="tdKey">UUID:</td>
|
||||||
|
<td id="uuid" class="tdVal"> </td>
|
||||||
|
<td class="tdKey">Arch:</td>
|
||||||
|
<td id="arch" class="tdVal"> </td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="tdKey">Streams:</td>
|
||||||
|
<td id="streams" class="tdVal"> </td>
|
||||||
|
<td class="tdKey">DVR:</td>
|
||||||
|
<td id="DVR" class="tdVal"> </td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div id="headline">
|
||||||
|
<h1 id="head-text" class="center">Configuration</h1>
|
||||||
|
</div>
|
||||||
|
<p id="err" class="errorMsg center"></p>
|
||||||
|
<div id="content">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div id="box-footer">
|
||||||
|
<input id="next" class="" type="button" name="next" value="Next" onclick="javascript: saveWizard();">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>xTeVe</title>
|
||||||
|
<link rel="stylesheet" href="css/screen.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 language="javascript" type="text/javascript" src="js/authentication_ts.js"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div id="header" class="imgCenter"></div>
|
||||||
|
|
||||||
|
<div id="box">
|
||||||
|
|
||||||
|
<div id="headline">
|
||||||
|
<h1 id="head-text" class="center">{{.account.headline}}</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p id="err" class="errorMsg center"></p>
|
||||||
|
|
||||||
|
<div id="content">
|
||||||
|
|
||||||
|
<form id="authentication" action="/web/" method="post">
|
||||||
|
|
||||||
|
<h5>{{.account.username.title}}:</h5>
|
||||||
|
<input id="username" type="text" name="username" placeholder="Username" value="">
|
||||||
|
<h5>{{.account.password.title}}:</h5>
|
||||||
|
<input id="password" type="password" name="password" placeholder="Password" value="">
|
||||||
|
<h5>{{.account.confirm.title}}:</h5>
|
||||||
|
<input id="confirm" type="password" name="confirm" placeholder="Confirm" value="">
|
||||||
|
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="box-footer">
|
||||||
|
<input id="submit" class="" type="button" value="{{.button.craeteAccount}}" onclick="javascript: login();">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,448 @@
|
|||||||
|
* {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
-moz-appearance: none;
|
||||||
|
-ms-appearance: none;
|
||||||
|
font-family: "Arial", sans-serif;
|
||||||
|
letter-spacing: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
::-webkit-scrollbar-track {
|
||||||
|
-webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3);
|
||||||
|
border-radius: 5px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
border-radius: 5px;
|
||||||
|
-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0,0.6);
|
||||||
|
background-color: #444;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-corner {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #00E6FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
html, body {
|
||||||
|
color: #fff;
|
||||||
|
margin: 0px auto;
|
||||||
|
height: 100%;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 24px;
|
||||||
|
letter-spacing: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: 22px;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
font-size: 20px;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
line-height: 1.5em;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
h5 {
|
||||||
|
font-size: 16px;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
line-height: 1.2em;
|
||||||
|
margin: 25px 0px 10px 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
border: 0;
|
||||||
|
height: 1px;
|
||||||
|
background: #333;
|
||||||
|
margin: 10px 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 2px;
|
||||||
|
padding: 2px 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
margin: 0px 0px 5px 0px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #ddd;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 12px;
|
||||||
|
font-style: normal;
|
||||||
|
font-variant: normal;
|
||||||
|
line-height: 1.6em;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
list-style-type: none;
|
||||||
|
background-color: #111;
|
||||||
|
padding: 10px 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
border-left: solid 2px #111;
|
||||||
|
transition: all 0.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
li:hover {
|
||||||
|
border-color: #00E6FF
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
cursor: pointer;
|
||||||
|
width: calc(100% + 2px);
|
||||||
|
border: solid 0px #00E6FF;
|
||||||
|
border-radius: 0px;
|
||||||
|
outline: none;
|
||||||
|
color: #fff;
|
||||||
|
padding: 9px 10px;
|
||||||
|
display:block;
|
||||||
|
background-color: #333;
|
||||||
|
font-size: 14px;
|
||||||
|
margin: 5px 0px 5px 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
select:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
margin: 5px 0px;
|
||||||
|
padding: 2.5px 10px;
|
||||||
|
outline: none;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=button], input[type=submit] {
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: #000;
|
||||||
|
margin: 10px 10px;
|
||||||
|
padding: 10px 25px;
|
||||||
|
border: solid 0px;
|
||||||
|
border-color: #000;
|
||||||
|
border-radius: 3px;
|
||||||
|
outline: none;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=button]:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=button]:hover {
|
||||||
|
background-color: #00E6FF;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=button]:hover.delete {
|
||||||
|
background-color: red;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=text], input[type=search], input[type=password] {
|
||||||
|
color: #fff;
|
||||||
|
width: -webkit-calc(100% - 0px);
|
||||||
|
width: -moz-calc(100% - 0px);
|
||||||
|
width: calc(100% - 0px);
|
||||||
|
outline: none;
|
||||||
|
border: solid 1px transparent;
|
||||||
|
background-color: transparent;
|
||||||
|
border-bottom-color: #555;
|
||||||
|
border-radius: 0px;
|
||||||
|
padding: 8px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="checkbox"] {
|
||||||
|
border: solid 1px #00E6FF;
|
||||||
|
background-color: #333;
|
||||||
|
height: 25px;
|
||||||
|
width: 25px;
|
||||||
|
cursor: pointer;
|
||||||
|
/*
|
||||||
|
-webkit-appearance: checkbox;
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="checkbox"]:checked {
|
||||||
|
color: #fff;
|
||||||
|
background-color: #00E6FF;
|
||||||
|
/*display: inline-block;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="checkbox"]:before {
|
||||||
|
position: initial;
|
||||||
|
left: 0px;
|
||||||
|
margin-left: -4px;
|
||||||
|
content: " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="checkbox"]:checked:before {
|
||||||
|
position: initial;
|
||||||
|
left: 0px;
|
||||||
|
margin-left: -3px;
|
||||||
|
content: "✓";
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
input[type=button].cancel {
|
||||||
|
|
||||||
|
background-color: transparent;
|
||||||
|
border-color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=button].save{
|
||||||
|
background-color: #111;
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
input[type=button].black, input[type=submit].black{
|
||||||
|
background-color: #000;
|
||||||
|
border-color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=button].center{
|
||||||
|
margin-right: auto;
|
||||||
|
margin-left: auto;
|
||||||
|
background-color: #000;
|
||||||
|
border-color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pointer {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pointer:hover {
|
||||||
|
color: #00E6FF;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sortThis {
|
||||||
|
color: #00E6FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.w40px {
|
||||||
|
max-width: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.w50px {
|
||||||
|
max-width: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.w80px {
|
||||||
|
max-width: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.w150px {
|
||||||
|
max-width: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.w200px {
|
||||||
|
max-width: 200px;
|
||||||
|
min-width: 100px;
|
||||||
|
width: 200px;
|
||||||
|
overflow-x: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.w300px {
|
||||||
|
max-width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.w220px {
|
||||||
|
max-width: 220px;
|
||||||
|
cursor: alias;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.screenLogHidden {
|
||||||
|
transform: translate(0px, -110px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.borderSpace {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.block {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.none {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.notVisible {
|
||||||
|
height: 0px;
|
||||||
|
display: none;
|
||||||
|
opacity: 0;
|
||||||
|
border-bottom: #000 solid 0px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.visible {
|
||||||
|
opacity: 1;
|
||||||
|
display: block;
|
||||||
|
border-bottom: #444 solid 1px;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.floatRight {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.floatLeft {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-active {
|
||||||
|
background-color: #00E6FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-notActive {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#branch {
|
||||||
|
display: table;
|
||||||
|
margin: auto;
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
#interaction {
|
||||||
|
margin-bottom: 100px;
|
||||||
|
text-align: center;
|
||||||
|
border-bottom: solid 0px #777;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.half {
|
||||||
|
display: block;
|
||||||
|
width: 45%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu {
|
||||||
|
border: solid 1px #00E6FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.infoMsg {
|
||||||
|
color: #aaa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.errorMsg {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.warningMsg {
|
||||||
|
color: yellow;
|
||||||
|
}
|
||||||
|
|
||||||
|
.debugMsg {
|
||||||
|
color: magenta;
|
||||||
|
}
|
||||||
|
|
||||||
|
.News, .Movie, .Series, .Sports, .Kids {
|
||||||
|
border-left: solid 2px
|
||||||
|
}
|
||||||
|
|
||||||
|
.News {
|
||||||
|
border-color: tomato
|
||||||
|
}
|
||||||
|
|
||||||
|
.Movie {
|
||||||
|
border-color: royalblue;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Series {
|
||||||
|
border-color: gold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Sports {
|
||||||
|
border-color: yellowgreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Kids {
|
||||||
|
border-color: mediumpurple;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Loading */
|
||||||
|
#loading {
|
||||||
|
left: 0px;
|
||||||
|
top: 0px;
|
||||||
|
z-index: 10000;
|
||||||
|
position: absolute;
|
||||||
|
background-color: rgba(0,0,0, 0.8);
|
||||||
|
margin: auto;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.loader {
|
||||||
|
border: 5px solid transparent;
|
||||||
|
border-radius: 50%;
|
||||||
|
border-top: 5px solid #00E6FF;
|
||||||
|
border-bottom: 5px solid #00E6FF;
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
-webkit-animation: spin 1.2s linear infinite;
|
||||||
|
animation: spin 1.2s linear infinite;
|
||||||
|
|
||||||
|
position: fixed;
|
||||||
|
margin: auto;
|
||||||
|
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@-webkit-keyframes spin {
|
||||||
|
0% { -webkit-transform: rotate(0deg); }
|
||||||
|
100% { -webkit-transform: rotate(360deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
0% { transform: rotate(0deg); }
|
||||||
|
100% { transform: rotate(360deg); }
|
||||||
|
}
|
||||||
@@ -0,0 +1,540 @@
|
|||||||
|
nav img {
|
||||||
|
display: block;
|
||||||
|
max-height: 20px;
|
||||||
|
max-width: 20px;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav p {
|
||||||
|
text-align: left;
|
||||||
|
padding: 0px 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#layout {
|
||||||
|
display: block;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.layout-left {
|
||||||
|
display: block;
|
||||||
|
min-width: 150px;
|
||||||
|
max-width: 20%;
|
||||||
|
background-color: #111;
|
||||||
|
height: inherit;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layout-right {
|
||||||
|
display: block;
|
||||||
|
background-color: #444;
|
||||||
|
}
|
||||||
|
|
||||||
|
#menu-wrapper {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#logo {
|
||||||
|
display: block;
|
||||||
|
min-width: 180px;
|
||||||
|
width: 100%;
|
||||||
|
height: 100px;
|
||||||
|
background: url("../img/logo_w_600x200.png");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: center;
|
||||||
|
background-size: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#page {
|
||||||
|
max-width: 950px;
|
||||||
|
margin: auto;
|
||||||
|
background-color: #444;
|
||||||
|
|
||||||
|
/*
|
||||||
|
height: -webkit-calc(100% - 130px);
|
||||||
|
height: -moz-calc(100% - 130px);
|
||||||
|
height: calc(100% - 130px);
|
||||||
|
*/
|
||||||
|
|
||||||
|
min-height: -webkit-calc(100% - 120px);
|
||||||
|
min-height: -moz-calc(100% - 120px);
|
||||||
|
min-height: calc(100% - 120px);
|
||||||
|
|
||||||
|
|
||||||
|
box-shadow: 0px 5px 5px #222;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#uiSetting {
|
||||||
|
float: right;
|
||||||
|
margin-right: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#box input[type=text], #box input[type=password] {
|
||||||
|
width: -webkit-calc(100% - 20px);
|
||||||
|
width: -moz-calc(100% - 20px);
|
||||||
|
width: calc(100% - 20px);
|
||||||
|
}
|
||||||
|
|
||||||
|
#box input[type=submit]{
|
||||||
|
margin: 50px auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#settings {
|
||||||
|
display: block;
|
||||||
|
padding: 10px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#settings h5 {
|
||||||
|
margin: 50px 0px 10px 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#content-interaction .search {
|
||||||
|
width: 200px;
|
||||||
|
border: 1px solid #000;
|
||||||
|
padding: 9px;
|
||||||
|
background-color: #333;
|
||||||
|
margin: 10px;
|
||||||
|
float: right;
|
||||||
|
border-radius: 3px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#myStreams {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0px;
|
||||||
|
background-color: #111;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 950px;
|
||||||
|
|
||||||
|
/*
|
||||||
|
max-height: 100px;
|
||||||
|
*/
|
||||||
|
margin-bottom: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#myStreams img {
|
||||||
|
width: 4%;
|
||||||
|
padding: 2px 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
#settings-footer {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Wizard*/
|
||||||
|
#box {
|
||||||
|
background-color: #444;
|
||||||
|
min-height: 400px;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
#box p{
|
||||||
|
padding: 10px 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#box-footer {
|
||||||
|
margin-top: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#box-footer {
|
||||||
|
margin: auto;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#headline {
|
||||||
|
background-color: #222;
|
||||||
|
border-bottom: solid 2px #222;
|
||||||
|
transition: all 0.5s;
|
||||||
|
padding: 10px 0px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
#content {
|
||||||
|
display: block;
|
||||||
|
overflow: auto;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- */
|
||||||
|
|
||||||
|
|
||||||
|
#clientInfo, #activeStreams, #inactiveStreams {
|
||||||
|
font-family: monospace;
|
||||||
|
display: block;
|
||||||
|
font-size: 9px;
|
||||||
|
background-color: #111;
|
||||||
|
color: #00E6FF;
|
||||||
|
border-bottom: solid 0px;;
|
||||||
|
padding: 0px;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
overflow-x: hidden;
|
||||||
|
border-spacing: 4px 4px;
|
||||||
|
border-bottom: solid 1px #444;
|
||||||
|
}
|
||||||
|
|
||||||
|
#myStreamsBox {
|
||||||
|
position: relative;
|
||||||
|
padding: 0px;
|
||||||
|
/*height: 100px;*/
|
||||||
|
max-height: 150px;
|
||||||
|
background-color: #111;
|
||||||
|
color: white;
|
||||||
|
display:flex;
|
||||||
|
justify-content:center;
|
||||||
|
align-items:center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#openStreams {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
float: right;
|
||||||
|
position: absolute;
|
||||||
|
right: 0px;
|
||||||
|
bottom: 0px;
|
||||||
|
background: url("../img/touch.png");
|
||||||
|
background-color: #111;
|
||||||
|
|
||||||
|
background-position: bottom right;
|
||||||
|
}
|
||||||
|
|
||||||
|
#allStreams {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
padding: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#activeStreams, #inactiveStreams {
|
||||||
|
overflow-y: scroll;
|
||||||
|
width: 50%;
|
||||||
|
max-height: 100px;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
#activeStreams .tdKey, #inactiveStreams .tdKey {
|
||||||
|
width: 75px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#inactiveStreams .tdKey {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
#clientInfo .tdVal, #logInfo .tdVal, #activeStreams .tdVal, #inactiveStreams .tdVal, #mappingInfo .tdVal{
|
||||||
|
color: #aaa;
|
||||||
|
white-space: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
#box-wrapper {
|
||||||
|
display: inline-block;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
overflow-y: scroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
#content_table, #mapping-detail-table, #content_table {
|
||||||
|
display: table;
|
||||||
|
|
||||||
|
border-collapse: collapse;
|
||||||
|
overflow-y: scroll;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#content_table .content_table_header {
|
||||||
|
background-color: #333;
|
||||||
|
height: 50px;
|
||||||
|
border-bottom: solid 1px #111;
|
||||||
|
border-left: solid 3px #333;
|
||||||
|
cursor: auto;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
tbody {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.tableEllipsis {
|
||||||
|
width: 150px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
#content_table img {
|
||||||
|
display: block;
|
||||||
|
max-height: 28px;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
max-width: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#content_table tr{
|
||||||
|
border-left: solid 3px 444;
|
||||||
|
border-bottom: solid 1px #333;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
#content_table tr:hover {
|
||||||
|
background-color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
#content_table td {
|
||||||
|
|
||||||
|
padding: 0px 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#content_table input[type=text]{
|
||||||
|
width: 80%;
|
||||||
|
border: 0px;
|
||||||
|
background-color: #333;
|
||||||
|
margin-left: 5px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
#content_table input[type=checkbox]{
|
||||||
|
max-width: 25px;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.showBulk {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hideBulk {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.noBulk {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#content_table tr.activeEPG{
|
||||||
|
border-left: solid 3px lawngreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
#content_table tr.notActiveEPG{
|
||||||
|
border-left: solid 3px red;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#logScreen p{
|
||||||
|
white-space: pre;
|
||||||
|
font-size: 10px;
|
||||||
|
/*
|
||||||
|
line-height: 1.6em;
|
||||||
|
font-family: "Arial", sans-serif;
|
||||||
|
*/
|
||||||
|
letter-spacing: 1px;
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 12px;
|
||||||
|
font-style: normal;
|
||||||
|
font-variant: normal;
|
||||||
|
line-height: 1.6em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#popup {
|
||||||
|
background-color: rgba(0, 0, 0, 0.4);
|
||||||
|
position: fixed;
|
||||||
|
left: 0px;
|
||||||
|
width: 100%;
|
||||||
|
z-index: 100;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mapping-detail, #user-detail, #file-detail, #popup-custom {
|
||||||
|
box-shadow: 0px 5px 40px #000;
|
||||||
|
margin-top: 20px;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
|
||||||
|
max-width: 600px;
|
||||||
|
background-color: #222;
|
||||||
|
padding: 10px;
|
||||||
|
overflow:auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#popup-custom h3 {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#file-detail input[type=text] {
|
||||||
|
width: -webkit-calc(100% - 20px);
|
||||||
|
width: -moz-calc(100% - 20px);
|
||||||
|
width: calc(100% - 20px);
|
||||||
|
}
|
||||||
|
|
||||||
|
#mapping-detail img {
|
||||||
|
display: block;
|
||||||
|
max-height: 30px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#popup-custom input[type=text], #popup-custom input[type=password], #mapping-detail input[type=text], #content_settings input[type=text], #content_settings input[type=password]{
|
||||||
|
border: solid 1px;
|
||||||
|
border-color: transparent;
|
||||||
|
background-color: #333;
|
||||||
|
text-align: left;
|
||||||
|
width: -webkit-calc(100% - 20px);
|
||||||
|
width: -moz-calc(100% - 20px);
|
||||||
|
width: calc(100% - 20px);
|
||||||
|
}
|
||||||
|
|
||||||
|
#popup-custom input[type=text].notAvailable {
|
||||||
|
border-color: red;
|
||||||
|
color: #666;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mapping-detail-table, #user-detail-table {
|
||||||
|
display: inline-table;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#popup-custom table, #content_settings table {
|
||||||
|
display: inline-table;
|
||||||
|
table-layout: fixed;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#mapping-detail-table td, #user-detail-table td {
|
||||||
|
padding: 10px 0px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#mapping-detail-table td.left, #user-detail-table td.left, #popup-custom td.left {
|
||||||
|
width: 38%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.interaction, #interaction {
|
||||||
|
margin-top: 20px;
|
||||||
|
display: inline-flex;
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.interaction input[type=button], .interaction input[type=submit] {
|
||||||
|
background-color: #000;
|
||||||
|
min-width: 100px;
|
||||||
|
margin: 0px 10px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#notification {
|
||||||
|
display: block;
|
||||||
|
position: fixed;
|
||||||
|
right: 0px;
|
||||||
|
height: 100%;
|
||||||
|
width: 250px;
|
||||||
|
|
||||||
|
background-color: #222;
|
||||||
|
box-shadow: 0px 0px 20px #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
#notification h5 {
|
||||||
|
background-color: #121212;
|
||||||
|
padding: 5px 10px 5px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#notification pre {
|
||||||
|
padding: 0px 10px 0px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#notification p {
|
||||||
|
font-size: 10 px;
|
||||||
|
margin: 0px;
|
||||||
|
padding: 0px 10px 5px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#notification .element {
|
||||||
|
/*padding: 0px 5px;*/
|
||||||
|
margin: 5px 5px;
|
||||||
|
border-radius: 5px;
|
||||||
|
background-color: #181818;
|
||||||
|
border-left: 10px solid green;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@media only screen and (min-width: 620px){
|
||||||
|
body {
|
||||||
|
width: 100%;
|
||||||
|
background-color: #444;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 26px;
|
||||||
|
letter-spacing: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav p {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#header_config {
|
||||||
|
display: block;
|
||||||
|
height: 100px;
|
||||||
|
background: url("../img/logo_w_600x200.png");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
|
||||||
|
background-size: 300px 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#screenLog {
|
||||||
|
margin-left: 300px;
|
||||||
|
|
||||||
|
transition: none;
|
||||||
|
background-color: transparent;
|
||||||
|
border-bottom: solid 1px transparent;
|
||||||
|
box-shadow: 0px 0px 0px #222;
|
||||||
|
}
|
||||||
|
|
||||||
|
#settings {
|
||||||
|
/*
|
||||||
|
height: -webkit-calc(100% - 100px);
|
||||||
|
height: -moz-calc(100% - 100px);
|
||||||
|
height: calc(100% - 100px);
|
||||||
|
*/
|
||||||
|
position: relative;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.screenLogHidden {
|
||||||
|
transform: translate(0px, 0px);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#box {
|
||||||
|
display: block;
|
||||||
|
min-height: 500px;
|
||||||
|
max-width: 500px;
|
||||||
|
margin: 10px auto;
|
||||||
|
background-color: #444;
|
||||||
|
box-shadow: 0px 5px 5px #222;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
#settings, #settings-footer {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
h1 {
|
||||||
|
color: green;
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 111 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 7.6 KiB |
|
After Width: | Height: | Size: 7.0 KiB |
|
After Width: | Height: | Size: 6.6 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,108 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<!---
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
-->
|
||||||
|
<title>xTeVe</title>
|
||||||
|
<link rel="stylesheet" href="css/screen.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 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>
|
||||||
|
|
||||||
|
<body onload="javascript: PageReady();">
|
||||||
|
|
||||||
|
<div id="loading" class="none">
|
||||||
|
<div class="loader"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="popup" class="none">
|
||||||
|
<div id="popup-custom"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="layout">
|
||||||
|
|
||||||
|
<!--
|
||||||
|
<div id="notification">
|
||||||
|
<div class="element">
|
||||||
|
<h5>XEPG</h5>
|
||||||
|
<pre>11.05.2019 - 20:21</pre>
|
||||||
|
<hr>
|
||||||
|
<p>Hallo das ist ein Test. Und noch mehr Text.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<div id="menu-wrapper" class="layout-left">
|
||||||
|
<div id= "branch"></div>
|
||||||
|
<div id="logo"></div>
|
||||||
|
<nav id="main-menu"></nav>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="layout-right">
|
||||||
|
|
||||||
|
<table id="clientInfo" class="">
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td class="tdKey">xTeVe:</td>
|
||||||
|
<td id="version" class="tdVal"> </td>
|
||||||
|
<td class="tdKey">OS:</td>
|
||||||
|
<td id="os" class="tdVal"> </td>
|
||||||
|
<td class="tdKey phone">DVR IP:</td>
|
||||||
|
<td id="DVR" class="tdVal phone"> </td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td class="tdKey">UUID:</td>
|
||||||
|
<td id="uuid" class="tdVal"> </td>
|
||||||
|
<td class="tdKey">Arch:</td>
|
||||||
|
<td id="arch" class="tdVal"> </td>
|
||||||
|
<td class="tdKey phone">M3U URL:</td>
|
||||||
|
<td id="m3u-url" class="tdVal phone"> </td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td class="tdKey">Available Streams:</td>
|
||||||
|
<td id="streams" class="tdVal"> </td>
|
||||||
|
<td class="tdKey">EPG Source:</td>
|
||||||
|
<td id="epgSource" class="tdVal"> </td>
|
||||||
|
<td class="tdKey phone">XEPG URL:</td>
|
||||||
|
<td id="xepg-url" class="tdVal phone"> </td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td class="tdKey">XEPG Channels:</td>
|
||||||
|
<td id="xepg" class="tdVal"> </td>
|
||||||
|
<td class="tdKey">Errors:</td>
|
||||||
|
<td id="errors" class="tdVal"> </td>
|
||||||
|
<td class="tdKey">Warnings:</td>
|
||||||
|
<td id="warnings" class="tdVal"> </td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div id="myStreamsBox" class="notVisible">
|
||||||
|
|
||||||
|
<div id="allStreams">
|
||||||
|
<table id="activeStreams"></table>
|
||||||
|
<table id="inactiveStreams"></table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="content" class=""></div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
function login() {
|
||||||
|
var err = false;
|
||||||
|
var data = new Object();
|
||||||
|
var div = document.getElementById("content");
|
||||||
|
var form = document.getElementById("authentication");
|
||||||
|
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";
|
||||||
|
err = true;
|
||||||
|
}
|
||||||
|
data[key] = value;
|
||||||
|
}
|
||||||
|
if (err == true) {
|
||||||
|
data = new Object();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (data.hasOwnProperty("confirm")) {
|
||||||
|
if (data["confirm"] != data["password"]) {
|
||||||
|
alert("sdafsd");
|
||||||
|
document.getElementById('password').style.borderColor = "red";
|
||||||
|
document.getElementById('confirm').style.borderColor = "red";
|
||||||
|
document.getElementById("err").innerHTML = "{{.account.failed}}";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(data);
|
||||||
|
form.submit();
|
||||||
|
}
|
||||||
@@ -0,0 +1,331 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,473 @@
|
|||||||
|
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;
|
||||||
|
// Menü
|
||||||
|
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}}", "xteveAutoUpdate,tuner,epgSource,api"));
|
||||||
|
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.files}}", "update,files.update,temp.path,cache.images,xepg.replace.missing.images"));
|
||||||
|
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.streaming}}", "buffer,buffer.size.kb,buffer.timeout,user.agent"));
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
document.getElementById(elmID).className = cssClass;
|
||||||
|
}
|
||||||
|
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 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
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");
|
||||||
|
var sortObj = new Object();
|
||||||
|
var x, xValue;
|
||||||
|
var tableHeader;
|
||||||
|
var sortByString = false;
|
||||||
|
if (column > 0 && COLUMN_TO_SORT > 0) {
|
||||||
|
tableItems[COLUMN_TO_SORT].className = "pointer";
|
||||||
|
tableItems[column].className = "sortThis";
|
||||||
|
}
|
||||||
|
COLUMN_TO_SORT = column;
|
||||||
|
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"];
|
||||||
|
channels.forEach(function (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 {
|
||||||
|
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];
|
||||||
|
switch (element.toLowerCase().includes(searchValue.toLowerCase())) {
|
||||||
|
case true:
|
||||||
|
document.getElementById(id).style.display = "";
|
||||||
|
break;
|
||||||
|
case false:
|
||||||
|
document.getElementById(id).style.display = "none";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
function calculateWrapperHeight() {
|
||||||
|
if (document.getElementById("box-wrapper")) {
|
||||||
|
var elm = document.getElementById("box-wrapper");
|
||||||
|
var divs = new Array("myStreamsBox", "clientInfo", "content");
|
||||||
|
var elementsHeight = 0 - elm.offsetHeight;
|
||||||
|
for (var i = 0; i < divs.length; i++) {
|
||||||
|
elementsHeight = elementsHeight + document.getElementById(divs[i]).offsetHeight;
|
||||||
|
}
|
||||||
|
elm.style.height = window.innerHeight - elementsHeight + "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());
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
@@ -0,0 +1,294 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
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 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 doc = document.getElementById(this.DocumentID);
|
||||||
|
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);
|
||||||
|
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);
|
||||||
|
description = "{{.wizard.epgSource.description}}";
|
||||||
|
break;
|
||||||
|
case "m3u":
|
||||||
|
var input = content.createInput("text", key, "");
|
||||||
|
input.setAttribute("class", "wizard");
|
||||||
|
input.id = key;
|
||||||
|
doc.appendChild(input);
|
||||||
|
description = "{{.wizard.m3u.description}}";
|
||||||
|
break;
|
||||||
|
case "xmltv":
|
||||||
|
var input = content.createInput("text", key, "");
|
||||||
|
input.setAttribute("class", "wizard");
|
||||||
|
input.id = key;
|
||||||
|
doc.appendChild(input);
|
||||||
|
description = "{{.wizard.xmltv.description}}";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.log(key);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
var pre = document.createElement("PRE");
|
||||||
|
pre.innerHTML = description;
|
||||||
|
doc.appendChild(pre);
|
||||||
|
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;
|
||||||
|
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}}"));
|
||||||
@@ -0,0 +1,329 @@
|
|||||||
|
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]
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,379 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
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;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
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");
|
||||||
|
div.innerHTML = "";
|
||||||
|
var keys = getObjKeys(logs);
|
||||||
|
keys.forEach(function (logID) {
|
||||||
|
var entry = log.createLog(logs[logID]);
|
||||||
|
div.append(entry);
|
||||||
|
});
|
||||||
|
setTimeout(function () {
|
||||||
|
if (bottom == true) {
|
||||||
|
var wrapper = document.getElementById("box-wrapper");
|
||||||
|
wrapper.scrollTop = wrapper.scrollHeight;
|
||||||
|
}
|
||||||
|
}, 10);
|
||||||
|
}
|
||||||
|
function resetLogs() {
|
||||||
|
var cmd = "resetLogs";
|
||||||
|
var data = new Object();
|
||||||
|
var server = new Server(cmd);
|
||||||
|
server.request(data);
|
||||||
|
}
|
||||||
@@ -0,0 +1,754 @@
|
|||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
switch (window.location.protocol) {
|
||||||
|
case "http:":
|
||||||
|
this.protocol = "ws://";
|
||||||
|
break;
|
||||||
|
case "https://":
|
||||||
|
this.protocol = "wss://";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
var url = this.protocol + window.location.hostname + ":" + window.location.port + "/data/" + "?Token=" + getCookie("Token");
|
||||||
|
data["cmd"] = this.cmd;
|
||||||
|
var ws = new WebSocket(url);
|
||||||
|
ws.onopen = function () {
|
||||||
|
WS_AVAILABLE = true;
|
||||||
|
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.");
|
||||||
|
SERVER_CONNECTION = false;
|
||||||
|
if (WS_AVAILABLE == false) {
|
||||||
|
alert("No websocket connection to xTeVe could be established. Check your network configuration.");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ws.onmessage = function (e) {
|
||||||
|
SERVER_CONNECTION = false;
|
||||||
|
showElement("loading", false);
|
||||||
|
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) {
|
||||||
|
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();
|
||||||
|
};
|
||||||
|
};
|
||||||
|
return Server;
|
||||||
|
}());
|
||||||
|
function getCookie(name) {
|
||||||
|
var value = "; " + document.cookie;
|
||||||
|
var parts = value.split("; " + name + "=");
|
||||||
|
if (parts.length == 2)
|
||||||
|
return parts.pop().split(";").shift();
|
||||||
|
}
|
||||||
@@ -0,0 +1,442 @@
|
|||||||
|
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 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 "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;
|
||||||
|
// 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 "xteveAutoUpdate":
|
||||||
|
var tdLeft = document.createElement("TD");
|
||||||
|
tdLeft.innerHTML = "{{.settings.xteveAutoUpdate.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 "buffer":
|
||||||
|
var tdLeft = document.createElement("TD");
|
||||||
|
tdLeft.innerHTML = "{{.settings.streamBuffering.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;
|
||||||
|
// Select
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
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 "xteveAutoUpdate":
|
||||||
|
text = "{{.settings.xteveAutoUpdate.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 "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 "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;
|
||||||
|
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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,341 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,419 @@
|
|||||||
|
{
|
||||||
|
"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"
|
||||||
|
},
|
||||||
|
"button":{
|
||||||
|
"back": "Back",
|
||||||
|
"backup": "Backup",
|
||||||
|
"bulkEdit": "Bulk Edit",
|
||||||
|
"cancel": "Cancel",
|
||||||
|
"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"
|
||||||
|
},
|
||||||
|
"active": {
|
||||||
|
"title": "Active",
|
||||||
|
"placeholder": "",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"channelName": {
|
||||||
|
"title": "Channel Name",
|
||||||
|
"placeholder": "",
|
||||||
|
"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": "Passoword",
|
||||||
|
"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."
|
||||||
|
},
|
||||||
|
"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>"
|
||||||
|
},
|
||||||
|
"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."
|
||||||
|
},
|
||||||
|
"xteveAutoUpdate": {
|
||||||
|
"title": "Automatic update of xTeVe",
|
||||||
|
"description": "If a new version of xTeVe is available, it will be automatically installed. The updates are downloaded from GitHub."
|
||||||
|
},
|
||||||
|
"streamBuffering": {
|
||||||
|
"title": "Stream Buffer",
|
||||||
|
"description": "- The stream is passed from xTeVe to Plex / Emby / M3U Player<br>- Small jerking of the streams can be compensated<br>- HLS / M3U8 support"
|
||||||
|
},
|
||||||
|
"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",
|
||||||
|
"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",
|
||||||
|
"description": "Local or remote playlists"
|
||||||
|
},
|
||||||
|
"xmltv": {
|
||||||
|
"title": "XMLTV File",
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>xTeVe</title>
|
||||||
|
<link rel="stylesheet" href="css/screen.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 language="javascript" type="text/javascript" src="js/authentication_ts.js"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div id="header" class="imgCenter"></div>
|
||||||
|
|
||||||
|
<div id="box">
|
||||||
|
|
||||||
|
<div id="headline">
|
||||||
|
<h1 id="head-text" class="center">{{.login.headline}}</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p id="err" class="errorMsg center">{{.authenticationErr}}</p>
|
||||||
|
|
||||||
|
<div id="content">
|
||||||
|
|
||||||
|
<form id="authentication" action="/web/" method="post">
|
||||||
|
|
||||||
|
<h5>{{.login.username.title}}:</h5>
|
||||||
|
<input id="username" type="text" name="username" placeholder="Username" value="">
|
||||||
|
<h5>{{.login.password.title}}:</h5>
|
||||||
|
<input id="password" type="password" name="password" placeholder="Password" value="">
|
||||||
|
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="box-footer">
|
||||||
|
<input id="submit" class="" type="button" value="{{.button.login}}" onclick="javascript: login();">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>xTeVe</title>
|
||||||
|
<link rel="stylesheet" href="css/screen.css" type="text/css">
|
||||||
|
<link rel="stylesheet" href="css/base.css" type="text/css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div id="header" class="imgCenter"></div>
|
||||||
|
|
||||||
|
<div id="box">
|
||||||
|
|
||||||
|
<div id="headline">
|
||||||
|
<h1 id="head-text" class="center">Maintenance</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="content">
|
||||||
|
xTeVe is updating the database, please try again later.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="box-footer"></div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,169 @@
|
|||||||
|
package src
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"../src/internal/authentication"
|
||||||
|
)
|
||||||
|
|
||||||
|
func activatedSystemAuthentication() (err error) {
|
||||||
|
|
||||||
|
err = authentication.Init(System.Folder.Config, 60)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var defaults = make(map[string]interface{})
|
||||||
|
defaults["authentication.web"] = false
|
||||||
|
defaults["authentication.pms"] = false
|
||||||
|
defaults["authentication.xml"] = false
|
||||||
|
defaults["authentication.api"] = false
|
||||||
|
err = authentication.SetDefaultUserData(defaults)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func createFirstUserForAuthentication(username, password string) (token string, err error) {
|
||||||
|
|
||||||
|
var authenticationErr = func(err error) {
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = authentication.CreateDefaultUser(username, password)
|
||||||
|
authenticationErr(err)
|
||||||
|
|
||||||
|
token, err = authentication.UserAuthentication(username, password)
|
||||||
|
authenticationErr(err)
|
||||||
|
|
||||||
|
token, err = authentication.CheckTheValidityOfTheToken(token)
|
||||||
|
authenticationErr(err)
|
||||||
|
|
||||||
|
var userData = make(map[string]interface{})
|
||||||
|
userData["username"] = username
|
||||||
|
userData["authentication.web"] = true
|
||||||
|
userData["authentication.pms"] = true
|
||||||
|
userData["authentication.m3u"] = true
|
||||||
|
userData["authentication.xml"] = true
|
||||||
|
userData["authentication.api"] = false
|
||||||
|
userData["defaultUser"] = true
|
||||||
|
|
||||||
|
userID, err := authentication.GetUserID(token)
|
||||||
|
authenticationErr(err)
|
||||||
|
|
||||||
|
err = authentication.WriteUserData(userID, userData)
|
||||||
|
authenticationErr(err)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func tokenAuthentication(token string) (newToken string, err error) {
|
||||||
|
|
||||||
|
if System.ConfigurationWizard == true {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
newToken, err = authentication.CheckTheValidityOfTheToken(token)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func basicAuth(r *http.Request, level string) (username string, err error) {
|
||||||
|
|
||||||
|
err = errors.New("User authentication failed")
|
||||||
|
|
||||||
|
auth := strings.SplitN(r.Header.Get("Authorization"), " ", 2)
|
||||||
|
|
||||||
|
if len(auth) != 2 || auth[0] != "Basic" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
payload, _ := base64.StdEncoding.DecodeString(auth[1])
|
||||||
|
pair := strings.SplitN(string(payload), ":", 2)
|
||||||
|
|
||||||
|
username = pair[0]
|
||||||
|
var password = pair[1]
|
||||||
|
|
||||||
|
token, err := authentication.UserAuthentication(username, password)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = checkAuthorizationLevel(token, level)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func urlAuth(r *http.Request, requestType string) (err error) {
|
||||||
|
var level, token string
|
||||||
|
|
||||||
|
var username = r.URL.Query().Get("username")
|
||||||
|
var password = r.URL.Query().Get("password")
|
||||||
|
|
||||||
|
switch requestType {
|
||||||
|
|
||||||
|
case "m3u":
|
||||||
|
level = "authentication.m3u"
|
||||||
|
if Settings.AuthenticationM3U == true {
|
||||||
|
token, err = authentication.UserAuthentication(username, password)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = checkAuthorizationLevel(token, level)
|
||||||
|
}
|
||||||
|
|
||||||
|
case "xml":
|
||||||
|
level = "authentication.xml"
|
||||||
|
if Settings.AuthenticationXML == true {
|
||||||
|
token, err = authentication.UserAuthentication(username, password)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = checkAuthorizationLevel(token, level)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkAuthorizationLevel(token, level string) (err error) {
|
||||||
|
|
||||||
|
var authenticationErr = func(err error) {
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
userID, err := authentication.GetUserID(token)
|
||||||
|
authenticationErr(err)
|
||||||
|
|
||||||
|
userData, err := authentication.ReadUserData(userID)
|
||||||
|
authenticationErr(err)
|
||||||
|
|
||||||
|
if len(userData) > 0 {
|
||||||
|
|
||||||
|
if v, ok := userData[level].(bool); ok {
|
||||||
|
|
||||||
|
if v == false {
|
||||||
|
err = errors.New("No authorization")
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
userData[level] = false
|
||||||
|
err = authentication.WriteUserData(userID, userData)
|
||||||
|
err = errors.New("No authorization")
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
err = authentication.WriteUserData(userID, userData)
|
||||||
|
err = errors.New("No authorization")
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -0,0 +1,191 @@
|
|||||||
|
package src
|
||||||
|
|
||||||
|
import (
|
||||||
|
b64 "encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func xTeVeAutoBackup() (err error) {
|
||||||
|
|
||||||
|
var archiv = "xteve_auto_backup_" + time.Now().Format("20060102_1504") + ".zip"
|
||||||
|
var target string
|
||||||
|
var sourceFiles = make([]string, 0)
|
||||||
|
var oldBackupFiles = make([]string, 0)
|
||||||
|
var debug string
|
||||||
|
|
||||||
|
if len(Settings.BackupPath) > 0 {
|
||||||
|
System.Folder.Backup = Settings.BackupPath
|
||||||
|
}
|
||||||
|
|
||||||
|
showInfo("Backup Path:" + System.Folder.Backup)
|
||||||
|
|
||||||
|
err = checkFolder(System.Folder.Backup)
|
||||||
|
if err != nil {
|
||||||
|
ShowError(err, 1070)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alte Backups löschen
|
||||||
|
files, err := ioutil.ReadDir(System.Folder.Backup)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
|
||||||
|
for _, file := range files {
|
||||||
|
|
||||||
|
if filepath.Ext(file.Name()) == ".zip" && strings.Contains(file.Name(), "xteve_auto_backup") {
|
||||||
|
oldBackupFiles = append(oldBackupFiles, file.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alle Backups löschen
|
||||||
|
var end int
|
||||||
|
switch Settings.BackupKeep {
|
||||||
|
case 0:
|
||||||
|
end = 0
|
||||||
|
default:
|
||||||
|
end = Settings.BackupKeep - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < len(oldBackupFiles)-end; i++ {
|
||||||
|
|
||||||
|
os.RemoveAll(System.Folder.Backup + oldBackupFiles[i])
|
||||||
|
debug = fmt.Sprintf("Delete backup file:%s", oldBackupFiles[i])
|
||||||
|
showDebug(debug, 1)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if Settings.BackupKeep == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Backup erstellen
|
||||||
|
if err == nil {
|
||||||
|
|
||||||
|
target = System.Folder.Backup + archiv
|
||||||
|
|
||||||
|
for _, i := range SystemFiles {
|
||||||
|
sourceFiles = append(sourceFiles, System.Folder.Config+i)
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceFiles = append(sourceFiles, System.Folder.ImagesUpload)
|
||||||
|
|
||||||
|
err = zipFiles(sourceFiles, target)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
|
||||||
|
debug = fmt.Sprintf("Create backup file:%s", target)
|
||||||
|
showDebug(debug, 1)
|
||||||
|
|
||||||
|
showInfo("Backup file:" + target)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func xteveBackup() (archiv string, err error) {
|
||||||
|
|
||||||
|
err = checkFolder(System.Folder.Temp)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
archiv = "xteve_backup_" + time.Now().Format("20060102_1504") + ".zip"
|
||||||
|
|
||||||
|
var target = System.Folder.Temp + archiv
|
||||||
|
var sourceFiles = make([]string, 0)
|
||||||
|
|
||||||
|
for _, i := range SystemFiles {
|
||||||
|
sourceFiles = append(sourceFiles, System.Folder.Config+i)
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceFiles = append(sourceFiles, System.Folder.Data)
|
||||||
|
|
||||||
|
err = zipFiles(sourceFiles, target)
|
||||||
|
if err != nil {
|
||||||
|
ShowError(err, 0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func xteveRestore(input string) (newWebURL string, err error) {
|
||||||
|
|
||||||
|
var newPort, oldPort string
|
||||||
|
|
||||||
|
// Base64 Json String in base64 umwandeln
|
||||||
|
b64data := input[strings.IndexByte(input, ',')+1:]
|
||||||
|
|
||||||
|
// Base64 in bytes umwandeln und speichern
|
||||||
|
sDec, err := b64.StdEncoding.DecodeString(b64data)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var archive = System.Folder.Temp + "restore.zip"
|
||||||
|
|
||||||
|
err = writeByteToFile(archive, sDec)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zip Archiv entpacken
|
||||||
|
err = extractZIP(archive, System.Folder.Config)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Neue Config laden um den Port zu überprüfen
|
||||||
|
newConfig, err := loadJSONFileToMap(System.Folder.Config + "settings.json")
|
||||||
|
if err != nil {
|
||||||
|
ShowError(err, 0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
newPort = newConfig["port"].(string)
|
||||||
|
oldPort = Settings.Port
|
||||||
|
|
||||||
|
if newPort == oldPort {
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
ShowError(err, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
loadSettings()
|
||||||
|
|
||||||
|
err := Init()
|
||||||
|
if err != nil {
|
||||||
|
ShowError(err, 0)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = StartSystem(true)
|
||||||
|
if err != nil {
|
||||||
|
ShowError(err, 0)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
var url = System.URLBase + "/web/"
|
||||||
|
newWebURL = strings.Replace(url, ":"+oldPort, ":"+newPort, 1)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -0,0 +1,148 @@
|
|||||||
|
package src
|
||||||
|
|
||||||
|
import (
|
||||||
|
"archive/zip"
|
||||||
|
"bytes"
|
||||||
|
"compress/gzip"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func zipFiles(sourceFiles []string, target string) error {
|
||||||
|
|
||||||
|
zipfile, err := os.Create(target)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer zipfile.Close()
|
||||||
|
|
||||||
|
archive := zip.NewWriter(zipfile)
|
||||||
|
defer archive.Close()
|
||||||
|
|
||||||
|
for _, source := range sourceFiles {
|
||||||
|
|
||||||
|
info, err := os.Stat(source)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var baseDir string
|
||||||
|
if info.IsDir() {
|
||||||
|
baseDir = filepath.Base(System.Folder.Data)
|
||||||
|
}
|
||||||
|
|
||||||
|
filepath.Walk(source, func(path string, info os.FileInfo, err error) error {
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
header, err := zip.FileInfoHeader(info)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if baseDir != "" {
|
||||||
|
header.Name = filepath.Join(strings.TrimPrefix(path, System.Folder.Config))
|
||||||
|
}
|
||||||
|
|
||||||
|
if info.IsDir() {
|
||||||
|
header.Name += string(os.PathSeparator)
|
||||||
|
} else {
|
||||||
|
header.Method = zip.Deflate
|
||||||
|
}
|
||||||
|
|
||||||
|
writer, err := archive.CreateHeader(header)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if info.IsDir() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
_, err = io.Copy(writer, file)
|
||||||
|
|
||||||
|
return err
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractZIP(archive, target string) (err error) {
|
||||||
|
|
||||||
|
reader, err := zip.OpenReader(archive)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.MkdirAll(target, 0755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, file := range reader.File {
|
||||||
|
|
||||||
|
path := filepath.Join(target, file.Name)
|
||||||
|
if file.FileInfo().IsDir() {
|
||||||
|
os.MkdirAll(path, file.Mode())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fileReader, err := file.Open()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer fileReader.Close()
|
||||||
|
|
||||||
|
targetFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer targetFile.Close()
|
||||||
|
|
||||||
|
if _, err := io.Copy(targetFile, fileReader); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractGZIP(gzipBody []byte, fileSource string) (body []byte, err error) {
|
||||||
|
|
||||||
|
var b = bytes.NewBuffer(gzipBody)
|
||||||
|
|
||||||
|
var r io.Reader
|
||||||
|
r, err = gzip.NewReader(b)
|
||||||
|
if err != nil {
|
||||||
|
// Keine gzip Datei
|
||||||
|
body = gzipBody
|
||||||
|
err = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
showInfo("Extract gzip:" + fileSource)
|
||||||
|
|
||||||
|
var resB bytes.Buffer
|
||||||
|
_, err = resB.ReadFrom(r)
|
||||||
|
if err != nil {
|
||||||
|
body = gzipBody
|
||||||
|
err = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
body = resB.Bytes()
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -0,0 +1,242 @@
|
|||||||
|
package src
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// System : Beinhaltet alle Systeminformationen
|
||||||
|
var System SystemStruct
|
||||||
|
|
||||||
|
// WebScreenLog : Logs werden im RAM gespeichert und für das Webinterface bereitgestellt
|
||||||
|
var WebScreenLog WebScreenLogStruct
|
||||||
|
|
||||||
|
// Settings : Inhalt der settings.json
|
||||||
|
var Settings SettingsStrcut
|
||||||
|
|
||||||
|
// Data : Alle Daten werden hier abgelegt. (Lineup, XMLTV)
|
||||||
|
var Data DataStruct
|
||||||
|
|
||||||
|
// SystemFiles : Alle Systemdateien
|
||||||
|
var SystemFiles = []string{"authentication.json", "pms.json", "settings.json", "xepg.json", "urls.json"}
|
||||||
|
|
||||||
|
// BufferInformation : Informationen über den Buffer (aktive Streams, maximale Streams)
|
||||||
|
var BufferInformation sync.Map
|
||||||
|
|
||||||
|
// BufferClients : Anzahl der Clients die einen Stream über den Buffer abspielen
|
||||||
|
var BufferClients sync.Map
|
||||||
|
|
||||||
|
// Init : Systeminitialisierung
|
||||||
|
func Init() (err error) {
|
||||||
|
|
||||||
|
var debug string
|
||||||
|
|
||||||
|
// System Einstellungen
|
||||||
|
System.AppName = strings.ToLower(System.Name)
|
||||||
|
System.ARCH = runtime.GOARCH
|
||||||
|
System.OS = runtime.GOOS
|
||||||
|
System.ServerProtocol.API = "http"
|
||||||
|
System.ServerProtocol.DVR = "http"
|
||||||
|
System.ServerProtocol.M3U = "http"
|
||||||
|
System.ServerProtocol.WEB = "http"
|
||||||
|
System.ServerProtocol.XML = "http"
|
||||||
|
System.DVRLimit = 480
|
||||||
|
System.Compatibility = "1.4.4"
|
||||||
|
|
||||||
|
// Default Logeinträge, wird später von denen aus der settings.json überschrieben. Muss gemacht werden, damit die ersten Einträge auch im Log (webUI aangezeigt werden)
|
||||||
|
Settings.LogEntriesRAM = 500
|
||||||
|
|
||||||
|
// Variablen für den Update Prozess
|
||||||
|
//System.Update.Git = "https://github.com/xteve-project/xTeVe-Downloads/blob"
|
||||||
|
System.Update.Git = fmt.Sprintf("https://github.com/%s/%s/blob", System.GitHub.User, System.GitHub.Repo)
|
||||||
|
System.Update.Name = "xteve_2"
|
||||||
|
|
||||||
|
// Ordnerpfade festlegen
|
||||||
|
var tempFolder = os.TempDir() + string(os.PathSeparator) + System.AppName + string(os.PathSeparator)
|
||||||
|
tempFolder = getPlatformPath(strings.Replace(tempFolder, "//", "/", -1))
|
||||||
|
|
||||||
|
if len(System.Folder.Config) == 0 {
|
||||||
|
System.Folder.Config = GetUserHomeDirectory() + string(os.PathSeparator) + "." + System.AppName + string(os.PathSeparator)
|
||||||
|
} else {
|
||||||
|
System.Folder.Config = strings.TrimRight(System.Folder.Config, string(os.PathSeparator)) + string(os.PathSeparator)
|
||||||
|
}
|
||||||
|
|
||||||
|
System.Folder.Config = getPlatformPath(System.Folder.Config)
|
||||||
|
|
||||||
|
System.Folder.Backup = System.Folder.Config + "backup" + string(os.PathSeparator)
|
||||||
|
System.Folder.Data = System.Folder.Config + "data" + string(os.PathSeparator)
|
||||||
|
System.Folder.Cache = System.Folder.Config + "cache" + string(os.PathSeparator)
|
||||||
|
System.Folder.ImagesCache = System.Folder.Cache + "images" + string(os.PathSeparator)
|
||||||
|
System.Folder.ImagesUpload = System.Folder.Data + "images" + string(os.PathSeparator)
|
||||||
|
System.Folder.Temp = tempFolder
|
||||||
|
|
||||||
|
// Dev Info
|
||||||
|
showDevInfo()
|
||||||
|
|
||||||
|
// System Ordner erstellen
|
||||||
|
err = createSystemFolders()
|
||||||
|
if err != nil {
|
||||||
|
ShowError(err, 1070)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
System.File.XML = getPlatformFile(fmt.Sprintf("%s%s.xml", System.Folder.Data, System.AppName))
|
||||||
|
System.File.M3U = getPlatformFile(fmt.Sprintf("%s%s.m3u", System.Folder.Data, System.AppName))
|
||||||
|
|
||||||
|
err = activatedSystemAuthentication()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = resolveHostIP()
|
||||||
|
if err != nil {
|
||||||
|
ShowError(err, 1002)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Menü für das Webinterface
|
||||||
|
System.WEB.Menu = []string{"playlist", "filter", "xmltv", "mapping", "users", "settings", "log", "logout"}
|
||||||
|
|
||||||
|
fmt.Println("For help run: " + getPlatformFile(os.Args[0]) + " -h")
|
||||||
|
fmt.Println()
|
||||||
|
|
||||||
|
// Überprüfen ob xTeVe als root läuft
|
||||||
|
if os.Geteuid() == 0 {
|
||||||
|
showWarning(2010)
|
||||||
|
}
|
||||||
|
|
||||||
|
if System.Flag.Debug > 0 {
|
||||||
|
debug = fmt.Sprintf("Debug Level:%d", System.Flag.Debug)
|
||||||
|
showDebug(debug, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
showInfo(fmt.Sprintf("Version:%s Build: %s", System.Version, System.Build))
|
||||||
|
showInfo(fmt.Sprintf("System IP Addresses:IPv4: %d | IPv6: %d", len(System.IPAddressesV4), len(System.IPAddressesV6)))
|
||||||
|
showInfo("Hostname:" + System.Hostname)
|
||||||
|
showInfo(fmt.Sprintf("System Folder:%s", getPlatformPath(System.Folder.Config)))
|
||||||
|
|
||||||
|
// Systemdateien erstellen (Falls nicht vorhanden)
|
||||||
|
err = createSystemFiles()
|
||||||
|
if err != nil {
|
||||||
|
ShowError(err, 1071)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bedingte Update Änderungen durchführen
|
||||||
|
err = conditionalUpdateChanges()
|
||||||
|
if err != nil {
|
||||||
|
ShowError(err, 0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Einstellungen laden (settings.json)
|
||||||
|
showInfo(fmt.Sprintf("Load Settings:%s", System.File.Settings))
|
||||||
|
|
||||||
|
_, err = loadSettings()
|
||||||
|
if err != nil {
|
||||||
|
ShowError(err, 0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Berechtigung aller Ordner überprüfen
|
||||||
|
err = checkFilePermission(System.Folder.Config)
|
||||||
|
if err == nil {
|
||||||
|
err = checkFilePermission(System.Folder.Temp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Separaten tmp Ordner für jede Instanz
|
||||||
|
//System.Folder.Temp = System.Folder.Temp + Settings.UUID + string(os.PathSeparator)
|
||||||
|
showInfo(fmt.Sprintf("Temporary Folder:%s", getPlatformPath(System.Folder.Temp)))
|
||||||
|
|
||||||
|
err = checkFolder(System.Folder.Temp)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = removeChildItems(getPlatformPath(System.Folder.Temp))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DLNA Server starten
|
||||||
|
go SSDP()
|
||||||
|
|
||||||
|
// Branch festlegen
|
||||||
|
System.Branch = Settings.Branch
|
||||||
|
|
||||||
|
if System.Dev == true {
|
||||||
|
System.Branch = "Development"
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(System.Branch) == 0 {
|
||||||
|
System.Branch = "master"
|
||||||
|
}
|
||||||
|
|
||||||
|
showInfo(fmt.Sprintf("GitHub:https://github.com/%s", System.GitHub.User))
|
||||||
|
showInfo(fmt.Sprintf("Git Branch:%s [%s]", System.Branch, System.GitHub.User))
|
||||||
|
|
||||||
|
// Domainnamen setzten
|
||||||
|
setGlobalDomain(fmt.Sprintf("%s:%s", 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
|
||||||
|
if System.Dev == true {
|
||||||
|
|
||||||
|
HTMLInit("webUI", "src", "html"+string(os.PathSeparator), "src"+string(os.PathSeparator)+"webUI.go")
|
||||||
|
err = BuildGoFile()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
loadHTMLMap()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartSystem : System wird gestartet
|
||||||
|
func StartSystem(updateProviderFiles bool) (err error) {
|
||||||
|
|
||||||
|
setDeviceID()
|
||||||
|
|
||||||
|
if System.ScanInProgress == 1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Systeminformationen in der Konsole ausgeben
|
||||||
|
showInfo(fmt.Sprintf("UUID:%s", Settings.UUID))
|
||||||
|
showInfo(fmt.Sprintf("Tuner (Plex / Emby):%d", Settings.Tuner))
|
||||||
|
showInfo(fmt.Sprintf("EPG Source:%s", Settings.EpgSource))
|
||||||
|
showInfo(fmt.Sprintf("Plex Channel Limit:%d", System.DVRLimit))
|
||||||
|
|
||||||
|
// Providerdaten aktualisieren
|
||||||
|
if len(Settings.Files.M3U) > 0 && Settings.FilesUpdate == true || updateProviderFiles == true {
|
||||||
|
|
||||||
|
err = xTeVeAutoBackup()
|
||||||
|
if err != nil {
|
||||||
|
ShowError(err, 1090)
|
||||||
|
}
|
||||||
|
|
||||||
|
getProviderData("m3u", "")
|
||||||
|
getProviderData("hdhr", "")
|
||||||
|
|
||||||
|
if Settings.EpgSource == "XEPG" {
|
||||||
|
getProviderData("xmltv", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
err = buildDatabaseDVR()
|
||||||
|
if err != nil {
|
||||||
|
ShowError(err, 0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
buildXEPG(false)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -0,0 +1,953 @@
|
|||||||
|
package src
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"../src/internal/authentication"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Einstellungen ändern (WebUI)
|
||||||
|
func updateServerSettings(request RequestStruct) (settings SettingsStrcut, err error) {
|
||||||
|
|
||||||
|
var oldSettings = jsonToMap(mapToJSON(Settings))
|
||||||
|
var newSettings = jsonToMap(mapToJSON(request.Settings))
|
||||||
|
var reloadData = false
|
||||||
|
var cacheImages = false
|
||||||
|
var createXEPGFiles = false
|
||||||
|
var debug string
|
||||||
|
|
||||||
|
for key, value := range newSettings {
|
||||||
|
|
||||||
|
if _, ok := oldSettings[key]; ok {
|
||||||
|
|
||||||
|
switch key {
|
||||||
|
|
||||||
|
case "tuner":
|
||||||
|
showWarning(2105)
|
||||||
|
|
||||||
|
case "epgSource":
|
||||||
|
reloadData = true
|
||||||
|
|
||||||
|
case "update":
|
||||||
|
// Die Formatierung der Uhrzeit überprüfen (0000 - 2359)
|
||||||
|
for _, i := range newSettings[key].([]interface{}) {
|
||||||
|
|
||||||
|
_, err := time.Parse("1504", i.(string))
|
||||||
|
if err != nil {
|
||||||
|
ShowError(err, 1012)
|
||||||
|
return Settings, err
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
case "cache.images":
|
||||||
|
cacheImages = true
|
||||||
|
|
||||||
|
case "xepg.replace.missing.images":
|
||||||
|
createXEPGFiles = true
|
||||||
|
|
||||||
|
case "backup.path":
|
||||||
|
value = strings.TrimRight(value.(string), string(os.PathSeparator)) + string(os.PathSeparator)
|
||||||
|
err = checkFolder(value.(string))
|
||||||
|
if err == nil {
|
||||||
|
|
||||||
|
err = checkFilePermission(value.(string))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
case "temp.path":
|
||||||
|
value = strings.TrimRight(value.(string), string(os.PathSeparator)) + string(os.PathSeparator)
|
||||||
|
err = checkFolder(value.(string))
|
||||||
|
if err == nil {
|
||||||
|
|
||||||
|
err = checkFilePermission(value.(string))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
oldSettings[key] = value
|
||||||
|
|
||||||
|
switch fmt.Sprintf("%T", value) {
|
||||||
|
|
||||||
|
case "bool":
|
||||||
|
debug = fmt.Sprintf("Save Setting:Key: %s | Value: %t (%T)", key, value, value)
|
||||||
|
|
||||||
|
case "string":
|
||||||
|
debug = fmt.Sprintf("Save Setting:Key: %s | Value: %s (%T)", key, value, value)
|
||||||
|
|
||||||
|
case "[]interface {}":
|
||||||
|
debug = fmt.Sprintf("Save Setting:Key: %s | Value: %v (%T)", key, value, value)
|
||||||
|
|
||||||
|
case "float64":
|
||||||
|
debug = fmt.Sprintf("Save Setting:Key: %s | Value: %d (%T)", key, int(value.(float64)), value)
|
||||||
|
|
||||||
|
default:
|
||||||
|
debug = fmt.Sprintf("%T", value)
|
||||||
|
}
|
||||||
|
|
||||||
|
showDebug(debug, 1)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Einstellungen aktualisieren
|
||||||
|
err = json.Unmarshal([]byte(mapToJSON(oldSettings)), &Settings)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if Settings.AuthenticationWEB == false {
|
||||||
|
|
||||||
|
Settings.AuthenticationAPI = false
|
||||||
|
Settings.AuthenticationM3U = false
|
||||||
|
Settings.AuthenticationPMS = false
|
||||||
|
Settings.AuthenticationWEB = false
|
||||||
|
Settings.AuthenticationXML = false
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
err = saveSettings(Settings)
|
||||||
|
if err == nil {
|
||||||
|
|
||||||
|
settings = Settings
|
||||||
|
|
||||||
|
if reloadData == true {
|
||||||
|
|
||||||
|
err = buildDatabaseDVR()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
buildXEPG(false)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if cacheImages == true {
|
||||||
|
|
||||||
|
if Settings.EpgSource == "XEPG" {
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
|
||||||
|
if Settings.CacheImages == true {
|
||||||
|
|
||||||
|
createXMLTVFile()
|
||||||
|
cachingImages()
|
||||||
|
createXMLTVFile()
|
||||||
|
createM3UFile()
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
createXMLTVFile()
|
||||||
|
createM3UFile()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if createXEPGFiles == true {
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
createXMLTVFile()
|
||||||
|
createM3UFile()
|
||||||
|
}()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Providerdaten speichern (WebUI)
|
||||||
|
func saveFiles(request RequestStruct, fileType string) (err error) {
|
||||||
|
|
||||||
|
var filesMap = make(map[string]interface{})
|
||||||
|
var newData = make(map[string]interface{})
|
||||||
|
var indicator string
|
||||||
|
var reloadData = false
|
||||||
|
|
||||||
|
switch fileType {
|
||||||
|
case "m3u":
|
||||||
|
filesMap = Settings.Files.M3U
|
||||||
|
newData = request.Files.M3U
|
||||||
|
indicator = "M"
|
||||||
|
|
||||||
|
case "hdhr":
|
||||||
|
filesMap = Settings.Files.HDHR
|
||||||
|
newData = request.Files.HDHR
|
||||||
|
indicator = "H"
|
||||||
|
|
||||||
|
case "xmltv":
|
||||||
|
filesMap = Settings.Files.XMLTV
|
||||||
|
newData = request.Files.XMLTV
|
||||||
|
indicator = "X"
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(filesMap) == 0 {
|
||||||
|
filesMap = make(map[string]interface{})
|
||||||
|
}
|
||||||
|
|
||||||
|
for dataID, data := range newData {
|
||||||
|
|
||||||
|
if dataID == "-" {
|
||||||
|
|
||||||
|
// Neue Providerdatei
|
||||||
|
dataID = indicator + randomString(19)
|
||||||
|
data.(map[string]interface{})["new"] = true
|
||||||
|
filesMap[dataID] = data
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// Bereits vorhandene Providerdatei
|
||||||
|
for key, value := range data.(map[string]interface{}) {
|
||||||
|
|
||||||
|
var oldData = filesMap[dataID].(map[string]interface{})
|
||||||
|
oldData[key] = value
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
switch fileType {
|
||||||
|
|
||||||
|
case "m3u":
|
||||||
|
Settings.Files.M3U = filesMap
|
||||||
|
|
||||||
|
case "hdhr":
|
||||||
|
Settings.Files.HDHR = filesMap
|
||||||
|
|
||||||
|
case "xmltv":
|
||||||
|
Settings.Files.XMLTV = filesMap
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Neue Providerdatei
|
||||||
|
if _, ok := data.(map[string]interface{})["new"]; ok {
|
||||||
|
|
||||||
|
reloadData = true
|
||||||
|
err = getProviderData(fileType, dataID)
|
||||||
|
delete(data.(map[string]interface{}), "new")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
delete(filesMap, dataID)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := data.(map[string]interface{})["delete"]; ok {
|
||||||
|
|
||||||
|
deleteLocalProviderFiles(dataID, fileType)
|
||||||
|
reloadData = true
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
err = saveSettings(Settings)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if reloadData == true {
|
||||||
|
|
||||||
|
err = buildDatabaseDVR()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
buildXEPG(false)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Settings, _ = loadSettings()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Providerdaten manuell aktualisieren (WebUI)
|
||||||
|
func updateFile(request RequestStruct, fileType string) (err error) {
|
||||||
|
|
||||||
|
var updateData = make(map[string]interface{})
|
||||||
|
|
||||||
|
switch fileType {
|
||||||
|
|
||||||
|
case "m3u":
|
||||||
|
updateData = request.Files.M3U
|
||||||
|
|
||||||
|
case "hdhr":
|
||||||
|
updateData = request.Files.HDHR
|
||||||
|
|
||||||
|
case "xmltv":
|
||||||
|
updateData = request.Files.XMLTV
|
||||||
|
}
|
||||||
|
|
||||||
|
for dataID := range updateData {
|
||||||
|
|
||||||
|
err = getProviderData(fileType, dataID)
|
||||||
|
if err == nil {
|
||||||
|
err = buildDatabaseDVR()
|
||||||
|
buildXEPG(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Providerdaten löschen (WebUI)
|
||||||
|
func deleteLocalProviderFiles(dataID, fileType string) {
|
||||||
|
|
||||||
|
var removeData = make(map[string]interface{})
|
||||||
|
var fileExtension string
|
||||||
|
|
||||||
|
switch fileType {
|
||||||
|
|
||||||
|
case "m3u":
|
||||||
|
removeData = Settings.Files.M3U
|
||||||
|
fileExtension = ".m3u"
|
||||||
|
|
||||||
|
case "hdhr":
|
||||||
|
removeData = Settings.Files.HDHR
|
||||||
|
fileExtension = ".json"
|
||||||
|
|
||||||
|
case "xmltv":
|
||||||
|
removeData = Settings.Files.XMLTV
|
||||||
|
fileExtension = ".xml"
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := removeData[dataID]; ok {
|
||||||
|
delete(removeData, dataID)
|
||||||
|
os.RemoveAll(System.Folder.Data + dataID + fileExtension)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filtereinstellungen speichern (WebUI)
|
||||||
|
func saveFilter(request RequestStruct) (settings SettingsStrcut, err error) {
|
||||||
|
|
||||||
|
var filterMap = make(map[int64]interface{})
|
||||||
|
var newData = make(map[int64]interface{})
|
||||||
|
var defaultFilter FilterStruct
|
||||||
|
|
||||||
|
defaultFilter.Active = true
|
||||||
|
defaultFilter.CaseSensitive = false
|
||||||
|
|
||||||
|
filterMap = Settings.Filter
|
||||||
|
newData = request.Filter
|
||||||
|
|
||||||
|
var createNewID = func() (id int64) {
|
||||||
|
|
||||||
|
newID:
|
||||||
|
if _, ok := filterMap[id]; ok {
|
||||||
|
id++
|
||||||
|
goto newID
|
||||||
|
}
|
||||||
|
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
|
||||||
|
for dataID, data := range newData {
|
||||||
|
|
||||||
|
if dataID == -1 {
|
||||||
|
|
||||||
|
// Neuer Filter
|
||||||
|
dataID = createNewID()
|
||||||
|
filterMap[dataID] = jsonToMap(mapToJSON(defaultFilter))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter aktualisieren / löschen
|
||||||
|
for key, value := range data.(map[string]interface{}) {
|
||||||
|
|
||||||
|
var oldData = filterMap[dataID].(map[string]interface{})
|
||||||
|
oldData[key] = value
|
||||||
|
|
||||||
|
// Filter löschen
|
||||||
|
if _, ok := data.(map[string]interface{})["delete"]; ok {
|
||||||
|
|
||||||
|
delete(filterMap, dataID)
|
||||||
|
break
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
err = saveSettings(Settings)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
settings = Settings
|
||||||
|
|
||||||
|
err = buildDatabaseDVR()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
buildXEPG(false)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// XEPG Mapping speichern
|
||||||
|
func saveXEpgMapping(request RequestStruct) (err error) {
|
||||||
|
|
||||||
|
var tmp = Data.XEPG
|
||||||
|
|
||||||
|
err = json.Unmarshal([]byte(mapToJSON(request.EpgMapping)), &tmp)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = saveMapToJSONFile(System.File.XEPG, request.EpgMapping)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
Data.XEPG.Channels = request.EpgMapping
|
||||||
|
|
||||||
|
cleanupXEPG()
|
||||||
|
buildXEPG(true)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Benutzerdaten speichern (WebUI)
|
||||||
|
func saveUserData(request RequestStruct) (err error) {
|
||||||
|
|
||||||
|
var userData = request.UserData
|
||||||
|
|
||||||
|
var newCredentials = func(userID string, newUserData map[string]interface{}) (err error) {
|
||||||
|
|
||||||
|
var newUsername, newPassword string
|
||||||
|
if username, ok := newUserData["username"].(string); ok {
|
||||||
|
newUsername = username
|
||||||
|
}
|
||||||
|
|
||||||
|
if password, ok := newUserData["password"].(string); ok {
|
||||||
|
newPassword = password
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(newUsername) > 0 {
|
||||||
|
err = authentication.ChangeCredentials(userID, newUsername, newPassword)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for userID, newUserData := range userData {
|
||||||
|
|
||||||
|
err = newCredentials(userID, newUserData.(map[string]interface{}))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if request.DeleteUser == true {
|
||||||
|
err = authentication.RemoveUser(userID)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(newUserData.(map[string]interface{}), "password")
|
||||||
|
delete(newUserData.(map[string]interface{}), "confirm")
|
||||||
|
|
||||||
|
if _, ok := newUserData.(map[string]interface{})["delete"]; ok {
|
||||||
|
|
||||||
|
authentication.RemoveUser(userID)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
err = authentication.WriteUserData(userID, newUserData.(map[string]interface{}))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Neuen Benutzer anlegen (WebUI)
|
||||||
|
func saveNewUser(request RequestStruct) (err error) {
|
||||||
|
|
||||||
|
var data = request.UserData
|
||||||
|
var username = data["username"].(string)
|
||||||
|
var password = data["password"].(string)
|
||||||
|
|
||||||
|
delete(data, "password")
|
||||||
|
delete(data, "confirm")
|
||||||
|
|
||||||
|
userID, err := authentication.CreateNewUser(username, password)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = authentication.WriteUserData(userID, data)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wizard (WebUI)
|
||||||
|
func saveWizard(request RequestStruct) (nextStep int, err error) {
|
||||||
|
|
||||||
|
var wizard = jsonToMap(mapToJSON(request.Wizard))
|
||||||
|
|
||||||
|
for key, value := range wizard {
|
||||||
|
|
||||||
|
switch key {
|
||||||
|
|
||||||
|
case "tuner":
|
||||||
|
Settings.Tuner = int(value.(float64))
|
||||||
|
nextStep = 1
|
||||||
|
|
||||||
|
case "epgSource":
|
||||||
|
Settings.EpgSource = value.(string)
|
||||||
|
nextStep = 2
|
||||||
|
|
||||||
|
case "m3u", "xmltv":
|
||||||
|
|
||||||
|
var filesMap = make(map[string]interface{})
|
||||||
|
var data = make(map[string]interface{})
|
||||||
|
var indicator, dataID string
|
||||||
|
|
||||||
|
filesMap = make(map[string]interface{})
|
||||||
|
|
||||||
|
data["type"] = key
|
||||||
|
data["new"] = true
|
||||||
|
|
||||||
|
switch key {
|
||||||
|
|
||||||
|
case "m3u":
|
||||||
|
filesMap = Settings.Files.M3U
|
||||||
|
data["name"] = "M3U"
|
||||||
|
indicator = "M"
|
||||||
|
|
||||||
|
case "xmltv":
|
||||||
|
filesMap = Settings.Files.XMLTV
|
||||||
|
data["name"] = "XMLTV"
|
||||||
|
indicator = "X"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
dataID = indicator + randomString(19)
|
||||||
|
data["file.source"] = value.(string)
|
||||||
|
|
||||||
|
filesMap[dataID] = data
|
||||||
|
|
||||||
|
switch key {
|
||||||
|
case "m3u":
|
||||||
|
Settings.Files.M3U = filesMap
|
||||||
|
nextStep = 3
|
||||||
|
|
||||||
|
err = getProviderData(key, dataID)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
ShowError(err, 000)
|
||||||
|
delete(filesMap, dataID)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = buildDatabaseDVR()
|
||||||
|
if err != nil {
|
||||||
|
ShowError(err, 000)
|
||||||
|
delete(filesMap, dataID)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if Settings.EpgSource == "PMS" {
|
||||||
|
nextStep = 10
|
||||||
|
}
|
||||||
|
|
||||||
|
case "xmltv":
|
||||||
|
Settings.Files.XMLTV = filesMap
|
||||||
|
nextStep = 10
|
||||||
|
|
||||||
|
err = getProviderData(key, dataID)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
|
||||||
|
ShowError(err, 000)
|
||||||
|
delete(filesMap, dataID)
|
||||||
|
return
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
buildXEPG(false)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
err = saveSettings(Settings)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filterregeln erstellen
|
||||||
|
func createFilterRules() (err error) {
|
||||||
|
|
||||||
|
Data.Filter = nil
|
||||||
|
var dataFilter Filter
|
||||||
|
|
||||||
|
for _, f := range Settings.Filter {
|
||||||
|
|
||||||
|
var filter FilterStruct
|
||||||
|
|
||||||
|
var exclude, include string
|
||||||
|
|
||||||
|
err = json.Unmarshal([]byte(mapToJSON(f)), &filter)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch filter.Type {
|
||||||
|
|
||||||
|
case "custom-filter":
|
||||||
|
dataFilter.CaseSensitive = filter.CaseSensitive
|
||||||
|
dataFilter.Rule = filter.Filter
|
||||||
|
dataFilter.Type = filter.Type
|
||||||
|
|
||||||
|
Data.Filter = append(Data.Filter, dataFilter)
|
||||||
|
|
||||||
|
case "group-title":
|
||||||
|
if len(filter.Include) > 0 {
|
||||||
|
include = fmt.Sprintf(" {%s}", filter.Include)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(filter.Exclude) > 0 {
|
||||||
|
exclude = fmt.Sprintf(" !{%s}", filter.Exclude)
|
||||||
|
}
|
||||||
|
|
||||||
|
dataFilter.CaseSensitive = filter.CaseSensitive
|
||||||
|
dataFilter.Rule = fmt.Sprintf("%s%s%s", filter.Filter, include, exclude)
|
||||||
|
dataFilter.Type = filter.Type
|
||||||
|
|
||||||
|
Data.Filter = append(Data.Filter, dataFilter)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Datenbank für das DVR System erstellen
|
||||||
|
func buildDatabaseDVR() (err error) {
|
||||||
|
|
||||||
|
System.ScanInProgress = 1
|
||||||
|
|
||||||
|
Data.Streams.All = make([]interface{}, 0)
|
||||||
|
Data.Streams.Active = make([]interface{}, 0)
|
||||||
|
Data.Streams.Inactive = make([]interface{}, 0)
|
||||||
|
Data.Playlist.M3U.Groups.Text = []string{}
|
||||||
|
Data.Playlist.M3U.Groups.Value = []string{}
|
||||||
|
Data.StreamPreviewUI.Active = []string{}
|
||||||
|
Data.StreamPreviewUI.Inactive = []string{}
|
||||||
|
|
||||||
|
var availableFileTypes = []string{"m3u", "hdhr"}
|
||||||
|
|
||||||
|
var tmpGroupsM3U = make(map[string]int64)
|
||||||
|
|
||||||
|
err = createFilterRules()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, fileType := range availableFileTypes {
|
||||||
|
|
||||||
|
var playlistFile = getLocalProviderFiles(fileType)
|
||||||
|
|
||||||
|
for n, i := range playlistFile {
|
||||||
|
|
||||||
|
var channels []interface{}
|
||||||
|
var groupTitle, tvgID, uuid int = 0, 0, 0
|
||||||
|
var keys = []string{"group-title", "tvg-id", "uuid"}
|
||||||
|
var compatibility = make(map[string]int)
|
||||||
|
|
||||||
|
var id = strings.TrimSuffix(getFilenameFromPath(i), path.Ext(getFilenameFromPath(i)))
|
||||||
|
var playlistName = getProviderParameter(id, fileType, "name")
|
||||||
|
|
||||||
|
switch fileType {
|
||||||
|
|
||||||
|
case "m3u":
|
||||||
|
channels, err = parsePlaylist(i, fileType)
|
||||||
|
case "hdhr":
|
||||||
|
channels, err = parsePlaylist(i, fileType)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
ShowError(err, 1005)
|
||||||
|
err = errors.New(playlistName + ": Local copy of the file no longer exists")
|
||||||
|
ShowError(err, 0)
|
||||||
|
playlistFile = append(playlistFile[:n], playlistFile[n+1:]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Streams analysieren
|
||||||
|
for _, stream := range channels {
|
||||||
|
|
||||||
|
var s = stream.(map[string]string)
|
||||||
|
s["_file.m3u.path"] = i
|
||||||
|
s["_file.m3u.name"] = playlistName
|
||||||
|
s["_file.m3u.id"] = id
|
||||||
|
|
||||||
|
// Kompatibilität berechnen
|
||||||
|
for _, key := range keys {
|
||||||
|
|
||||||
|
switch key {
|
||||||
|
case "uuid":
|
||||||
|
if value, ok := s["_uuid.key"]; ok {
|
||||||
|
if len(value) > 0 {
|
||||||
|
uuid++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case "group-title":
|
||||||
|
if value, ok := s[key]; ok {
|
||||||
|
if len(value) > 0 {
|
||||||
|
|
||||||
|
if _, ok := tmpGroupsM3U[value]; ok {
|
||||||
|
tmpGroupsM3U[value]++
|
||||||
|
} else {
|
||||||
|
tmpGroupsM3U[value] = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
groupTitle++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case "tvg-id":
|
||||||
|
if value, ok := s[key]; ok {
|
||||||
|
if len(value) > 0 {
|
||||||
|
tvgID++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Data.Streams.All = append(Data.Streams.All, stream)
|
||||||
|
|
||||||
|
// Neuer Filter ab Version 1.3.0
|
||||||
|
var preview string
|
||||||
|
var status = filterThisStream(stream)
|
||||||
|
|
||||||
|
if name, ok := s["name"]; ok {
|
||||||
|
var group string
|
||||||
|
|
||||||
|
if v, ok := s["group-title"]; ok {
|
||||||
|
group = v
|
||||||
|
}
|
||||||
|
|
||||||
|
preview = fmt.Sprintf("%s [%s]", name, group)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
switch status {
|
||||||
|
|
||||||
|
case true:
|
||||||
|
Data.StreamPreviewUI.Active = append(Data.StreamPreviewUI.Active, preview)
|
||||||
|
Data.Streams.Active = append(Data.Streams.Active, stream)
|
||||||
|
|
||||||
|
case false:
|
||||||
|
Data.StreamPreviewUI.Inactive = append(Data.StreamPreviewUI.Inactive, preview)
|
||||||
|
Data.Streams.Inactive = append(Data.Streams.Inactive, stream)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if tvgID == 0 {
|
||||||
|
compatibility["tvg.id"] = 0
|
||||||
|
} else {
|
||||||
|
compatibility["tvg.id"] = int(tvgID * 100 / len(channels))
|
||||||
|
}
|
||||||
|
|
||||||
|
if groupTitle == 0 {
|
||||||
|
compatibility["group.title"] = 0
|
||||||
|
} else {
|
||||||
|
compatibility["group.title"] = int(groupTitle * 100 / len(channels))
|
||||||
|
}
|
||||||
|
|
||||||
|
if uuid == 0 {
|
||||||
|
compatibility["stream.id"] = 0
|
||||||
|
} else {
|
||||||
|
compatibility["stream.id"] = int(uuid * 100 / len(channels))
|
||||||
|
}
|
||||||
|
|
||||||
|
compatibility["streams"] = len(channels)
|
||||||
|
|
||||||
|
setProviderCompatibility(id, fileType, compatibility)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
for group, count := range tmpGroupsM3U {
|
||||||
|
var text = fmt.Sprintf("%s (%d)", group, count)
|
||||||
|
var value = fmt.Sprintf("%s", group)
|
||||||
|
Data.Playlist.M3U.Groups.Text = append(Data.Playlist.M3U.Groups.Text, text)
|
||||||
|
Data.Playlist.M3U.Groups.Value = append(Data.Playlist.M3U.Groups.Value, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Strings(Data.Playlist.M3U.Groups.Text)
|
||||||
|
sort.Strings(Data.Playlist.M3U.Groups.Value)
|
||||||
|
|
||||||
|
if len(Data.Streams.Active) == 0 && len(Data.Streams.All) <= System.DVRLimit && len(Settings.Filter) == 0 {
|
||||||
|
Data.Streams.Active = Data.Streams.All
|
||||||
|
Data.Streams.Inactive = make([]interface{}, 0)
|
||||||
|
|
||||||
|
Data.StreamPreviewUI.Active = Data.StreamPreviewUI.Inactive
|
||||||
|
Data.StreamPreviewUI.Inactive = []string{}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(Data.Streams.Active) > System.DVRLimit {
|
||||||
|
showWarning(2000)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(Settings.Filter) == 0 && len(Data.Streams.All) > System.DVRLimit {
|
||||||
|
showWarning(2001)
|
||||||
|
}
|
||||||
|
|
||||||
|
System.ScanInProgress = 0
|
||||||
|
showInfo(fmt.Sprintf("All streams:%d", len(Data.Streams.All)))
|
||||||
|
showInfo(fmt.Sprintf("Active streams:%d", len(Data.Streams.Active)))
|
||||||
|
showInfo(fmt.Sprintf("Filter:%d", len(Data.Filter)))
|
||||||
|
|
||||||
|
sort.Strings(Data.StreamPreviewUI.Active)
|
||||||
|
sort.Strings(Data.StreamPreviewUI.Inactive)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Speicherort aller lokalen Providerdateien laden, immer für eine Dateityp (M3U, XMLTV usw.)
|
||||||
|
func getLocalProviderFiles(fileType string) (localFiles []string) {
|
||||||
|
|
||||||
|
var fileExtension string
|
||||||
|
var dataMap = make(map[string]interface{})
|
||||||
|
|
||||||
|
switch fileType {
|
||||||
|
|
||||||
|
case "m3u":
|
||||||
|
fileExtension = ".m3u"
|
||||||
|
dataMap = Settings.Files.M3U
|
||||||
|
|
||||||
|
case "hdhr":
|
||||||
|
fileExtension = ".json"
|
||||||
|
dataMap = Settings.Files.HDHR
|
||||||
|
|
||||||
|
case "xmltv":
|
||||||
|
fileExtension = ".xml"
|
||||||
|
dataMap = Settings.Files.XMLTV
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
for dataID := range dataMap {
|
||||||
|
localFiles = append(localFiles, System.Folder.Data+dataID+fileExtension)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Providerparameter anhand von dem Key ausgeben
|
||||||
|
func getProviderParameter(id, fileType, key string) (s string) {
|
||||||
|
|
||||||
|
var dataMap = make(map[string]interface{})
|
||||||
|
|
||||||
|
switch fileType {
|
||||||
|
case "m3u":
|
||||||
|
dataMap = Settings.Files.M3U
|
||||||
|
|
||||||
|
case "hdhr":
|
||||||
|
dataMap = Settings.Files.HDHR
|
||||||
|
|
||||||
|
case "xmltv":
|
||||||
|
dataMap = Settings.Files.XMLTV
|
||||||
|
}
|
||||||
|
|
||||||
|
if data, ok := dataMap[id].(map[string]interface{}); ok {
|
||||||
|
|
||||||
|
if v, ok := data[key].(string); ok {
|
||||||
|
s = v
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := data[key].(float64); ok {
|
||||||
|
s = strconv.Itoa(int(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Provider Statistiken Kompatibilität aktualisieren
|
||||||
|
func setProviderCompatibility(id, fileType string, compatibility map[string]int) {
|
||||||
|
|
||||||
|
var dataMap = make(map[string]interface{})
|
||||||
|
|
||||||
|
switch fileType {
|
||||||
|
case "m3u":
|
||||||
|
dataMap = Settings.Files.M3U
|
||||||
|
|
||||||
|
case "hdhr":
|
||||||
|
dataMap = Settings.Files.HDHR
|
||||||
|
|
||||||
|
case "xmltv":
|
||||||
|
dataMap = Settings.Files.XMLTV
|
||||||
|
}
|
||||||
|
|
||||||
|
if data, ok := dataMap[id].(map[string]interface{}); ok {
|
||||||
|
|
||||||
|
data["compatibility"] = compatibility
|
||||||
|
|
||||||
|
switch fileType {
|
||||||
|
case "m3u":
|
||||||
|
Settings.Files.M3U = dataMap
|
||||||
|
case "hdhr":
|
||||||
|
Settings.Files.HDHR = dataMap
|
||||||
|
case "xmltv":
|
||||||
|
Settings.Files.XMLTV = dataMap
|
||||||
|
}
|
||||||
|
|
||||||
|
saveSettings(Settings)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,236 @@
|
|||||||
|
package src
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"encoding/xml"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func makeInteraceFromHDHR(content []byte, playlistName, id string) (channels []interface{}, err error) {
|
||||||
|
|
||||||
|
var hdhrData []interface{}
|
||||||
|
|
||||||
|
err = json.Unmarshal(content, &hdhrData)
|
||||||
|
if err == nil {
|
||||||
|
|
||||||
|
for _, d := range hdhrData {
|
||||||
|
|
||||||
|
var channel = make(map[string]string)
|
||||||
|
var data = d.(map[string]interface{})
|
||||||
|
|
||||||
|
channel["group-title"] = playlistName
|
||||||
|
channel["name"] = data["GuideName"].(string)
|
||||||
|
channel["tvg-id"] = data["GuideName"].(string)
|
||||||
|
channel["url"] = data["URL"].(string)
|
||||||
|
channel["ID-"+id] = data["GuideNumber"].(string)
|
||||||
|
channel["_uuid.key"] = "ID-" + id
|
||||||
|
channel["_values"] = playlistName + " " + channel["name"]
|
||||||
|
|
||||||
|
channels = append(channels, channel)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCapability() (xmlContent []byte, err error) {
|
||||||
|
|
||||||
|
var capability Capability
|
||||||
|
var buffer bytes.Buffer
|
||||||
|
|
||||||
|
capability.Xmlns = "urn:schemas-upnp-org:device-1-0"
|
||||||
|
capability.URLBase = System.ServerProtocol.WEB + "://" + System.Domain
|
||||||
|
|
||||||
|
capability.SpecVersion.Major = 1
|
||||||
|
capability.SpecVersion.Minor = 0
|
||||||
|
|
||||||
|
capability.Device.DeviceType = "urn:schemas-upnp-org:device:MediaServer:1"
|
||||||
|
capability.Device.FriendlyName = System.Name
|
||||||
|
capability.Device.Manufacturer = "Silicondust"
|
||||||
|
capability.Device.ModelName = "HDTC-2US"
|
||||||
|
capability.Device.ModelNumber = "HDTC-2US"
|
||||||
|
capability.Device.SerialNumber = ""
|
||||||
|
capability.Device.UDN = "uuid:" + System.DeviceID
|
||||||
|
|
||||||
|
output, err := xml.MarshalIndent(capability, " ", " ")
|
||||||
|
if err != nil {
|
||||||
|
ShowError(err, 1003)
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer.Write([]byte(xml.Header))
|
||||||
|
buffer.Write([]byte(output))
|
||||||
|
xmlContent = buffer.Bytes()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDiscover() (jsonContent []byte, err error) {
|
||||||
|
|
||||||
|
var discover Discover
|
||||||
|
|
||||||
|
discover.BaseURL = System.ServerProtocol.WEB + "://" + System.Domain
|
||||||
|
discover.DeviceAuth = System.AppName
|
||||||
|
discover.DeviceID = System.DeviceID
|
||||||
|
discover.FirmwareName = "bin_" + System.Version
|
||||||
|
discover.FirmwareVersion = System.Version
|
||||||
|
discover.FriendlyName = System.Name
|
||||||
|
|
||||||
|
discover.LineupURL = fmt.Sprintf("%s://%s/lineup.json", System.ServerProtocol.DVR, System.Domain)
|
||||||
|
discover.Manufacturer = "Golang"
|
||||||
|
discover.ModelNumber = System.Version
|
||||||
|
discover.TunerCount = Settings.Tuner
|
||||||
|
|
||||||
|
jsonContent, err = json.MarshalIndent(discover, "", " ")
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getLineupStatus() (jsonContent []byte, err error) {
|
||||||
|
|
||||||
|
var lineupStatus LineupStatus
|
||||||
|
|
||||||
|
lineupStatus.ScanInProgress = System.ScanInProgress
|
||||||
|
lineupStatus.ScanPossible = 0
|
||||||
|
lineupStatus.Source = "Cable"
|
||||||
|
lineupStatus.SourceList = []string{"IPTV", "Cable"}
|
||||||
|
|
||||||
|
jsonContent, err = json.MarshalIndent(lineupStatus, "", " ")
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getLineup() (jsonContent []byte, err error) {
|
||||||
|
|
||||||
|
var lineup Lineup
|
||||||
|
|
||||||
|
switch Settings.EpgSource {
|
||||||
|
|
||||||
|
case "PMS":
|
||||||
|
for i, dsa := range Data.Streams.Active {
|
||||||
|
|
||||||
|
var m3uChannel M3UChannelStructXEPG
|
||||||
|
|
||||||
|
err = json.Unmarshal([]byte(mapToJSON(dsa)), &m3uChannel)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var stream LineupStream
|
||||||
|
stream.GuideName = m3uChannel.Name
|
||||||
|
switch len(m3uChannel.UUIDValue) {
|
||||||
|
|
||||||
|
case 0:
|
||||||
|
stream.GuideNumber = fmt.Sprintf("%d", i+1000)
|
||||||
|
guideNumber, err := getGuideNumberPMS(stream.GuideName)
|
||||||
|
if err != nil {
|
||||||
|
ShowError(err, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.GuideNumber = guideNumber
|
||||||
|
|
||||||
|
default:
|
||||||
|
stream.GuideNumber = m3uChannel.UUIDValue
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.URL, err = createStreamingURL("DVR", m3uChannel.FileM3UID, stream.GuideNumber, m3uChannel.Name, m3uChannel.URL)
|
||||||
|
if err == nil {
|
||||||
|
lineup = append(lineup, stream)
|
||||||
|
} else {
|
||||||
|
ShowError(err, 1202)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
case "XEPG":
|
||||||
|
for _, dxc := range Data.XEPG.Channels {
|
||||||
|
|
||||||
|
var xepgChannel XEPGChannelStruct
|
||||||
|
err = json.Unmarshal([]byte(mapToJSON(dxc)), &xepgChannel)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if xepgChannel.XActive == true {
|
||||||
|
var stream LineupStream
|
||||||
|
stream.GuideName = xepgChannel.XName
|
||||||
|
stream.GuideNumber = xepgChannel.XChannelID
|
||||||
|
//stream.URL = fmt.Sprintf("%s://%s/stream/%s-%s", System.ServerProtocol.DVR, System.Domain, xepgChannel.FileM3UID, base64.StdEncoding.EncodeToString([]byte(xepgChannel.URL)))
|
||||||
|
stream.URL, err = createStreamingURL("DVR", xepgChannel.FileM3UID, xepgChannel.XChannelID, xepgChannel.XName, xepgChannel.URL)
|
||||||
|
if err == nil {
|
||||||
|
lineup = append(lineup, stream)
|
||||||
|
} else {
|
||||||
|
ShowError(err, 1202)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonContent, err = json.MarshalIndent(lineup, "", " ")
|
||||||
|
|
||||||
|
Data.Cache.PMS = nil
|
||||||
|
|
||||||
|
saveMapToJSONFile(System.File.URLS, Data.Cache.StreamingURLS)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getGuideNumberPMS(channelName string) (pmsID string, err error) {
|
||||||
|
|
||||||
|
if len(Data.Cache.PMS) == 0 {
|
||||||
|
|
||||||
|
Data.Cache.PMS = make(map[string]string)
|
||||||
|
|
||||||
|
pms, err := loadJSONFileToMap(System.File.PMS)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, value := range pms {
|
||||||
|
Data.Cache.PMS[key] = value.(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
var getNewID = func(channelName string) (id string) {
|
||||||
|
|
||||||
|
var i int
|
||||||
|
|
||||||
|
newID:
|
||||||
|
|
||||||
|
var ids []string
|
||||||
|
id = fmt.Sprintf("id-%d", i)
|
||||||
|
|
||||||
|
for _, v := range Data.Cache.PMS {
|
||||||
|
ids = append(ids, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
if indexOfString(id, ids) != -1 {
|
||||||
|
i++
|
||||||
|
goto newID
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if value, ok := Data.Cache.PMS[channelName]; ok {
|
||||||
|
|
||||||
|
pmsID = value
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
pmsID = getNewID(channelName)
|
||||||
|
Data.Cache.PMS[channelName] = pmsID
|
||||||
|
saveMapToJSONFile(System.File.PMS, Data.Cache.PMS)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -0,0 +1,147 @@
|
|||||||
|
package src
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
var htmlFolder string
|
||||||
|
var goFile string
|
||||||
|
var mapName string
|
||||||
|
var packageName string
|
||||||
|
|
||||||
|
var blankMap = make(map[string]interface{})
|
||||||
|
|
||||||
|
// 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 string
|
||||||
|
|
||||||
|
for key := range blankMap {
|
||||||
|
var newKey = key
|
||||||
|
content += ` ` + mapName + `["` + newKey + `"` + `] = "` + blankMap[key].(string) + `"` + "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
return content
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,153 @@
|
|||||||
|
package src
|
||||||
|
|
||||||
|
import (
|
||||||
|
b64 "encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getCacheImageURL(url string) (cacheImageURL string) {
|
||||||
|
|
||||||
|
url = strings.Trim(url, "\r\n")
|
||||||
|
|
||||||
|
var urlMD5 = getMD5(url)
|
||||||
|
var fileExtension = filepath.Ext(url)
|
||||||
|
|
||||||
|
if indexOfString(urlMD5+fileExtension, Data.Cache.ImagesFiles) == -1 {
|
||||||
|
Data.Cache.ImagesFiles = append(Data.Cache.ImagesFiles, urlMD5+fileExtension)
|
||||||
|
}
|
||||||
|
|
||||||
|
if Settings.CacheImages == false || System.ImageCachingInProgress == 1 {
|
||||||
|
return url
|
||||||
|
}
|
||||||
|
|
||||||
|
if indexOfString(urlMD5+fileExtension, Data.Cache.ImagesCache) != -1 {
|
||||||
|
|
||||||
|
cacheImageURL = fmt.Sprintf("%s://%s/images/%s%s", System.ServerProtocol.WEB, System.Domain, urlMD5, fileExtension)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if strings.Contains(url, System.Domain+"/images/") == false {
|
||||||
|
|
||||||
|
if indexOfString(url, Data.Cache.ImagesURLS) == -1 {
|
||||||
|
Data.Cache.ImagesURLS = append(Data.Cache.ImagesURLS, url)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
cacheImageURL = url
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func cachingImages() {
|
||||||
|
|
||||||
|
if Settings.CacheImages == false || System.ImageCachingInProgress == 1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
System.ImageCachingInProgress = 1
|
||||||
|
|
||||||
|
showInfo("Image Caching:Images are cached")
|
||||||
|
|
||||||
|
for _, url := range Data.Cache.ImagesURLS {
|
||||||
|
|
||||||
|
if len(url) > 0 {
|
||||||
|
cacheImage(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
showInfo("Image Caching:Done")
|
||||||
|
|
||||||
|
// Bilder die nicht mehr verwendet werden, werden gelöscht
|
||||||
|
files, err := ioutil.ReadDir(System.Folder.ImagesCache)
|
||||||
|
if err != nil {
|
||||||
|
ShowError(err, 0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, file := range files {
|
||||||
|
|
||||||
|
if indexOfString(file.Name(), Data.Cache.ImagesFiles) == -1 {
|
||||||
|
|
||||||
|
var debug = fmt.Sprintf("Image Caching:Remove file: %s %s %d", System.Folder.ImagesCache+file.Name(), file.Name(), len(file.Name()))
|
||||||
|
showDebug(debug, 1)
|
||||||
|
err := os.RemoveAll(System.Folder.ImagesCache + file.Name())
|
||||||
|
if err != nil {
|
||||||
|
ShowError(err, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
System.ImageCachingInProgress = 0
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func cacheImage(url string) {
|
||||||
|
|
||||||
|
var debug string
|
||||||
|
var urlMD5 = getMD5(url)
|
||||||
|
var fileExtension = filepath.Ext(url)
|
||||||
|
|
||||||
|
debug = fmt.Sprintf("Image Caching:File: %s Download: %s", urlMD5+fileExtension, url)
|
||||||
|
showDebug(debug, 1)
|
||||||
|
|
||||||
|
resp, err := http.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var filePath = System.Folder.ImagesCache + urlMD5 + fileExtension
|
||||||
|
|
||||||
|
// Datei speichern
|
||||||
|
file, err := os.Create(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
_, err = io.Copy(file, resp.Body)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func uploadLogo(input, filename string) (logoURL string, err error) {
|
||||||
|
|
||||||
|
b64data := input[strings.IndexByte(input, ',')+1:]
|
||||||
|
|
||||||
|
// BAse64 in bytes umwandeln un speichern
|
||||||
|
sDec, err := b64.StdEncoding.DecodeString(b64data)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var file = fmt.Sprintf("%s%s", System.Folder.ImagesUpload, filename)
|
||||||
|
|
||||||
|
err = writeByteToFile(file, sDec)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
logoURL = fmt.Sprintf("%s://%s/data_images/%s", System.ServerProtocol.WEB, System.Domain, filename)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,592 @@
|
|||||||
|
package authentication
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"crypto/hmac"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/base64"
|
||||||
|
|
||||||
|
"time"
|
||||||
|
//"fmt"
|
||||||
|
//"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
const tokenLength = 40
|
||||||
|
const saltLength = 20
|
||||||
|
const idLength = 10
|
||||||
|
|
||||||
|
var tokenValidity int
|
||||||
|
var database string
|
||||||
|
|
||||||
|
var databaseFile = "authentication.json"
|
||||||
|
|
||||||
|
var data = make(map[string]interface{})
|
||||||
|
var tokens = make(map[string]interface{})
|
||||||
|
|
||||||
|
var initAuthentication = false
|
||||||
|
|
||||||
|
// Cookie : cookie
|
||||||
|
type Cookie struct {
|
||||||
|
Name string
|
||||||
|
Value string
|
||||||
|
Path string
|
||||||
|
Domain string
|
||||||
|
Expires time.Time
|
||||||
|
RawExpires string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Framework examples
|
||||||
|
|
||||||
|
/*
|
||||||
|
func main() {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
var checkErr = func(err error) {
|
||||||
|
log.Println(err)
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = Init("", 10) // Path to save the data, Validity of tokens in minutes | (error)
|
||||||
|
if err != nil {
|
||||||
|
checkErr(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
err = CreateDefaultUser("admin", "123")
|
||||||
|
if err != nil {
|
||||||
|
checkErr(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
err = CreateNewUser("xteve", "xteve") // Username, Password | (error)
|
||||||
|
if err != nil {
|
||||||
|
checkErr(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
err, token := UserAuthentication("xteve", "xteve") // Username, Password | (error, token)
|
||||||
|
if err != nil {
|
||||||
|
checkErr(err)
|
||||||
|
} else {
|
||||||
|
fmt.Println("UserAuthentication()")
|
||||||
|
fmt.Println("Token:", token)
|
||||||
|
fmt.Println("---")
|
||||||
|
}
|
||||||
|
|
||||||
|
err, newToken := CheckTheValidityOfTheToken(token) // Current token | (error, new token)
|
||||||
|
if err != nil {
|
||||||
|
checkErr(err)
|
||||||
|
} else {
|
||||||
|
fmt.Println("CheckTheValidityOfTheToken()")
|
||||||
|
fmt.Println("New Token:", newToken)
|
||||||
|
fmt.Println("---")
|
||||||
|
}
|
||||||
|
|
||||||
|
err, userID := GetUserID(newToken) // Current token | (error, user id)
|
||||||
|
if err != nil {
|
||||||
|
checkErr(err)
|
||||||
|
} else {
|
||||||
|
fmt.Println("GetUserID()")
|
||||||
|
fmt.Println("User ID:", userID)
|
||||||
|
fmt.Println("---")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var userData = make(map[string]interface{})
|
||||||
|
userData["type"] = "Administrator"
|
||||||
|
err = WriteUserData(userID, userData) // User id, user data | (error)
|
||||||
|
if err != nil {
|
||||||
|
checkErr(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err, userData = ReadUserData(userID) // User id | (error, userData)
|
||||||
|
if err != nil {
|
||||||
|
checkErr(err)
|
||||||
|
} else {
|
||||||
|
fmt.Println("ReadUserData()")
|
||||||
|
fmt.Println("User data:", userData)
|
||||||
|
fmt.Println("---")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = RemoveUser(userID)
|
||||||
|
if err != nil {
|
||||||
|
checkErr(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Init : databasePath = Path to authentication.json
|
||||||
|
func Init(databasePath string, validity int) (err error) {
|
||||||
|
database = filepath.Dir(databasePath) + string(os.PathSeparator) + databaseFile
|
||||||
|
|
||||||
|
// Check if the database already exists
|
||||||
|
if _, err = os.Stat(database); os.IsNotExist(err) {
|
||||||
|
// Create an empty database
|
||||||
|
var defaults = make(map[string]interface{})
|
||||||
|
defaults["dbVersion"] = "1.0"
|
||||||
|
defaults["hash"] = "sha256"
|
||||||
|
defaults["users"] = make(map[string]interface{})
|
||||||
|
|
||||||
|
if saveDatabase(defaults) != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loading the database
|
||||||
|
err = loadDatabase()
|
||||||
|
|
||||||
|
// Set Token Validity
|
||||||
|
tokenValidity = validity
|
||||||
|
initAuthentication = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateDefaultUser = created efault user
|
||||||
|
func CreateDefaultUser(username, password string) (err error) {
|
||||||
|
|
||||||
|
err = checkInit()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var users = data["users"].(map[string]interface{})
|
||||||
|
// Check if the default user exists
|
||||||
|
if len(users) > 0 {
|
||||||
|
err = createError(001)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var defaults = defaultsForNewUser(username, password)
|
||||||
|
users[defaults["_id"].(string)] = defaults
|
||||||
|
saveDatabase(data)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateNewUser : create new user
|
||||||
|
func CreateNewUser(username, password string) (userID string, err error) {
|
||||||
|
|
||||||
|
err = checkInit()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var checkIfTheUserAlreadyExists = func(username string, userData map[string]interface{}) (err error) {
|
||||||
|
var salt = userData["_salt"].(string)
|
||||||
|
var loginUsername = userData["_username"].(string)
|
||||||
|
|
||||||
|
if SHA256(username, salt) == loginUsername {
|
||||||
|
err = createError(020)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var users = data["users"].(map[string]interface{})
|
||||||
|
for _, userData := range users {
|
||||||
|
err = checkIfTheUserAlreadyExists(username, userData.(map[string]interface{}))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var defaults = defaultsForNewUser(username, password)
|
||||||
|
userID = defaults["_id"].(string)
|
||||||
|
users[userID] = defaults
|
||||||
|
|
||||||
|
saveDatabase(data)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserAuthentication : user authentication
|
||||||
|
func UserAuthentication(username, password string) (token string, err error) {
|
||||||
|
|
||||||
|
err = checkInit()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var login = func(username, password string, loginData map[string]interface{}) (err error) {
|
||||||
|
err = createError(010)
|
||||||
|
|
||||||
|
var salt = loginData["_salt"].(string)
|
||||||
|
var loginUsername = loginData["_username"].(string)
|
||||||
|
var loginPassword = loginData["_password"].(string)
|
||||||
|
|
||||||
|
if SHA256(username, salt) == loginUsername {
|
||||||
|
if SHA256(password, salt) == loginPassword {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var users = data["users"].(map[string]interface{})
|
||||||
|
for id, loginData := range users {
|
||||||
|
err = login(username, password, loginData.(map[string]interface{}))
|
||||||
|
if err == nil {
|
||||||
|
token = setToken(id, "-")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckTheValidityOfTheToken : check token
|
||||||
|
func CheckTheValidityOfTheToken(token string) (newToken string, err error) {
|
||||||
|
|
||||||
|
err = checkInit()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = createError(011)
|
||||||
|
|
||||||
|
if v, ok := tokens[token]; ok {
|
||||||
|
var expires = v.(map[string]interface{})["expires"].(time.Time)
|
||||||
|
var userID = v.(map[string]interface{})["id"].(string)
|
||||||
|
|
||||||
|
if expires.Sub(time.Now().Local()) < 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
newToken = setToken(userID, token)
|
||||||
|
|
||||||
|
err = nil
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserID : get user ID
|
||||||
|
func GetUserID(token string) (userID string, err error) {
|
||||||
|
|
||||||
|
err = checkInit()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = createError(002)
|
||||||
|
|
||||||
|
if v, ok := tokens[token]; ok {
|
||||||
|
var expires = v.(map[string]interface{})["expires"].(time.Time)
|
||||||
|
userID = v.(map[string]interface{})["id"].(string)
|
||||||
|
|
||||||
|
if expires.Sub(time.Now().Local()) < 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteUserData : save user date
|
||||||
|
func WriteUserData(userID string, userData map[string]interface{}) (err error) {
|
||||||
|
|
||||||
|
err = checkInit()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = createError(030)
|
||||||
|
|
||||||
|
if v, ok := data["users"].(map[string]interface{})[userID].(map[string]interface{}); ok {
|
||||||
|
|
||||||
|
v["data"] = userData
|
||||||
|
err = saveDatabase(data)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadUserData : load user date
|
||||||
|
func ReadUserData(userID string) (userData map[string]interface{}, err error) {
|
||||||
|
|
||||||
|
err = checkInit()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = createError(031)
|
||||||
|
|
||||||
|
if v, ok := data["users"].(map[string]interface{})[userID].(map[string]interface{}); ok {
|
||||||
|
userData = v["data"].(map[string]interface{})
|
||||||
|
err = nil
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveUser : remove user
|
||||||
|
func RemoveUser(userID string) (err error) {
|
||||||
|
|
||||||
|
err = checkInit()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = createError(032)
|
||||||
|
|
||||||
|
if _, ok := data["users"].(map[string]interface{})[userID]; ok {
|
||||||
|
|
||||||
|
delete(data["users"].(map[string]interface{}), userID)
|
||||||
|
err = saveDatabase(data)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetDefaultUserData : set default user data
|
||||||
|
func SetDefaultUserData(defaults map[string]interface{}) (err error) {
|
||||||
|
|
||||||
|
allUserData, err := GetAllUserData()
|
||||||
|
|
||||||
|
for _, d := range allUserData {
|
||||||
|
var data = d.(map[string]interface{})["data"].(map[string]interface{})
|
||||||
|
var userID = d.(map[string]interface{})["_id"].(string)
|
||||||
|
|
||||||
|
for k, v := range defaults {
|
||||||
|
if _, ok := data[k]; ok {
|
||||||
|
// Key exist
|
||||||
|
} else {
|
||||||
|
data[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = WriteUserData(userID, data)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChangeCredentials : change credentials
|
||||||
|
func ChangeCredentials(userID, username, password string) (err error) {
|
||||||
|
err = checkInit()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = createError(032)
|
||||||
|
|
||||||
|
if userData, ok := data["users"].(map[string]interface{})[userID]; ok {
|
||||||
|
//var userData = tmp.(map[string]interface{})
|
||||||
|
var salt = userData.(map[string]interface{})["_salt"].(string)
|
||||||
|
|
||||||
|
if len(username) > 0 {
|
||||||
|
userData.(map[string]interface{})["_username"] = SHA256(username, salt)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(password) > 0 {
|
||||||
|
userData.(map[string]interface{})["_password"] = SHA256(password, salt)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = saveDatabase(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllUserData : get all user data
|
||||||
|
func GetAllUserData() (allUserData map[string]interface{}, err error) {
|
||||||
|
|
||||||
|
err = checkInit()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(data) == 0 {
|
||||||
|
var defaults = make(map[string]interface{})
|
||||||
|
defaults["dbVersion"] = "1.0"
|
||||||
|
defaults["hash"] = "sha256"
|
||||||
|
defaults["users"] = make(map[string]interface{})
|
||||||
|
saveDatabase(defaults)
|
||||||
|
data = defaults
|
||||||
|
}
|
||||||
|
|
||||||
|
allUserData = data["users"].(map[string]interface{})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckTheValidityOfTheTokenFromHTTPHeader : get token from HTTP header
|
||||||
|
func CheckTheValidityOfTheTokenFromHTTPHeader(w http.ResponseWriter, r *http.Request) (writer http.ResponseWriter, newToken string, err error) {
|
||||||
|
err = createError(011)
|
||||||
|
for _, cookie := range r.Cookies() {
|
||||||
|
if cookie.Name == "Token" {
|
||||||
|
var token string
|
||||||
|
token, err = CheckTheValidityOfTheToken(cookie.Value)
|
||||||
|
//fmt.Println("T", token, err)
|
||||||
|
writer = SetCookieToken(w, token)
|
||||||
|
newToken = token
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Framework tools
|
||||||
|
|
||||||
|
func checkInit() (err error) {
|
||||||
|
if initAuthentication == false {
|
||||||
|
err = createError(000)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func saveDatabase(tmpMap interface{}) (err error) {
|
||||||
|
|
||||||
|
jsonString, err := json.MarshalIndent(tmpMap, "", " ")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ioutil.WriteFile(database, []byte(jsonString), 0600)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadDatabase() (err error) {
|
||||||
|
jsonString, err := ioutil.ReadFile(database)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal([]byte(jsonString), &data)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// SHA256 : password + salt = sha256 string
|
||||||
|
func SHA256(secret, salt string) string {
|
||||||
|
key := []byte(secret)
|
||||||
|
h := hmac.New(sha256.New, key)
|
||||||
|
h.Write([]byte("_remote_db"))
|
||||||
|
return base64.StdEncoding.EncodeToString(h.Sum(nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
func randomString(n int) string {
|
||||||
|
const alphanum = "-AbCdEfGhIjKlMnOpQrStUvWxYz0123456789aBcDeFgHiJkLmNoPqRsTuVwXyZ_"
|
||||||
|
|
||||||
|
var bytes = make([]byte, n)
|
||||||
|
rand.Read(bytes)
|
||||||
|
for i, b := range bytes {
|
||||||
|
bytes[i] = alphanum[b%byte(len(alphanum))]
|
||||||
|
}
|
||||||
|
return string(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func randomID(n int) string {
|
||||||
|
const alphanum = "ABCDEFGHJKLMNOPQRSTUVWXYZ0123456789"
|
||||||
|
|
||||||
|
var bytes = make([]byte, n)
|
||||||
|
rand.Read(bytes)
|
||||||
|
for i, b := range bytes {
|
||||||
|
bytes[i] = alphanum[b%byte(len(alphanum))]
|
||||||
|
}
|
||||||
|
return string(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func createError(errCode int) (err error) {
|
||||||
|
var errMsg string
|
||||||
|
switch errCode {
|
||||||
|
case 000:
|
||||||
|
errMsg = "Authentication has not yet been initialized"
|
||||||
|
case 001:
|
||||||
|
errMsg = "Default user already exists"
|
||||||
|
case 002:
|
||||||
|
errMsg = "No user id found for this token"
|
||||||
|
case 010:
|
||||||
|
errMsg = "User authentication failed"
|
||||||
|
case 011:
|
||||||
|
errMsg = "Session has expired"
|
||||||
|
case 020:
|
||||||
|
errMsg = "User already exists"
|
||||||
|
case 030:
|
||||||
|
errMsg = "User data could not be saved"
|
||||||
|
case 031:
|
||||||
|
errMsg = "User data could not be read"
|
||||||
|
case 032:
|
||||||
|
errMsg = "User ID was not found"
|
||||||
|
}
|
||||||
|
|
||||||
|
err = errors.New(errMsg)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func defaultsForNewUser(username, password string) map[string]interface{} {
|
||||||
|
var defaults = make(map[string]interface{})
|
||||||
|
var salt = randomString(saltLength)
|
||||||
|
defaults["_username"] = SHA256(username, salt)
|
||||||
|
defaults["_password"] = SHA256(password, salt)
|
||||||
|
defaults["_salt"] = salt
|
||||||
|
defaults["_id"] = "id-" + randomID(idLength)
|
||||||
|
//defaults["_one.time.token"] = randomString(tokenLength)
|
||||||
|
defaults["data"] = make(map[string]interface{})
|
||||||
|
|
||||||
|
return defaults
|
||||||
|
}
|
||||||
|
|
||||||
|
func setToken(id, oldToken string) (newToken string) {
|
||||||
|
delete(tokens, oldToken)
|
||||||
|
|
||||||
|
loopToken:
|
||||||
|
newToken = randomString(tokenLength)
|
||||||
|
if _, ok := tokens[newToken]; ok {
|
||||||
|
goto loopToken
|
||||||
|
}
|
||||||
|
|
||||||
|
var tmp = make(map[string]interface{})
|
||||||
|
tmp["id"] = id
|
||||||
|
tmp["expires"] = time.Now().Local().Add(time.Minute * time.Duration(tokenValidity))
|
||||||
|
|
||||||
|
tokens[newToken] = tmp
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func mapToJSON(tmpMap interface{}) string {
|
||||||
|
jsonString, err := json.MarshalIndent(tmpMap, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return "{}"
|
||||||
|
}
|
||||||
|
return string(jsonString)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCookieToken : set cookie
|
||||||
|
func SetCookieToken(w http.ResponseWriter, token string) http.ResponseWriter {
|
||||||
|
expiration := time.Now().Add(time.Minute * time.Duration(tokenValidity))
|
||||||
|
cookie := http.Cookie{Name: "Token", Value: token, Expires: expiration}
|
||||||
|
http.SetCookie(w, &cookie)
|
||||||
|
return w
|
||||||
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
package m3u
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type M3UStream struct {
|
||||||
|
GroupTitle string `json:"group-title,required"`
|
||||||
|
Name string `json:"name,required"`
|
||||||
|
TvgID string `json:"tvg-id,required"`
|
||||||
|
TvgLogo string `json:"tvg-logo,required"`
|
||||||
|
TvgName string `json:"tvg-name,required"`
|
||||||
|
URL string `json:"url,required"`
|
||||||
|
UUIDKey string `json:"_uuid.key,omitempty"`
|
||||||
|
UUIDValue string `json:"_uuid.value,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStream1(t *testing.T) {
|
||||||
|
|
||||||
|
var file = "test_list_1.m3u"
|
||||||
|
var content, err = ioutil.ReadFile(file)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
streams, err := MakeInterfaceFromM3U(content)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = checkStream(streams)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("Streams:", len(streams))
|
||||||
|
t.Log(streams)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkStream(streamInterface []interface{}) (err error) {
|
||||||
|
|
||||||
|
for i, s := range streamInterface {
|
||||||
|
|
||||||
|
var stream = s.(map[string]string)
|
||||||
|
var m3uStream M3UStream
|
||||||
|
|
||||||
|
jsonString, err := json.MarshalIndent(stream, "", " ")
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
|
||||||
|
err = json.Unmarshal(jsonString, &m3uStream)
|
||||||
|
if err == nil {
|
||||||
|
|
||||||
|
log.Print(fmt.Sprintf("Stream: %d", i))
|
||||||
|
log.Print(fmt.Sprintf("Name*: %s", m3uStream.Name))
|
||||||
|
log.Print(fmt.Sprintf("URL*: %s", m3uStream.URL))
|
||||||
|
log.Print(fmt.Sprintf("tvg-name: %s", m3uStream.TvgName))
|
||||||
|
log.Print(fmt.Sprintf("tvg-id**: %s", m3uStream.TvgID))
|
||||||
|
log.Print(fmt.Sprintf("tvg-logo: %s", m3uStream.TvgLogo))
|
||||||
|
log.Print(fmt.Sprintf("group-title**: %s", m3uStream.GroupTitle))
|
||||||
|
|
||||||
|
if len(m3uStream.UUIDKey) > 0 {
|
||||||
|
log.Print(fmt.Sprintf("UUID key***: %s", m3uStream.UUIDKey))
|
||||||
|
log.Print(fmt.Sprintf("UUID value: %s", m3uStream.UUIDValue))
|
||||||
|
} else {
|
||||||
|
log.Print(fmt.Sprintf("UUID key: false"))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println(fmt.Sprintf("- - - - - (*: Required) | (**: Nice to have) | (***: Love it) - - - - -"))
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
#EXTM3U url-tvg="http://example.com/file.xml" x-tvg-url="http://example.com/xteve.xml"
|
||||||
|
#EXTINF:0 channelID="1" tvg-chno="1" tvg-name="Channel.1" tvg-id="tvg.id.1" tvg-logo="https://example/logo.png" group-title="Group 1",Channel 1
|
||||||
|
http://example.com/stream/1
|
||||||
|
|
||||||
|
#EXTINF:0 channelID="2" tvg-chno="2" tvg-name="Channel.2" tvg-id="tvg.id.2" tvg-logo="https://example/logo.png" group-title="Group 2",Channel 2
|
||||||
|
#123
|
||||||
|
http://example.com/stream/2
|
||||||
@@ -0,0 +1,267 @@
|
|||||||
|
package m3u
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net/url"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MakeInterfaceFromM3U :
|
||||||
|
func MakeInterfaceFromM3U(byteStream []byte) (allChannels []interface{}, err error) {
|
||||||
|
|
||||||
|
var content = string(byteStream)
|
||||||
|
var channelName string
|
||||||
|
|
||||||
|
var parseMetaData = func(channel string) (stream map[string]string) {
|
||||||
|
|
||||||
|
stream = make(map[string]string)
|
||||||
|
var exceptForParameter = `[a-z-A-Z=]*(".*?")`
|
||||||
|
var exceptForChannelName = `,([^\n]*|,[^\r]*)`
|
||||||
|
|
||||||
|
var lines = strings.Split(strings.Replace(channel, "\r\n", "\n", -1), "\n")
|
||||||
|
|
||||||
|
// Zeilen mit # und leerer Zeilen entfernen
|
||||||
|
for i := len(lines) - 1; i >= 0; i-- {
|
||||||
|
|
||||||
|
if len(lines[i]) == 0 || lines[i][0:1] == "#" {
|
||||||
|
lines = append(lines[:i], lines[i+1:]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(lines) >= 2 {
|
||||||
|
|
||||||
|
for _, line := range lines {
|
||||||
|
|
||||||
|
_, err := url.ParseRequestURI(line)
|
||||||
|
|
||||||
|
switch err {
|
||||||
|
|
||||||
|
case nil:
|
||||||
|
stream["url"] = strings.Trim(line, "\r\n")
|
||||||
|
|
||||||
|
default:
|
||||||
|
|
||||||
|
var value string
|
||||||
|
// Alle Parameter parsen
|
||||||
|
var p = regexp.MustCompile(exceptForParameter)
|
||||||
|
var streamParameter = p.FindAllString(line, -1)
|
||||||
|
|
||||||
|
for _, p := range streamParameter {
|
||||||
|
|
||||||
|
line = strings.Replace(line, p, "", 1)
|
||||||
|
|
||||||
|
p = strings.Replace(p, `"`, "", -1)
|
||||||
|
var parameter = strings.Split(p, "=")
|
||||||
|
|
||||||
|
if len(parameter) == 2 {
|
||||||
|
|
||||||
|
// TVG Key als Kleinbuchstaben speichern
|
||||||
|
switch strings.Contains(parameter[0], "tvg") {
|
||||||
|
|
||||||
|
case true:
|
||||||
|
stream[strings.ToLower(parameter[0])] = parameter[1]
|
||||||
|
case false:
|
||||||
|
stream[parameter[0]] = parameter[1]
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// URL's nicht an die Filterfunktion übergeben
|
||||||
|
if !strings.Contains(parameter[1], "://") && len(parameter[1]) > 0 {
|
||||||
|
value = value + parameter[1] + " "
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kanalnamen parsen
|
||||||
|
n := regexp.MustCompile(exceptForChannelName)
|
||||||
|
var name = n.FindAllString(line, 1)
|
||||||
|
|
||||||
|
if len(name) > 0 {
|
||||||
|
channelName = name[0]
|
||||||
|
channelName = strings.Replace(channelName, `,`, "", 1)
|
||||||
|
channelName = strings.TrimRight(channelName, "\r\n")
|
||||||
|
channelName = strings.TrimRight(channelName, " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(channelName) == 0 {
|
||||||
|
|
||||||
|
if v, ok := stream["tvg-name"]; ok {
|
||||||
|
channelName = v
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
channelName = strings.TrimRight(channelName, " ")
|
||||||
|
|
||||||
|
// Kanäle ohne Namen werden augelassen
|
||||||
|
if len(channelName) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
stream["name"] = channelName
|
||||||
|
value = value + channelName
|
||||||
|
|
||||||
|
stream["_values"] = value
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nach eindeutiger ID im Stream suchen
|
||||||
|
for key, value := range stream {
|
||||||
|
|
||||||
|
if !strings.Contains(strings.ToLower(key), "tvg-id") {
|
||||||
|
|
||||||
|
if strings.Contains(strings.ToLower(key), "id") {
|
||||||
|
|
||||||
|
stream["_uuid.key"] = key
|
||||||
|
stream["_uuid.value"] = value
|
||||||
|
//os.Exit(0)
|
||||||
|
break
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//fmt.Println(content)
|
||||||
|
|
||||||
|
if strings.Contains(content, "#EXTM3U") {
|
||||||
|
|
||||||
|
var channels = strings.Split(content, "#EXTINF")
|
||||||
|
|
||||||
|
channels = append(channels[:0], channels[1:]...)
|
||||||
|
|
||||||
|
for _, channel := range channels {
|
||||||
|
|
||||||
|
var stream = parseMetaData(channel)
|
||||||
|
|
||||||
|
if len(stream) > 0 && stream != nil {
|
||||||
|
allChannels = append(allChannels, stream)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
err = errors.New("No valid m3u file")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeInterfaceFromM3U2 :
|
||||||
|
func MakeInterfaceFromM3U2(byteStream []byte) (allChannels []interface{}, err error) {
|
||||||
|
var content = string(byteStream)
|
||||||
|
//var allChannels = make([]interface{}, 0)
|
||||||
|
|
||||||
|
var channels = strings.Split(content, "#EXTINF")
|
||||||
|
|
||||||
|
var parseMetaData = func(metaData string) map[string]string {
|
||||||
|
var values string // Save all values in a key
|
||||||
|
var channel = make(map[string]string)
|
||||||
|
|
||||||
|
var exceptForParameter = `[a-z-A-Z=]*(".*?")`
|
||||||
|
//var exceptForChannelName = `(,[^.$\n]*|,[^.$\r]*)`
|
||||||
|
var exceptForChannelName = `(,[^\n]*|,[^\r]*)`
|
||||||
|
|
||||||
|
var exceptForStreamingURL = `(\n.*?\n|\r.*?\r|\n.*?\z|\r.*?\z)`
|
||||||
|
//var exceptForStreamingURL = `^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?`
|
||||||
|
|
||||||
|
// Parse all parameters
|
||||||
|
p := regexp.MustCompile(exceptForParameter)
|
||||||
|
var parameter = p.FindAllString(metaData, -1)
|
||||||
|
//fmt.Println(parameter)
|
||||||
|
for _, i := range parameter {
|
||||||
|
var remove = i
|
||||||
|
i = strings.Replace(i, `"`, "", -1)
|
||||||
|
if strings.Contains(i, "=") {
|
||||||
|
var item = strings.Split(i, "=")
|
||||||
|
switch strings.Contains(item[0], "tvg") {
|
||||||
|
case true:
|
||||||
|
channel[strings.ToLower(item[0])] = item[1]
|
||||||
|
case false:
|
||||||
|
channel[item[0]] = item[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
switch strings.Contains(item[1], "://") {
|
||||||
|
case false:
|
||||||
|
values = values + item[1] + " "
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
metaData = strings.Replace(metaData, remove, "", 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse channel name (after the comma)
|
||||||
|
n := regexp.MustCompile(exceptForChannelName)
|
||||||
|
var name = n.FindAllString(metaData, 1)
|
||||||
|
//name[len(name) - 1] = strings.Replace(name[len(name) - 1], `\r`, "", -1)
|
||||||
|
|
||||||
|
var channelName string
|
||||||
|
if len(name) == 0 {
|
||||||
|
if v, ok := channel["tvg-name"]; ok {
|
||||||
|
channelName = v
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
channelName = name[len(name)-1][1:len(name[len(name)-1])]
|
||||||
|
}
|
||||||
|
|
||||||
|
channelName = strings.Replace(channelName, `"`, "", -1)
|
||||||
|
|
||||||
|
var replacer = strings.NewReplacer("\n", "", "\r", "")
|
||||||
|
channel["name"] = replacer.Replace(channelName)
|
||||||
|
|
||||||
|
values = values + channelName + " "
|
||||||
|
|
||||||
|
// Parse streaming URL
|
||||||
|
u := regexp.MustCompile(exceptForStreamingURL)
|
||||||
|
var streamingURL = u.FindAllString(metaData, -1)
|
||||||
|
var url = strings.Replace(streamingURL[0], "\n", "", -1)
|
||||||
|
url = strings.Replace(url, "\r", "", -1)
|
||||||
|
url = strings.Trim(url, "\r\n")
|
||||||
|
channel["url"] = url
|
||||||
|
|
||||||
|
channel["_values"] = values
|
||||||
|
|
||||||
|
// Search for a unique ID
|
||||||
|
|
||||||
|
for key, value := range channel {
|
||||||
|
if !strings.Contains(strings.ToLower(key), "tvg-id") {
|
||||||
|
if strings.Contains(strings.ToLower(key), "id") {
|
||||||
|
channel["_uuid.key"] = key
|
||||||
|
channel["_uuid.value"] = value
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return channel
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.Contains(channels[0], "#EXTM3U") {
|
||||||
|
|
||||||
|
for _, thisStream := range channels {
|
||||||
|
if !strings.Contains(thisStream, "#EXTM3U") {
|
||||||
|
var channel = parseMetaData(thisStream)
|
||||||
|
allChannels = append(allChannels, channel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
err = errors.New("No valid m3u file")
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -0,0 +1,129 @@
|
|||||||
|
package up2date
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"runtime"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ClientInfo : Information about the key (NAME OS, ARCH, UUID, KEY)
|
||||||
|
type ClientInfo struct {
|
||||||
|
Arch string `json:"arch,required"`
|
||||||
|
Branch string `json:"branch,required"`
|
||||||
|
CMD string `json:"cmd,omitempty"`
|
||||||
|
Name string `json:"name,required"`
|
||||||
|
OS string `json:"os,required"`
|
||||||
|
URL string `json:"url,required"`
|
||||||
|
|
||||||
|
Response ServerResponse `json:"response,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
//ServerResponse : Response from server after client request
|
||||||
|
type ServerResponse struct {
|
||||||
|
Status bool `json:"status,omitempty"`
|
||||||
|
Reason string `json:"reason,omitempty"`
|
||||||
|
Version string `json:"version,omitempty"`
|
||||||
|
UpdateBIN string `json:"update.url.bin,omitempty"`
|
||||||
|
UpdateZIP string `json:"update.url.zip,omitempty"`
|
||||||
|
Filename string `json:"filename.bin,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Updater : Client infos
|
||||||
|
var Updater ClientInfo
|
||||||
|
|
||||||
|
// UpdateURL : URL for the new binary
|
||||||
|
var UpdateURL string
|
||||||
|
|
||||||
|
// Init : Init
|
||||||
|
func Init() {
|
||||||
|
Updater.OS = runtime.GOOS
|
||||||
|
Updater.Arch = runtime.GOARCH
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetVersion : Information about the latest version
|
||||||
|
func GetVersion() (err error) {
|
||||||
|
|
||||||
|
Updater.CMD = "getVersion"
|
||||||
|
err = serverRequest()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func serverRequest() (err error) {
|
||||||
|
|
||||||
|
var serverResponse ServerResponse
|
||||||
|
jsonByte, err := json.MarshalIndent(Updater, "", " ")
|
||||||
|
if err == nil {
|
||||||
|
|
||||||
|
// Serververbindung prüfen
|
||||||
|
u, err := url.Parse(Updater.URL)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var server = u.Host
|
||||||
|
|
||||||
|
timeout := time.Duration(1 * time.Second)
|
||||||
|
_, err = net.DialTimeout("tcp", server, timeout)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check redirect 301 <---> 308
|
||||||
|
redirect, err := http.NewRequest("POST", Updater.URL, nil)
|
||||||
|
|
||||||
|
client := &http.Client{}
|
||||||
|
client.CheckRedirect = func(redirect *http.Request, via []*http.Request) error {
|
||||||
|
return errors.New("Redirect")
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := client.Do(redirect)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
// Redirect
|
||||||
|
if resp.StatusCode >= 301 && resp.StatusCode <= 308 { //status code 301 <---> 308
|
||||||
|
Updater.URL = resp.Header.Get("Location")
|
||||||
|
} else {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ---
|
||||||
|
|
||||||
|
req, err := http.NewRequest("POST", Updater.URL, bytes.NewBuffer(jsonByte))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
client = &http.Client{}
|
||||||
|
resp, err = client.Do(req)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
//fmt.Println(resp.StatusCode, Updater.URL, Updater.CMD)
|
||||||
|
err = fmt.Errorf(fmt.Sprintf("%d: %s (%s)", resp.StatusCode, http.StatusText(resp.StatusCode), Updater.URL))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
Updater.CMD = ""
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
body, _ := ioutil.ReadAll(resp.Body)
|
||||||
|
|
||||||
|
err = json.Unmarshal(body, &serverResponse)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
Updater.Response = serverResponse
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -0,0 +1,271 @@
|
|||||||
|
package up2date
|
||||||
|
|
||||||
|
import (
|
||||||
|
"archive/zip"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/kardianos/osext"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DoUpdate : Update binary
|
||||||
|
func DoUpdate(fileType, filenameBIN string) (err error) {
|
||||||
|
|
||||||
|
var url string
|
||||||
|
switch fileType {
|
||||||
|
case "bin":
|
||||||
|
url = Updater.Response.UpdateBIN
|
||||||
|
case "zip":
|
||||||
|
url = Updater.Response.UpdateZIP
|
||||||
|
}
|
||||||
|
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "windows":
|
||||||
|
filenameBIN = filenameBIN + ".exe"
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(url) > 0 {
|
||||||
|
log.Println("["+strings.ToUpper(fileType)+"]", "New version ("+Updater.Name+"):", Updater.Response.Version)
|
||||||
|
|
||||||
|
// Download new binary
|
||||||
|
resp, err := http.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
log.Println("["+strings.ToUpper(fileType)+"]", "Download new version...")
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
log.Println("["+strings.ToUpper(fileType)+"]", "Download new version...OK")
|
||||||
|
return fmt.Errorf("bad status: %s", resp.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change binary filename to .filename
|
||||||
|
binary, err := osext.Executable()
|
||||||
|
var filename = getFilenameFromPath(binary)
|
||||||
|
var path = getPlatformPath(binary)
|
||||||
|
var oldBinary = path + "_old_" + filename
|
||||||
|
var newBinary = binary
|
||||||
|
|
||||||
|
// ZIP
|
||||||
|
var tmpFolder = path + "tmp"
|
||||||
|
var tmpFile = tmpFolder + string(os.PathSeparator) + filenameBIN
|
||||||
|
|
||||||
|
//fmt.Println(binary, path+"."+filename)
|
||||||
|
os.Rename(newBinary, oldBinary)
|
||||||
|
|
||||||
|
// Save the new binary with the old file name
|
||||||
|
out, err := os.Create(binary)
|
||||||
|
if err != nil {
|
||||||
|
restorOldBinary(oldBinary, newBinary)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer out.Close()
|
||||||
|
|
||||||
|
// Write the body to file
|
||||||
|
|
||||||
|
_, err = io.Copy(out, resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
restorOldBinary(oldBinary, newBinary)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update as a ZIP file
|
||||||
|
if fileType == "zip" {
|
||||||
|
|
||||||
|
log.Println("["+strings.ToUpper(fileType)+"]", "Update file:", filenameBIN)
|
||||||
|
log.Println("["+strings.ToUpper(fileType)+"]", "Unzip ZIP file...")
|
||||||
|
err = extractZIP(binary, tmpFolder)
|
||||||
|
|
||||||
|
binary = newBinary
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
|
||||||
|
log.Println("["+strings.ToUpper(fileType)+"]", "Unzip ZIP file...ERROR")
|
||||||
|
|
||||||
|
restorOldBinary(oldBinary, newBinary)
|
||||||
|
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
|
||||||
|
log.Println("["+strings.ToUpper(fileType)+"]", "Unzip ZIP file...OK")
|
||||||
|
log.Println("["+strings.ToUpper(fileType)+"]", "Copy binary file...")
|
||||||
|
|
||||||
|
err = copyFile(tmpFile, binary)
|
||||||
|
if err == nil {
|
||||||
|
log.Println("["+strings.ToUpper(fileType)+"]", "Copy binary file...OK")
|
||||||
|
} else {
|
||||||
|
|
||||||
|
log.Println("["+strings.ToUpper(fileType)+"]", "Copy binary file...ERROR")
|
||||||
|
restorOldBinary(oldBinary, newBinary)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
os.RemoveAll(tmpFolder)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the permission
|
||||||
|
err = os.Chmod(binary, 0755)
|
||||||
|
|
||||||
|
// Close the new file !Windows
|
||||||
|
out.Close()
|
||||||
|
|
||||||
|
log.Println("["+strings.ToUpper(fileType)+"]", "Update Successful")
|
||||||
|
|
||||||
|
// Restart binary (Windows)
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
|
||||||
|
bin, err := os.Executable()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
restorOldBinary(oldBinary, newBinary)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var pid = os.Getpid()
|
||||||
|
var process, _ = os.FindProcess(pid)
|
||||||
|
|
||||||
|
if proc, err := start(bin); err == nil {
|
||||||
|
|
||||||
|
os.RemoveAll(oldBinary)
|
||||||
|
process.Kill()
|
||||||
|
proc.Wait()
|
||||||
|
|
||||||
|
} else {
|
||||||
|
restorOldBinary(oldBinary, newBinary)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// Restart binary (Linux and UNIX)
|
||||||
|
file, _ := osext.Executable()
|
||||||
|
os.RemoveAll(oldBinary)
|
||||||
|
err = syscall.Exec(file, os.Args, os.Environ())
|
||||||
|
if err != nil {
|
||||||
|
restorOldBinary(oldBinary, newBinary)
|
||||||
|
log.Fatal(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func start(args ...string) (p *os.Process, err error) {
|
||||||
|
|
||||||
|
if args[0], err = exec.LookPath(args[0]); err == nil {
|
||||||
|
//fmt.Println(args[0])
|
||||||
|
var procAttr os.ProcAttr
|
||||||
|
procAttr.Files = []*os.File{os.Stdin, os.Stdout, os.Stderr}
|
||||||
|
p, err := os.StartProcess(args[0], args, &procAttr)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func restorOldBinary(oldBinary, newBinary string) {
|
||||||
|
os.RemoveAll(newBinary)
|
||||||
|
os.Rename(oldBinary, newBinary)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPlatformFile(filename string) string {
|
||||||
|
|
||||||
|
path, file := filepath.Split(filename)
|
||||||
|
var newPath = filepath.Dir(path)
|
||||||
|
var newFileName = newPath + string(os.PathSeparator) + file
|
||||||
|
|
||||||
|
return newFileName
|
||||||
|
}
|
||||||
|
|
||||||
|
func getFilenameFromPath(path string) string {
|
||||||
|
|
||||||
|
file := filepath.Base(path)
|
||||||
|
|
||||||
|
return file
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPlatformPath(path string) string {
|
||||||
|
|
||||||
|
var newPath = filepath.Dir(path) + string(os.PathSeparator)
|
||||||
|
|
||||||
|
return newPath
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyFile(src, dst string) (err error) {
|
||||||
|
in, err := os.Open(src)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer in.Close()
|
||||||
|
|
||||||
|
out, err := os.Create(dst)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer out.Close()
|
||||||
|
|
||||||
|
_, err = io.Copy(out, in)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return out.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractZIP(archive, target string) (err error) {
|
||||||
|
|
||||||
|
reader, err := zip.OpenReader(archive)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.MkdirAll(target, 0755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, file := range reader.File {
|
||||||
|
|
||||||
|
path := filepath.Join(target, file.Name)
|
||||||
|
if file.FileInfo().IsDir() {
|
||||||
|
os.MkdirAll(path, file.Mode())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fileReader, err := file.Open()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer fileReader.Close()
|
||||||
|
|
||||||
|
targetFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer targetFile.Close()
|
||||||
|
|
||||||
|
if _, err := io.Copy(targetFile, fileReader); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -0,0 +1,238 @@
|
|||||||
|
package src
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"path"
|
||||||
|
"regexp"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
m3u "../src/internal/m3u-parser"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Playlisten parsen
|
||||||
|
func parsePlaylist(filename, fileType string) (channels []interface{}, err error) {
|
||||||
|
|
||||||
|
content, err := readByteFromFile(filename)
|
||||||
|
var id = strings.TrimSuffix(getFilenameFromPath(filename), path.Ext(getFilenameFromPath(filename)))
|
||||||
|
var playlistName = getProviderParameter(id, fileType, "name")
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
|
||||||
|
switch fileType {
|
||||||
|
case "m3u":
|
||||||
|
channels, err = m3u.MakeInterfaceFromM3U(content)
|
||||||
|
case "hdhr":
|
||||||
|
channels, err = makeInteraceFromHDHR(content, playlistName, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Streams filtern
|
||||||
|
func filterThisStream(s interface{}) (status bool) {
|
||||||
|
|
||||||
|
status = false
|
||||||
|
var stream = s.(map[string]string)
|
||||||
|
var regexpYES = `[{]+[^.]+[}]`
|
||||||
|
var regexpNO = `!+[{]+[^.]+[}]`
|
||||||
|
|
||||||
|
for _, filter := range Data.Filter {
|
||||||
|
|
||||||
|
var group, name, search string
|
||||||
|
var exclude, include string
|
||||||
|
var match = false
|
||||||
|
|
||||||
|
var streamValues = strings.Replace(stream["_values"], "\r", "", -1)
|
||||||
|
|
||||||
|
if v, ok := stream["group-title"]; ok {
|
||||||
|
group = v
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := stream["name"]; ok {
|
||||||
|
name = v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unerwünschte Streams !{DEU}
|
||||||
|
r := regexp.MustCompile(regexpNO)
|
||||||
|
val := r.FindStringSubmatch(filter.Rule)
|
||||||
|
|
||||||
|
if len(val) == 1 {
|
||||||
|
|
||||||
|
exclude = val[0][2 : len(val[0])-1]
|
||||||
|
filter.Rule = strings.Replace(filter.Rule, " "+val[0], "", -1)
|
||||||
|
filter.Rule = strings.Replace(filter.Rule, val[0], "", -1)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Muss zusätzlich erfüllt sein {DEU}
|
||||||
|
r = regexp.MustCompile(regexpYES)
|
||||||
|
val = r.FindStringSubmatch(filter.Rule)
|
||||||
|
|
||||||
|
if len(val) == 1 {
|
||||||
|
|
||||||
|
include = val[0][1 : len(val[0])-1]
|
||||||
|
filter.Rule = strings.Replace(filter.Rule, " "+val[0], "", -1)
|
||||||
|
filter.Rule = strings.Replace(filter.Rule, val[0], "", -1)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
switch filter.CaseSensitive {
|
||||||
|
|
||||||
|
case false:
|
||||||
|
|
||||||
|
streamValues = strings.ToLower(streamValues)
|
||||||
|
filter.Rule = strings.ToLower(filter.Rule)
|
||||||
|
exclude = strings.ToLower(exclude)
|
||||||
|
include = strings.ToLower(include)
|
||||||
|
group = strings.ToLower(group)
|
||||||
|
name = strings.ToLower(name)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
switch filter.Type {
|
||||||
|
|
||||||
|
case "group-title":
|
||||||
|
search = name
|
||||||
|
|
||||||
|
if group == filter.Rule {
|
||||||
|
match = true
|
||||||
|
}
|
||||||
|
|
||||||
|
case "custom-filter":
|
||||||
|
search = streamValues
|
||||||
|
if strings.Contains(search, filter.Rule) {
|
||||||
|
match = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if match == true {
|
||||||
|
|
||||||
|
if len(exclude) > 0 {
|
||||||
|
var status = checkConditions(search, exclude, "exclude")
|
||||||
|
if status == false {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(include) > 0 {
|
||||||
|
var status = checkConditions(search, include, "include")
|
||||||
|
if status == false {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bedingungen für den Filter
|
||||||
|
func checkConditions(streamValues, conditions, coType string) (status bool) {
|
||||||
|
|
||||||
|
switch coType {
|
||||||
|
|
||||||
|
case "exclude":
|
||||||
|
status = true
|
||||||
|
|
||||||
|
case "include":
|
||||||
|
status = false
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
conditions = strings.Replace(conditions, ", ", ",", -1)
|
||||||
|
conditions = strings.Replace(conditions, " ,", ",", -1)
|
||||||
|
|
||||||
|
var keys = strings.Split(conditions, ",")
|
||||||
|
|
||||||
|
for _, key := range keys {
|
||||||
|
|
||||||
|
if strings.Contains(streamValues, key) {
|
||||||
|
|
||||||
|
switch coType {
|
||||||
|
|
||||||
|
case "exclude":
|
||||||
|
return false
|
||||||
|
|
||||||
|
case "include":
|
||||||
|
return true
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// xTeVe M3U Datei erstellen
|
||||||
|
func buildM3U(groups []string) (m3u string, err error) {
|
||||||
|
|
||||||
|
var m3uChannels = make(map[float64]XEPGChannelStruct)
|
||||||
|
var channelNumbers []float64
|
||||||
|
|
||||||
|
for _, dxc := range Data.XEPG.Channels {
|
||||||
|
|
||||||
|
var xepgChannel XEPGChannelStruct
|
||||||
|
err := json.Unmarshal([]byte(mapToJSON(dxc)), &xepgChannel)
|
||||||
|
if err == nil {
|
||||||
|
|
||||||
|
if xepgChannel.XActive == true {
|
||||||
|
|
||||||
|
if len(groups) > 0 {
|
||||||
|
|
||||||
|
if indexOfString(xepgChannel.XGroupTitle, groups) == -1 {
|
||||||
|
goto Done
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
var channelNumber, err = strconv.ParseFloat(strings.TrimSpace(xepgChannel.XChannelID), 64)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
m3uChannels[channelNumber] = xepgChannel
|
||||||
|
channelNumbers = append(channelNumbers, channelNumber)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Done:
|
||||||
|
}
|
||||||
|
|
||||||
|
// M3U Inhalt erstellen
|
||||||
|
sort.Float64s(channelNumbers)
|
||||||
|
|
||||||
|
var xmltvURL = fmt.Sprintf("%s://%s/xmltv/xteve.xml", System.ServerProtocol.XML, System.Domain)
|
||||||
|
m3u = fmt.Sprintf(`#EXTM3U url-tvg="%s" x-tvg-url="%s"`+"\n", xmltvURL, xmltvURL)
|
||||||
|
|
||||||
|
for _, channelNumber := range channelNumbers {
|
||||||
|
|
||||||
|
var channel = m3uChannels[channelNumber]
|
||||||
|
var parameter = fmt.Sprintf(`#EXTINF:0 channelID="%s" tvg-chno="%s" tvg-name="%s" tvg-id="%s" tvg-logo="%s" group-title="%s",%s`+"\n", channel.XEPG, channel.XChannelID, channel.XName, channel.XChannelID, getCacheImageURL(channel.TvgLogo), channel.XGroupTitle, channel.XName)
|
||||||
|
var stream, err = createStreamingURL("M3U", channel.FileM3UID, channel.XChannelID, channel.XName, channel.URL)
|
||||||
|
if err == nil {
|
||||||
|
m3u = m3u + parameter + stream + "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(groups) == 0 {
|
||||||
|
|
||||||
|
var filename = System.Folder.Data + "xteve.m3u"
|
||||||
|
err = writeByteToFile(filename, []byte(m3u))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
package src
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// InitMaintenance : Wartungsprozess initialisieren
|
||||||
|
func InitMaintenance() (err error) {
|
||||||
|
|
||||||
|
rand.Seed(time.Now().Unix())
|
||||||
|
System.TimeForAutoUpdate = fmt.Sprintf("0%d%d", randomTime(0, 2), randomTime(10, 59))
|
||||||
|
|
||||||
|
go maintenance()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func maintenance() {
|
||||||
|
|
||||||
|
for {
|
||||||
|
|
||||||
|
var t = time.Now()
|
||||||
|
|
||||||
|
// Aktualisierung der Playlist und XMLTV Dateien
|
||||||
|
if System.ScanInProgress == 0 {
|
||||||
|
|
||||||
|
for _, schedule := range Settings.Update {
|
||||||
|
|
||||||
|
if schedule == t.Format("1504") {
|
||||||
|
|
||||||
|
showInfo("Update:" + schedule)
|
||||||
|
|
||||||
|
// Backup erstellen
|
||||||
|
err := xTeVeAutoBackup()
|
||||||
|
if err != nil {
|
||||||
|
ShowError(err, 000)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Playlist und XMLTV Dateien aktualisieren
|
||||||
|
getProviderData("m3u", "")
|
||||||
|
getProviderData("hdhr", "")
|
||||||
|
|
||||||
|
if Settings.EpgSource == "XEPG" {
|
||||||
|
getProviderData("xmltv", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Datenbank für DVR erstellen
|
||||||
|
err = buildDatabaseDVR()
|
||||||
|
if err != nil {
|
||||||
|
ShowError(err, 000)
|
||||||
|
}
|
||||||
|
|
||||||
|
if Settings.CacheImages == false && System.ImageCachingInProgress == 0 {
|
||||||
|
removeChildItems(System.Folder.ImagesCache)
|
||||||
|
}
|
||||||
|
|
||||||
|
// XEPG Dateien erstellen
|
||||||
|
Data.Cache.XMLTV = make(map[string]XMLTV)
|
||||||
|
buildXEPG(false)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update xTeVe (Binary)
|
||||||
|
if System.TimeForAutoUpdate == t.Format("1504") {
|
||||||
|
BinaryUpdate()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(60 * time.Second)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func randomTime(min, max int) int {
|
||||||
|
rand.Seed(time.Now().Unix())
|
||||||
|
return rand.Intn(max-min) + min
|
||||||
|
}
|
||||||
@@ -0,0 +1,323 @@
|
|||||||
|
package src
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
m3u "../src/internal/m3u-parser"
|
||||||
|
)
|
||||||
|
|
||||||
|
// fileType: Welcher Dateityp soll aktualisiert werden (m3u, hdhr, xml) | fileID: Update einer bestimmten Datei (Provider ID)
|
||||||
|
func getProviderData(fileType, fileID string) (err error) {
|
||||||
|
|
||||||
|
var fileExtension, serverFileName string
|
||||||
|
var body = make([]byte, 0)
|
||||||
|
var newProvider = false
|
||||||
|
var dataMap = make(map[string]interface{})
|
||||||
|
|
||||||
|
var saveDateFromProvider = func(fileSource, serverFileName, id string, body []byte) (err error) {
|
||||||
|
|
||||||
|
var data = make(map[string]interface{})
|
||||||
|
|
||||||
|
if value, ok := dataMap[id].(map[string]interface{}); ok {
|
||||||
|
data = value
|
||||||
|
} else {
|
||||||
|
data["id.provider"] = id
|
||||||
|
dataMap[id] = data
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default keys für die Providerdaten
|
||||||
|
var keys = []string{"name", "description", "type", "file." + System.AppName, "file.source", "tuner", "last.update", "compatibility", "counter.error", "counter.download", "provider.availability"}
|
||||||
|
|
||||||
|
for _, key := range keys {
|
||||||
|
|
||||||
|
if _, ok := data[key]; !ok {
|
||||||
|
|
||||||
|
switch key {
|
||||||
|
|
||||||
|
case "name":
|
||||||
|
data[key] = serverFileName
|
||||||
|
|
||||||
|
case "description":
|
||||||
|
data[key] = ""
|
||||||
|
|
||||||
|
case "type":
|
||||||
|
data[key] = fileType
|
||||||
|
|
||||||
|
case "file." + System.AppName:
|
||||||
|
data[key] = id + fileExtension
|
||||||
|
|
||||||
|
case "file.source":
|
||||||
|
data[key] = fileSource
|
||||||
|
|
||||||
|
case "last.update":
|
||||||
|
data[key] = time.Now().Format("2006-01-02 15:04:05")
|
||||||
|
|
||||||
|
case "tuner":
|
||||||
|
if fileType == "m3u" || fileType == "hdhr" {
|
||||||
|
if _, ok := data[key].(float64); !ok {
|
||||||
|
data[key] = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case "compatibility":
|
||||||
|
data[key] = make(map[string]interface{})
|
||||||
|
|
||||||
|
case "counter.download":
|
||||||
|
data[key] = 0.0
|
||||||
|
|
||||||
|
case "counter.error":
|
||||||
|
data[key] = 0.0
|
||||||
|
|
||||||
|
case "provider.availability":
|
||||||
|
data[key] = 100
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := data["id.provider"]; !ok {
|
||||||
|
data["id.provider"] = id
|
||||||
|
}
|
||||||
|
|
||||||
|
// Datei extrahieren
|
||||||
|
body, err = extractGZIP(body, fileSource)
|
||||||
|
if err != nil {
|
||||||
|
ShowError(err, 000)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Daten überprüfen
|
||||||
|
showInfo("Check File:" + fileSource)
|
||||||
|
switch fileType {
|
||||||
|
|
||||||
|
case "m3u":
|
||||||
|
_, err = m3u.MakeInterfaceFromM3U(body)
|
||||||
|
|
||||||
|
case "hdhr":
|
||||||
|
_, err = jsonToInterface(string(body))
|
||||||
|
|
||||||
|
case "xmltv":
|
||||||
|
err = checkXMLCompatibility(id, body)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var filePath = System.Folder.Data + data["file."+System.AppName].(string)
|
||||||
|
|
||||||
|
err = writeByteToFile(filePath, body)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
data["last.update"] = time.Now().Format("2006-01-02 15:04:05")
|
||||||
|
data["counter.download"] = data["counter.download"].(float64) + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
switch fileType {
|
||||||
|
|
||||||
|
case "m3u":
|
||||||
|
dataMap = Settings.Files.M3U
|
||||||
|
fileExtension = ".m3u"
|
||||||
|
|
||||||
|
case "hdhr":
|
||||||
|
dataMap = Settings.Files.HDHR
|
||||||
|
fileExtension = ".json"
|
||||||
|
|
||||||
|
case "xmltv":
|
||||||
|
dataMap = Settings.Files.XMLTV
|
||||||
|
fileExtension = ".xml"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
for dataID, d := range dataMap {
|
||||||
|
|
||||||
|
var data = d.(map[string]interface{})
|
||||||
|
var fileSource = data["file.source"].(string)
|
||||||
|
newProvider = false
|
||||||
|
|
||||||
|
if _, ok := data["new"]; ok {
|
||||||
|
newProvider = true
|
||||||
|
delete(data, "new")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wenn eine ID vorhanden ist und nicht mit der aus der Datanbank übereinstimmt, wird die Aktualisierung übersprungen (goto)
|
||||||
|
if len(fileID) > 0 && newProvider == false {
|
||||||
|
if dataID != fileID {
|
||||||
|
goto Done
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch fileType {
|
||||||
|
|
||||||
|
case "hdhr":
|
||||||
|
|
||||||
|
// Laden vom HDHomeRun Tuner
|
||||||
|
showInfo("Tuner:" + fileSource)
|
||||||
|
var tunerURL = "http://" + fileSource + "/lineup.json"
|
||||||
|
serverFileName, body, err = downloadFileFromServer(tunerURL)
|
||||||
|
|
||||||
|
default:
|
||||||
|
|
||||||
|
if strings.Contains(fileSource, "http://") || strings.Contains(fileSource, "https://") {
|
||||||
|
|
||||||
|
// Laden vom Remote Server
|
||||||
|
showInfo("Download:" + fileSource)
|
||||||
|
serverFileName, body, err = downloadFileFromServer(fileSource)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// Laden einer lokalen Datei
|
||||||
|
showInfo("Open:" + fileSource)
|
||||||
|
|
||||||
|
err = checkFile(fileSource)
|
||||||
|
if err == nil {
|
||||||
|
body, err = readByteFromFile(fileSource)
|
||||||
|
serverFileName = getFilenameFromPath(fileSource)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
|
||||||
|
err = saveDateFromProvider(fileSource, serverFileName, dataID, body)
|
||||||
|
if err == nil {
|
||||||
|
showInfo("Save File:" + fileSource + " [ID: " + dataID + "]")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
|
||||||
|
ShowError(err, 000)
|
||||||
|
var downloadErr = err
|
||||||
|
|
||||||
|
if newProvider == false {
|
||||||
|
|
||||||
|
// Prüfen ob ältere Datei vorhanden ist
|
||||||
|
var file = System.Folder.Data + dataID + fileExtension
|
||||||
|
|
||||||
|
err = checkFile(file)
|
||||||
|
if err == nil {
|
||||||
|
|
||||||
|
if len(fileID) == 0 {
|
||||||
|
showWarning(1011)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = downloadErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fehler Counter um 1 erhöhen
|
||||||
|
var data = make(map[string]interface{})
|
||||||
|
if value, ok := dataMap[dataID].(map[string]interface{}); ok {
|
||||||
|
|
||||||
|
data = value
|
||||||
|
data["counter.error"] = data["counter.error"].(float64) + 1
|
||||||
|
data["counter.download"] = data["counter.download"].(float64) + 1
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return downloadErr
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Berechnen der Fehlerquote
|
||||||
|
if newProvider == false {
|
||||||
|
|
||||||
|
if value, ok := dataMap[dataID].(map[string]interface{}); ok {
|
||||||
|
|
||||||
|
var data = make(map[string]interface{})
|
||||||
|
data = value
|
||||||
|
|
||||||
|
if data["counter.error"].(float64) == 0 {
|
||||||
|
data["provider.availability"] = 100
|
||||||
|
} else {
|
||||||
|
data["provider.availability"] = int(data["counter.error"].(float64)*100/data["counter.download"].(float64)*-1 + 100)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
switch fileType {
|
||||||
|
|
||||||
|
case "m3u":
|
||||||
|
Settings.Files.M3U = dataMap
|
||||||
|
|
||||||
|
case "hdhr":
|
||||||
|
Settings.Files.HDHR = dataMap
|
||||||
|
|
||||||
|
case "xmltv":
|
||||||
|
Settings.Files.XMLTV = dataMap
|
||||||
|
delete(Data.Cache.XMLTV, System.Folder.Data+dataID+fileExtension)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
saveSettings(Settings)
|
||||||
|
|
||||||
|
Done:
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func downloadFileFromServer(providerURL string) (filename string, body []byte, err error) {
|
||||||
|
|
||||||
|
_, err = url.ParseRequestURI(providerURL)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := http.Get(providerURL)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.Header.Set("User-Agent", Settings.UserAgent)
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
err = fmt.Errorf(fmt.Sprintf("%d: %s "+http.StatusText(resp.StatusCode), resp.StatusCode, providerURL))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dateiname aus dem Header holen
|
||||||
|
var index = strings.Index(resp.Header.Get("Content-Disposition"), "filename")
|
||||||
|
|
||||||
|
if index > -1 {
|
||||||
|
|
||||||
|
var headerFilename = resp.Header.Get("Content-Disposition")[index:len(resp.Header.Get("Content-Disposition"))]
|
||||||
|
var value = strings.Split(headerFilename, `=`)
|
||||||
|
var f = strings.Replace(value[1], `"`, "", -1)
|
||||||
|
|
||||||
|
f = strings.Replace(f, `;`, "", -1)
|
||||||
|
filename = f
|
||||||
|
showInfo("Header filename:" + filename)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
var cleanFilename = strings.SplitN(getFilenameFromPath(providerURL), "?", 2)
|
||||||
|
filename = cleanFilename[0]
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err = ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -0,0 +1,404 @@
|
|||||||
|
package src
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func showInfo(str string) {
|
||||||
|
|
||||||
|
var max = 22
|
||||||
|
var msg = strings.SplitN(str, ":", 2)
|
||||||
|
var length = len(msg[0])
|
||||||
|
var space string
|
||||||
|
|
||||||
|
if len(msg) == 2 {
|
||||||
|
|
||||||
|
for i := length; i < max; i++ {
|
||||||
|
space = space + " "
|
||||||
|
}
|
||||||
|
|
||||||
|
msg[0] = msg[0] + ":" + space
|
||||||
|
|
||||||
|
var logMsg = fmt.Sprintf("[%s] %s%s", System.Name, msg[0], msg[1])
|
||||||
|
|
||||||
|
printLogOnScreen(logMsg, "info")
|
||||||
|
|
||||||
|
logMsg = strings.Replace(logMsg, " ", " ", -1)
|
||||||
|
WebScreenLog.Log = append(WebScreenLog.Log, time.Now().Format("2006-01-02 15:04:05")+" "+logMsg)
|
||||||
|
logCleanUp()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func showDebug(str string, level int) {
|
||||||
|
|
||||||
|
if System.Flag.Debug < level {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var max = 22
|
||||||
|
var msg = strings.SplitN(str, ":", 2)
|
||||||
|
var length = len(msg[0])
|
||||||
|
var space string
|
||||||
|
var mutex = sync.RWMutex{}
|
||||||
|
|
||||||
|
if len(msg) == 2 {
|
||||||
|
|
||||||
|
for i := length; i < max; i++ {
|
||||||
|
space = space + " "
|
||||||
|
}
|
||||||
|
msg[0] = msg[0] + ":" + space
|
||||||
|
|
||||||
|
var logMsg = fmt.Sprintf("[DEBUG] %s%s", msg[0], msg[1])
|
||||||
|
|
||||||
|
printLogOnScreen(logMsg, "debug")
|
||||||
|
|
||||||
|
mutex.Lock()
|
||||||
|
logMsg = strings.Replace(logMsg, " ", " ", -1)
|
||||||
|
WebScreenLog.Log = append(WebScreenLog.Log, time.Now().Format("2006-01-02 15:04:05")+" "+logMsg)
|
||||||
|
logCleanUp()
|
||||||
|
mutex.Unlock()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func showHighlight(str string) {
|
||||||
|
|
||||||
|
var max = 22
|
||||||
|
var msg = strings.SplitN(str, ":", 2)
|
||||||
|
var length = len(msg[0])
|
||||||
|
var space string
|
||||||
|
|
||||||
|
var notification Notification
|
||||||
|
notification.Type = "info"
|
||||||
|
|
||||||
|
if len(msg) == 2 {
|
||||||
|
|
||||||
|
for i := length; i < max; i++ {
|
||||||
|
space = space + " "
|
||||||
|
}
|
||||||
|
|
||||||
|
msg[0] = msg[0] + ":" + space
|
||||||
|
|
||||||
|
var logMsg = fmt.Sprintf("[%s] %s%s", System.Name, msg[0], msg[1])
|
||||||
|
|
||||||
|
printLogOnScreen(logMsg, "highlight")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
notification.Type = "info"
|
||||||
|
notification.Message = msg[1]
|
||||||
|
|
||||||
|
addNotification(notification)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func showWarning(errCode int) {
|
||||||
|
|
||||||
|
var errMsg = getErrMsg(errCode)
|
||||||
|
var logMsg = fmt.Sprintf("[%s] [WARNING] %s", System.Name, errMsg)
|
||||||
|
var mutex = sync.RWMutex{}
|
||||||
|
|
||||||
|
printLogOnScreen(logMsg, "warning")
|
||||||
|
|
||||||
|
mutex.Lock()
|
||||||
|
WebScreenLog.Log = append(WebScreenLog.Log, time.Now().Format("2006-01-02 15:04:05")+" "+logMsg)
|
||||||
|
WebScreenLog.Warnings++
|
||||||
|
mutex.Unlock()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ShowError : Zeigt die Fehlermeldungen in der Konsole
|
||||||
|
func ShowError(err error, errCode int) {
|
||||||
|
|
||||||
|
var mutex = sync.RWMutex{}
|
||||||
|
|
||||||
|
var errMsg = getErrMsg(errCode)
|
||||||
|
var logMsg = fmt.Sprintf("[%s] [ERROR] %s (%s) - EC: %d", System.Name, err, errMsg, errCode)
|
||||||
|
|
||||||
|
printLogOnScreen(logMsg, "error")
|
||||||
|
|
||||||
|
mutex.Lock()
|
||||||
|
WebScreenLog.Log = append(WebScreenLog.Log, time.Now().Format("2006-01-02 15:04:05")+" "+logMsg)
|
||||||
|
WebScreenLog.Errors++
|
||||||
|
mutex.Unlock()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func printLogOnScreen(logMsg string, logType string) {
|
||||||
|
|
||||||
|
var color string
|
||||||
|
|
||||||
|
switch logType {
|
||||||
|
|
||||||
|
case "info":
|
||||||
|
color = "\033[0m"
|
||||||
|
|
||||||
|
case "debug":
|
||||||
|
color = "\033[35m"
|
||||||
|
|
||||||
|
case "highlight":
|
||||||
|
color = "\033[32m"
|
||||||
|
|
||||||
|
case "warning":
|
||||||
|
color = "\033[33m"
|
||||||
|
|
||||||
|
case "error":
|
||||||
|
color = "\033[31m"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
switch runtime.GOOS {
|
||||||
|
|
||||||
|
case "windows":
|
||||||
|
log.Println(logMsg)
|
||||||
|
|
||||||
|
default:
|
||||||
|
fmt.Print(color)
|
||||||
|
log.Println(logMsg)
|
||||||
|
fmt.Print("\033[0m")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func logCleanUp() {
|
||||||
|
|
||||||
|
var logEntriesRAM = Settings.LogEntriesRAM
|
||||||
|
var logs = WebScreenLog.Log
|
||||||
|
|
||||||
|
WebScreenLog.Warnings = 0
|
||||||
|
WebScreenLog.Errors = 0
|
||||||
|
|
||||||
|
if len(logs) > logEntriesRAM {
|
||||||
|
|
||||||
|
var tmp = make([]string, 0)
|
||||||
|
for i := len(logs) - logEntriesRAM; i < logEntriesRAM; i++ {
|
||||||
|
tmp = append(tmp, logs[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
logs = tmp
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, log := range logs {
|
||||||
|
|
||||||
|
if strings.Contains(log, "WARNING") {
|
||||||
|
WebScreenLog.Warnings++
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.Contains(log, "ERROR") {
|
||||||
|
WebScreenLog.Errors++
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
WebScreenLog.Log = logs
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fehlercodes
|
||||||
|
func getErrMsg(errCode int) (errMsg string) {
|
||||||
|
|
||||||
|
switch errCode {
|
||||||
|
|
||||||
|
case 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
// Errors
|
||||||
|
case 1001:
|
||||||
|
errMsg = fmt.Sprintf("Web server could not be started.")
|
||||||
|
case 1002:
|
||||||
|
errMsg = fmt.Sprintf("No local IP address found.")
|
||||||
|
case 1003:
|
||||||
|
errMsg = fmt.Sprintf("Invalid xml")
|
||||||
|
case 1004:
|
||||||
|
errMsg = fmt.Sprintf("File not found")
|
||||||
|
case 1005:
|
||||||
|
errMsg = fmt.Sprintf("Invalide m3u")
|
||||||
|
case 1006:
|
||||||
|
errMsg = fmt.Sprintf("No playlist!")
|
||||||
|
case 1007:
|
||||||
|
errMsg = fmt.Sprintf("XEPG requires an XMLTV file.")
|
||||||
|
case 1010:
|
||||||
|
errMsg = fmt.Sprintf("Invalid file compression")
|
||||||
|
case 1011:
|
||||||
|
errMsg = fmt.Sprintf("Data is corrupt or unavailable, %s now uses an older version of this file", System.Name)
|
||||||
|
case 1012:
|
||||||
|
errMsg = fmt.Sprintf("Invalid formatting of the time")
|
||||||
|
case 1013:
|
||||||
|
errMsg = fmt.Sprintf("Invalid settings file (%s), file must be at least version %s", System.File.Settings, System.Compatibility)
|
||||||
|
|
||||||
|
case 1020:
|
||||||
|
errMsg = fmt.Sprintf("Data could not be saved, invalid keyword")
|
||||||
|
|
||||||
|
// Datenbank Update
|
||||||
|
case 1030:
|
||||||
|
errMsg = fmt.Sprintf("Invalid settings file (%s)", System.File.Settings)
|
||||||
|
|
||||||
|
// M3U Parser
|
||||||
|
case 1050:
|
||||||
|
errMsg = fmt.Sprintf("Invalid duration specification in the M3U8 playlist.")
|
||||||
|
|
||||||
|
// M3U Parser
|
||||||
|
case 1060:
|
||||||
|
errMsg = fmt.Sprintf("Invalid characters found in the tvg parameters, streams with invalid parameters were skipped.")
|
||||||
|
|
||||||
|
// Dateisystem
|
||||||
|
case 1070:
|
||||||
|
errMsg = fmt.Sprintf("Folder could not be created.")
|
||||||
|
case 1071:
|
||||||
|
errMsg = fmt.Sprintf("File could not be created")
|
||||||
|
|
||||||
|
// Backup
|
||||||
|
case 1090:
|
||||||
|
errMsg = fmt.Sprintf("Automatic backup failed")
|
||||||
|
|
||||||
|
// Websockets
|
||||||
|
case 1100:
|
||||||
|
errMsg = fmt.Sprintf("WebUI build error")
|
||||||
|
case 1101:
|
||||||
|
errMsg = fmt.Sprintf("WebUI request error")
|
||||||
|
case 1102:
|
||||||
|
errMsg = fmt.Sprintf("WebUI response error")
|
||||||
|
|
||||||
|
// PMS Guide Numbers
|
||||||
|
case 1200:
|
||||||
|
errMsg = fmt.Sprintf("Could not create file")
|
||||||
|
|
||||||
|
// Stream URL Fehler
|
||||||
|
case 1201:
|
||||||
|
errMsg = fmt.Sprintf("Plex stream error")
|
||||||
|
case 1202:
|
||||||
|
errMsg = fmt.Sprintf("Steaming URL could not be found in any playlist")
|
||||||
|
case 1203:
|
||||||
|
errMsg = fmt.Sprintf("Steaming URL could not be found in any playlist")
|
||||||
|
|
||||||
|
// Warnings
|
||||||
|
case 2000:
|
||||||
|
errMsg = fmt.Sprintf("Plex can not handle more than %d streams. If you do not use Plex, you can ignore this warning.", System.DVRLimit)
|
||||||
|
case 2001:
|
||||||
|
errMsg = fmt.Sprintf("%s has loaded more than %d streams. Use the filter to reduce the number of streams.", System.Name, System.DVRLimit)
|
||||||
|
case 2002:
|
||||||
|
errMsg = fmt.Sprintf("PMS can not play m3u8 streams")
|
||||||
|
case 2003:
|
||||||
|
errMsg = fmt.Sprintf("PMS can not play streams over RTSP.")
|
||||||
|
case 2004:
|
||||||
|
errMsg = fmt.Sprintf("Buffer is disabled for this stream.")
|
||||||
|
case 2005:
|
||||||
|
errMsg = fmt.Sprintf("There are no channels mapped, use the mapping menu to assign EPG data to the channels.")
|
||||||
|
case 2010:
|
||||||
|
errMsg = fmt.Sprintf("No valid streaming URL")
|
||||||
|
|
||||||
|
case 2099:
|
||||||
|
errMsg = fmt.Sprintf("Updates have been disabled by the developer")
|
||||||
|
|
||||||
|
// Tuner
|
||||||
|
case 2105:
|
||||||
|
errMsg = fmt.Sprintf("The number of tuners has changed, you have to delete " + System.Name + " in Plex / Emby HDHR and set it up again.")
|
||||||
|
case 2106:
|
||||||
|
errMsg = fmt.Sprintf("This function is only available with XEPG as EPG source")
|
||||||
|
|
||||||
|
case 2110:
|
||||||
|
errMsg = fmt.Sprintf("Don't run this as Root!")
|
||||||
|
|
||||||
|
case 2300:
|
||||||
|
errMsg = fmt.Sprintf("No channel logo found in the XMLTV or M3U file.")
|
||||||
|
case 2301:
|
||||||
|
errMsg = fmt.Sprintf("XMLTV file no longer available, channel has been deactivated.")
|
||||||
|
case 2302:
|
||||||
|
errMsg = fmt.Sprintf("Channel ID in the XMLTV file has changed. Channel has been deactivated.")
|
||||||
|
|
||||||
|
// Benutzerauthentifizierung
|
||||||
|
case 3000:
|
||||||
|
errMsg = fmt.Sprintf("Database for user authentication could not be initialized.")
|
||||||
|
case 3001:
|
||||||
|
errMsg = fmt.Sprintf("The user has no authorization to load the channels.")
|
||||||
|
|
||||||
|
// Buffer
|
||||||
|
case 4000:
|
||||||
|
errMsg = fmt.Sprintf("Connection to streaming source was interrupted.")
|
||||||
|
case 4001:
|
||||||
|
errMsg = fmt.Sprintf("Too many errors connecting to the provider. Streaming is canceled.")
|
||||||
|
case 4002:
|
||||||
|
errMsg = fmt.Sprintf("New URL for the redirect to the streaming server is missing")
|
||||||
|
case 4003:
|
||||||
|
errMsg = fmt.Sprintf("Server sends an incompatible content-type")
|
||||||
|
case 4004:
|
||||||
|
errMsg = fmt.Sprintf("This error message comes from the provider")
|
||||||
|
case 4005:
|
||||||
|
errMsg = fmt.Sprintf("Temporary buffer files could not be deleted")
|
||||||
|
|
||||||
|
// Buffer (M3U8)
|
||||||
|
case 4050:
|
||||||
|
errMsg = fmt.Sprintf("Invalid M3U8 file")
|
||||||
|
case 4051:
|
||||||
|
errMsg = fmt.Sprintf("#EXTM3U header is missing")
|
||||||
|
|
||||||
|
// Caching
|
||||||
|
case 4100:
|
||||||
|
errMsg = fmt.Sprintf("Unknown content type for downloaded image")
|
||||||
|
|
||||||
|
// API
|
||||||
|
case 5000:
|
||||||
|
errMsg = fmt.Sprintf("Invalid API command")
|
||||||
|
|
||||||
|
// Update Server
|
||||||
|
case 6001:
|
||||||
|
errMsg = fmt.Sprintf("Ivalid key")
|
||||||
|
case 6002:
|
||||||
|
errMsg = fmt.Sprintf("Update failed")
|
||||||
|
case 6003:
|
||||||
|
errMsg = fmt.Sprintf("Server not available")
|
||||||
|
case 6004:
|
||||||
|
errMsg = fmt.Sprintf("xTeVe update available")
|
||||||
|
|
||||||
|
default:
|
||||||
|
errMsg = fmt.Sprintf("Unknown error / warning (%d)", errCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
return errMsg
|
||||||
|
}
|
||||||
|
|
||||||
|
func addNotification(notification Notification) (err error) {
|
||||||
|
|
||||||
|
var i int
|
||||||
|
var t = time.Now().UnixNano() / (int64(time.Millisecond) / int64(time.Nanosecond))
|
||||||
|
notification.Time = strconv.FormatInt(t, 10)
|
||||||
|
notification.New = true
|
||||||
|
|
||||||
|
if len(notification.Headline) == 0 {
|
||||||
|
notification.Headline = strings.ToUpper(notification.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(System.Notification) == 0 {
|
||||||
|
System.Notification = make(map[string]Notification)
|
||||||
|
}
|
||||||
|
|
||||||
|
System.Notification[notification.Time] = notification
|
||||||
|
|
||||||
|
for key := range System.Notification {
|
||||||
|
|
||||||
|
if i < len(System.Notification)-10 {
|
||||||
|
delete(System.Notification, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
i++
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
package src
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/koron/go-ssdp"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SSDP : SSPD / DLNA Server
|
||||||
|
func SSDP() {
|
||||||
|
|
||||||
|
showInfo(fmt.Sprintf("SSDP / DLNA:%t", Settings.SSDP))
|
||||||
|
|
||||||
|
if Settings.SSDP == false {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(10 * time.Second)
|
||||||
|
ad, err := ssdp.Advertise(
|
||||||
|
"upnp:"+System.AppName, // send as "ST"
|
||||||
|
System.DeviceID+"::upnp:"+System.AppName, // send as "USN"
|
||||||
|
System.URLBase+"/device.xml", // send as "LOCATION"
|
||||||
|
System.AppName, // send as "SERVER"
|
||||||
|
1800) // send as "maxAge" in "CACHE-CONTROL"
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
ShowError(err, 000)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug SSDP
|
||||||
|
if System.Flag.Debug == 3 {
|
||||||
|
ssdp.Logger = log.New(os.Stderr, "[SSDP] ", log.LstdFlags)
|
||||||
|
}
|
||||||
|
|
||||||
|
var aliveTick <-chan time.Time
|
||||||
|
var ai = 10
|
||||||
|
|
||||||
|
if ai > 0 {
|
||||||
|
aliveTick = time.Tick(time.Duration(ai) * time.Second)
|
||||||
|
} else {
|
||||||
|
aliveTick = make(chan time.Time)
|
||||||
|
}
|
||||||
|
|
||||||
|
quit := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(quit, os.Interrupt)
|
||||||
|
|
||||||
|
loop:
|
||||||
|
|
||||||
|
for {
|
||||||
|
|
||||||
|
select {
|
||||||
|
|
||||||
|
case <-aliveTick:
|
||||||
|
ad.Alive()
|
||||||
|
case <-quit:
|
||||||
|
os.Exit(0)
|
||||||
|
break loop
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ad.Bye()
|
||||||
|
ad.Close()
|
||||||
|
}
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
package src
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
// Playlist : Enthält allen Playlistinformationen, die der Buffer benötigr
|
||||||
|
type Playlist struct {
|
||||||
|
Folder string
|
||||||
|
PlaylistID string
|
||||||
|
PlaylistName string
|
||||||
|
Tuner int
|
||||||
|
|
||||||
|
Clients map[int]ThisClient
|
||||||
|
Streams map[int]ThisStream
|
||||||
|
}
|
||||||
|
|
||||||
|
// ThisClient : Clientinfos
|
||||||
|
type ThisClient struct {
|
||||||
|
Connection int
|
||||||
|
}
|
||||||
|
|
||||||
|
// ThisStream : Enthält Informationen zu dem abzuspielenden Stream einer Playlist
|
||||||
|
type ThisStream struct {
|
||||||
|
ChannelName string
|
||||||
|
Error string
|
||||||
|
Folder string
|
||||||
|
MD5 string
|
||||||
|
NetworkBandwidth int
|
||||||
|
PlaylistID string
|
||||||
|
PlaylistName string
|
||||||
|
Status bool
|
||||||
|
URL string
|
||||||
|
|
||||||
|
Segment []Segment
|
||||||
|
|
||||||
|
// Serverinformationen
|
||||||
|
Location string
|
||||||
|
URLFile string
|
||||||
|
URLHost string
|
||||||
|
URLPath string
|
||||||
|
URLRedirect string
|
||||||
|
URLScheme string
|
||||||
|
URLStreamingServer string
|
||||||
|
|
||||||
|
// Wird nur für HLS / M3U8 verwendet
|
||||||
|
Body string
|
||||||
|
Difference float64
|
||||||
|
Duration float64
|
||||||
|
DynamicBandwidth bool
|
||||||
|
FirstSequence int64
|
||||||
|
HLS bool
|
||||||
|
LastSequence int64
|
||||||
|
M3U8URL string
|
||||||
|
NewSegCount int
|
||||||
|
OldSegCount int
|
||||||
|
Sequence int64
|
||||||
|
TimeDiff float64
|
||||||
|
TimeEnd time.Time
|
||||||
|
TimeSegDuration float64
|
||||||
|
TimeStart time.Time
|
||||||
|
Version int
|
||||||
|
Wait float64
|
||||||
|
|
||||||
|
DynamicStream map[int]DynamicStream
|
||||||
|
|
||||||
|
// Lokale Temp Datein
|
||||||
|
OldSegments []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Segment : URL Segmente (HLS / M3U8)
|
||||||
|
type Segment struct {
|
||||||
|
Duration float64
|
||||||
|
Info bool
|
||||||
|
Sequence int64
|
||||||
|
URL string
|
||||||
|
Version int
|
||||||
|
Wait float64
|
||||||
|
|
||||||
|
StreamInf struct {
|
||||||
|
AverageBandwidth int
|
||||||
|
Bandwidth int
|
||||||
|
Framerate float64
|
||||||
|
Resolution string
|
||||||
|
SegmentURL string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DynamicStream : Streaminformationen bei dynamischer Bandbreite
|
||||||
|
type DynamicStream struct {
|
||||||
|
AverageBandwidth int
|
||||||
|
Bandwidth int
|
||||||
|
Framerate float64
|
||||||
|
Resolution string
|
||||||
|
URL string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClientConnection : Client Verbindungen
|
||||||
|
type ClientConnection struct {
|
||||||
|
Connection int
|
||||||
|
Error error
|
||||||
|
}
|
||||||
|
|
||||||
|
// BandwidthCalculation : Bandbreitenberechnung für den Stream
|
||||||
|
type BandwidthCalculation struct {
|
||||||
|
NetworkBandwidth int
|
||||||
|
Size int
|
||||||
|
Start time.Time
|
||||||
|
Stop time.Time
|
||||||
|
TimeDiff float64
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
package src
|
||||||
|
|
||||||
|
import "encoding/xml"
|
||||||
|
|
||||||
|
// Capability : HDHR Capability XML
|
||||||
|
type Capability struct {
|
||||||
|
URLBase string `xml:"URLBase"`
|
||||||
|
XMLName xml.Name `xml:"root"`
|
||||||
|
Xmlns string `xml:"xmlns,attr"`
|
||||||
|
|
||||||
|
SpecVersion struct {
|
||||||
|
Major int `xml:"major"`
|
||||||
|
Minor int `xml:"minor"`
|
||||||
|
} `xml:"specVersion"`
|
||||||
|
|
||||||
|
Device struct {
|
||||||
|
DeviceType string `xml:"deviceType"`
|
||||||
|
FriendlyName string `xml:"friendlyName"`
|
||||||
|
Manufacturer string `xml:"manufacturer"`
|
||||||
|
ModelName string `xml:"modelName"`
|
||||||
|
ModelNumber string `xml:"modelNumber"`
|
||||||
|
SerialNumber string `xml:"serialNumber"`
|
||||||
|
UDN string `xml:"UDN"`
|
||||||
|
} `xml:"device"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Discover : HDHR Discover /discover.json
|
||||||
|
type Discover struct {
|
||||||
|
BaseURL string `json:"BaseURL"`
|
||||||
|
DeviceAuth string `json:"DeviceAuth"`
|
||||||
|
DeviceID string `json:"DeviceID"`
|
||||||
|
FirmwareName string `json:"FirmwareName"`
|
||||||
|
FirmwareVersion string `json:"FirmwareVersion"`
|
||||||
|
FriendlyName string `json:"FriendlyName"`
|
||||||
|
LineupURL string `json:"LineupURL"`
|
||||||
|
Manufacturer string `json:"Manufacturer"`
|
||||||
|
ModelNumber string `json:"ModelNumber"`
|
||||||
|
TunerCount int `json:"TunerCount"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// LineupStatus : HDHR Lineup status /lineup_status.json
|
||||||
|
type LineupStatus struct {
|
||||||
|
ScanInProgress int `json:"ScanInProgress"`
|
||||||
|
ScanPossible int `json:"ScanPossible"`
|
||||||
|
Source string `json:"Source"`
|
||||||
|
SourceList []string `json:"SourceList"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lineup : HDHR Lineup /lineup.json
|
||||||
|
type Lineup []interface {
|
||||||
|
//GuideName string `json:"GuideName"`
|
||||||
|
//GuideNumber string `json:"GuideNumber"`
|
||||||
|
//URL string `json:"URL"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// LineupStream : HDHR einzelner Stream im Lineup
|
||||||
|
type LineupStream struct {
|
||||||
|
GuideName string `json:"GuideName"`
|
||||||
|
GuideNumber string `json:"GuideNumber"`
|
||||||
|
URL string `json:"URL"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,280 @@
|
|||||||
|
package src
|
||||||
|
|
||||||
|
// SystemStruct : Beinhaltet alle Systeminformationen
|
||||||
|
type SystemStruct struct {
|
||||||
|
Addresses struct {
|
||||||
|
DVR string
|
||||||
|
M3U string
|
||||||
|
XML string
|
||||||
|
}
|
||||||
|
|
||||||
|
APIVersion string
|
||||||
|
AppName string
|
||||||
|
ARCH string
|
||||||
|
Branch string
|
||||||
|
Build string
|
||||||
|
Compatibility string
|
||||||
|
ConfigurationWizard bool
|
||||||
|
Dev bool
|
||||||
|
DeviceID string
|
||||||
|
Domain string
|
||||||
|
DVRLimit int
|
||||||
|
|
||||||
|
File struct {
|
||||||
|
Authentication string
|
||||||
|
M3U string
|
||||||
|
PMS string
|
||||||
|
Settings string
|
||||||
|
URLS string
|
||||||
|
XEPG string
|
||||||
|
XML string
|
||||||
|
}
|
||||||
|
|
||||||
|
Flag struct {
|
||||||
|
Branch string
|
||||||
|
Debug int
|
||||||
|
Port string
|
||||||
|
SSDP bool
|
||||||
|
}
|
||||||
|
|
||||||
|
Folder struct {
|
||||||
|
Backup string
|
||||||
|
Cache string
|
||||||
|
Config string
|
||||||
|
Data string
|
||||||
|
ImagesCache string
|
||||||
|
ImagesUpload string
|
||||||
|
Temp string
|
||||||
|
}
|
||||||
|
|
||||||
|
Hostname string
|
||||||
|
ImageCachingInProgress int
|
||||||
|
IPAddress string
|
||||||
|
IPAddressesList []string
|
||||||
|
IPAddressesV4 []string
|
||||||
|
IPAddressesV6 []string
|
||||||
|
Name string
|
||||||
|
OS string
|
||||||
|
ScanInProgress int
|
||||||
|
TimeForAutoUpdate string
|
||||||
|
|
||||||
|
Notification map[string]Notification
|
||||||
|
|
||||||
|
ServerProtocol struct {
|
||||||
|
API string
|
||||||
|
DVR string
|
||||||
|
M3U string
|
||||||
|
WEB string
|
||||||
|
XML string
|
||||||
|
}
|
||||||
|
|
||||||
|
GitHub struct {
|
||||||
|
Branch string
|
||||||
|
Repo string
|
||||||
|
Update bool
|
||||||
|
User string
|
||||||
|
}
|
||||||
|
|
||||||
|
Update struct {
|
||||||
|
Git string
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
URLBase string
|
||||||
|
Version string
|
||||||
|
WEB struct {
|
||||||
|
Menu []string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GitStruct : Updateinformationen von GitHub
|
||||||
|
type GitStruct struct {
|
||||||
|
Filename string `json:"filename"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DataStruct : Alle Daten werden hier abgelegt. (Lineup, XMLTV)
|
||||||
|
type DataStruct struct {
|
||||||
|
Cache struct {
|
||||||
|
ImagesCache []string
|
||||||
|
ImagesFiles []string
|
||||||
|
ImagesURLS []string
|
||||||
|
PMS map[string]string
|
||||||
|
|
||||||
|
StreamingURLS map[string]StreamInfo
|
||||||
|
XMLTV map[string]XMLTV
|
||||||
|
|
||||||
|
Streams struct {
|
||||||
|
Active []string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Filter []Filter
|
||||||
|
|
||||||
|
Playlist struct {
|
||||||
|
M3U struct {
|
||||||
|
Groups struct {
|
||||||
|
Text []string
|
||||||
|
Value []string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StreamPreviewUI struct {
|
||||||
|
Active []string
|
||||||
|
Inactive []string
|
||||||
|
}
|
||||||
|
|
||||||
|
Streams struct {
|
||||||
|
Active []interface{}
|
||||||
|
All []interface{}
|
||||||
|
Inactive []interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
XMLTV struct {
|
||||||
|
Files []string
|
||||||
|
Mapping map[string]interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
XEPG struct {
|
||||||
|
Channels map[string]interface{}
|
||||||
|
XEPGCount int64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter : Wird für die Filterregeln verwendet
|
||||||
|
type Filter struct {
|
||||||
|
CaseSensitive bool
|
||||||
|
Rule string
|
||||||
|
Type string
|
||||||
|
}
|
||||||
|
|
||||||
|
// XEPGChannelStruct : XEPG Struktur
|
||||||
|
type XEPGChannelStruct struct {
|
||||||
|
FileM3UID string `json:"_file.m3u.id,required"`
|
||||||
|
FileM3UName string `json:"_file.m3u.name,required"`
|
||||||
|
FileM3UPath string `json:"_file.m3u.path,required"`
|
||||||
|
GroupTitle string `json:"group-title,required"`
|
||||||
|
Name string `json:"name,required"`
|
||||||
|
TvgID string `json:"tvg-id,required"`
|
||||||
|
TvgLogo string `json:"tvg-logo,required"`
|
||||||
|
TvgName string `json:"tvg-name,required"`
|
||||||
|
URL string `json:"url,required"`
|
||||||
|
UUIDKey string `json:"_uuid.key,required"`
|
||||||
|
UUIDValue string `json:"_uuid.value,omitempty"`
|
||||||
|
Values string `json:"_values,required"`
|
||||||
|
XActive bool `json:"x-active,required"`
|
||||||
|
XCategory string `json:"x-category,required"`
|
||||||
|
XChannelID string `json:"x-channelID,required"`
|
||||||
|
XEPG string `json:"x-epg,required"`
|
||||||
|
XGroupTitle string `json:"x-group-title,required"`
|
||||||
|
XMapping string `json:"x-mapping,required"`
|
||||||
|
XmltvFile string `json:"x-xmltv-file,required"`
|
||||||
|
XName string `json:"x-name,required"`
|
||||||
|
XUpdateChannelIcon bool `json:"x-update-channel-icon,required"`
|
||||||
|
XUpdateChannelName bool `json:"x-update-channel-name,required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// M3UChannelStructXEPG : M3U Struktur für XEPG
|
||||||
|
type M3UChannelStructXEPG struct {
|
||||||
|
FileM3UID string `json:"_file.m3u.id,required"`
|
||||||
|
FileM3UName string `json:"_file.m3u.name,required"`
|
||||||
|
FileM3UPath string `json:"_file.m3u.path,required"`
|
||||||
|
GroupTitle string `json:"group-title,required"`
|
||||||
|
Name string `json:"name,required"`
|
||||||
|
TvgID string `json:"tvg-id,required"`
|
||||||
|
TvgLogo string `json:"tvg-logo,required"`
|
||||||
|
TvgName string `json:"tvg-name,required"`
|
||||||
|
URL string `json:"url,required"`
|
||||||
|
UUIDKey string `json:"_uuid.key,required"`
|
||||||
|
UUIDValue string `json:"_uuid.value,required"`
|
||||||
|
Values string `json:"_values,required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FilterStruct : Filter Struktur
|
||||||
|
type FilterStruct struct {
|
||||||
|
Active bool `json:"active,required"`
|
||||||
|
CaseSensitive bool `json:"caseSensitive,required"`
|
||||||
|
Description string `json:"description,required"`
|
||||||
|
Exclude string `json:"exclude,required"`
|
||||||
|
Filter string `json:"filter,required"`
|
||||||
|
Include string `json:"include,required"`
|
||||||
|
Name string `json:"name,required"`
|
||||||
|
Rule string `json:"rule,omitempty"`
|
||||||
|
Type string `json:"type,required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// StreamingURLS : Informationen zu allen streaming URL's
|
||||||
|
type StreamingURLS struct {
|
||||||
|
Streams map[string]StreamInfo `json:"channels,required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// StreamInfo : Informationen zum Kanal für die streaming URL
|
||||||
|
type StreamInfo struct {
|
||||||
|
ChannelNumber string `json:"channelNumber,required"`
|
||||||
|
Name string `json:"name,required"`
|
||||||
|
PlaylistID string `json:"playlistID,required"`
|
||||||
|
URL string `json:"url,required"`
|
||||||
|
URLid string `json:"urlID,required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notification : Notifikationen im Webinterface
|
||||||
|
type Notification struct {
|
||||||
|
Headline string `json:"headline,required"`
|
||||||
|
Message string `json:"message,required"`
|
||||||
|
New bool `json:"new,required"`
|
||||||
|
Time string `json:"time,required"`
|
||||||
|
Type string `json:"type,required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SettingsStrcut : Inhalt der settings.json
|
||||||
|
type SettingsStrcut struct {
|
||||||
|
API bool `json:"api"`
|
||||||
|
AuthenticationAPI bool `json:"authentication.api"`
|
||||||
|
AuthenticationM3U bool `json:"authentication.m3u"`
|
||||||
|
AuthenticationPMS bool `json:"authentication.pms"`
|
||||||
|
AuthenticationWEB bool `json:"authentication.web"`
|
||||||
|
AuthenticationXML bool `json:"authentication.xml"`
|
||||||
|
BackupKeep int `json:"backup.keep"`
|
||||||
|
BackupPath string `json:"backup.path"`
|
||||||
|
Branch string `json:"git.branch,omitempty"`
|
||||||
|
Buffer bool `json:"buffer"`
|
||||||
|
BufferSize int `json:"buffer.size.kb"`
|
||||||
|
BufferTimeout float64 `json:"buffer.timeout"`
|
||||||
|
CacheImages bool `json:"cache.images"`
|
||||||
|
EpgSource string `json:"epgSource"`
|
||||||
|
FileM3U []string `json:"file,omitempty"` // Beim Wizard wird die M3U in ein Slice gespeichert
|
||||||
|
FileXMLTV []string `json:"xmltv,omitempty"` // Altes Speichersystem der Provider XML Datei Slice (Wird für die Umwandlung auf das neue benötigt)
|
||||||
|
|
||||||
|
Files struct {
|
||||||
|
HDHR map[string]interface{} `json:"hdhr"`
|
||||||
|
M3U map[string]interface{} `json:"m3u"`
|
||||||
|
XMLTV map[string]interface{} `json:"xmltv"`
|
||||||
|
} `json:"files"`
|
||||||
|
|
||||||
|
FilesUpdate bool `json:"files.update"`
|
||||||
|
Filter map[int64]interface{} `json:"filter"`
|
||||||
|
Key string `json:"key,omitempty"`
|
||||||
|
Language string `json:"language"`
|
||||||
|
LogEntriesRAM int `json:"log.entries.ram"`
|
||||||
|
M3U8AdaptiveBandwidthMBPS int `json:"m3u8.adaptive.bandwidth.mbps"`
|
||||||
|
MappingFirstChannel float64 `json:"mapping.first.channel"`
|
||||||
|
Port string `json:"port"`
|
||||||
|
SSDP bool `json:"ssdp"`
|
||||||
|
TempPath string `json:"temp.path"`
|
||||||
|
Tuner int `json:"tuner"`
|
||||||
|
Update []string `json:"update"`
|
||||||
|
UpdateURL string `json:"update.url,omitempty"`
|
||||||
|
UserAgent string `json:"user.agent"`
|
||||||
|
UUID string `json:"uuid"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
XepgReplaceMissingImages bool `json:"xepg.replace.missing.images"`
|
||||||
|
XteveAutoUpdate bool `json:"xteveAutoUpdate"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// LanguageUI : Sprache für das WebUI
|
||||||
|
type LanguageUI struct {
|
||||||
|
Login struct {
|
||||||
|
Failed string
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
package src
|
||||||
|
|
||||||
|
// RequestStruct : Anfragen über die Websocket Schnittstelle
|
||||||
|
type RequestStruct struct {
|
||||||
|
// Befehle an xTeVe
|
||||||
|
Cmd string `json:"cmd,required"`
|
||||||
|
|
||||||
|
// Benutzer
|
||||||
|
DeleteUser bool `json:"deleteUser,omitempty"`
|
||||||
|
UserData map[string]interface{} `json:"userData,omitempty"`
|
||||||
|
|
||||||
|
// Mapping
|
||||||
|
EpgMapping map[string]interface{} `json:"epgMapping,omitempty"`
|
||||||
|
|
||||||
|
// Restore
|
||||||
|
Base64 string `json:"base64,omitempty"`
|
||||||
|
|
||||||
|
// Neue Werte für die Einstellungen (settings.json)
|
||||||
|
Settings struct {
|
||||||
|
API *bool `json:"api,omitempty"`
|
||||||
|
AuthenticationAPI *bool `json:"authentication.api,omitempty"`
|
||||||
|
AuthenticationM3U *bool `json:"authentication.m3u,omitempty"`
|
||||||
|
AuthenticationPMS *bool `json:"authentication.pms,omitempty"`
|
||||||
|
AuthenticationWEP *bool `json:"authentication.web,omitempty"`
|
||||||
|
AuthenticationXML *bool `json:"authentication.xml,omitempty"`
|
||||||
|
BackupKeep *int `json:"backup.keep,omitempty"`
|
||||||
|
BackupPath *string `json:"backup.path,omitempty"`
|
||||||
|
Buffer *bool `json:"buffer,omitempty"`
|
||||||
|
BufferSize *int `json:"buffer.size.kb, omitempty"`
|
||||||
|
BufferTimeout *float64 `json:"buffer.timeout,omitempty"`
|
||||||
|
CacheImages *bool `json:"cache.images,omitempty"`
|
||||||
|
EpgSource *string `json:"epgSource,omitempty"`
|
||||||
|
FilesUpdate *bool `json:"files.update,omitempty"`
|
||||||
|
TempPath *string `json:"temp.path,omitempty"`
|
||||||
|
Tuner *int `json:"tuner,omitempty"`
|
||||||
|
Update *[]string `json:"update,omitempty"`
|
||||||
|
UserAgent *string `json:"user.agent,omitempty"`
|
||||||
|
XepgReplaceMissingImages *bool `json:"xepg.replace.missing.images,omitempty"`
|
||||||
|
XteveAutoUpdate *bool `json:"xteveAutoUpdate,omitempty"`
|
||||||
|
} `json:"settings,omitempty"`
|
||||||
|
|
||||||
|
// Upload Logo
|
||||||
|
Filename string `json:"filename,omitempty"`
|
||||||
|
|
||||||
|
// Filter
|
||||||
|
Filter map[int64]interface{} `json:"filter,omitempty"`
|
||||||
|
|
||||||
|
// Dateien (M3U, HDHR, XMLTV)
|
||||||
|
Files struct {
|
||||||
|
HDHR map[string]interface{} `json:"hdhr,omitempty"`
|
||||||
|
M3U map[string]interface{} `json:"m3u,omitempty"`
|
||||||
|
XMLTV map[string]interface{} `json:"xmltv,omitempty"`
|
||||||
|
} `json:"files,omitempty"`
|
||||||
|
|
||||||
|
// Wizard
|
||||||
|
Wizard struct {
|
||||||
|
EpgSource *string `json:"epgSource,omitempty"`
|
||||||
|
M3U *string `json:"m3u,omitempty"`
|
||||||
|
Tuner *int `json:"tuner,omitempty"`
|
||||||
|
XMLTV *string `json:"xmltv,omitempty"`
|
||||||
|
} `json:"wizard,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResponseStruct : Antworten an den Client (WEB)
|
||||||
|
type ResponseStruct struct {
|
||||||
|
ClientInfo struct {
|
||||||
|
ARCH string `json:"arch"`
|
||||||
|
Branch string `json:"branch,omitempty"`
|
||||||
|
DVR string `json:"DVR"`
|
||||||
|
EpgSource string `json:"epgSource"`
|
||||||
|
Errors int `json:"errors"`
|
||||||
|
M3U string `json:"m3u-url,required"`
|
||||||
|
OS string `json:"os"`
|
||||||
|
Streams string `json:"streams"`
|
||||||
|
UUID string `json:"uuid"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
Warnings int `json:"warnings"`
|
||||||
|
XEPGCount int64 `json:"xepg"`
|
||||||
|
XML string `json:"xepg-url,required"`
|
||||||
|
} `json:"clientInfo,omitempty"`
|
||||||
|
|
||||||
|
Data struct {
|
||||||
|
Playlist struct {
|
||||||
|
M3U struct {
|
||||||
|
Groups struct {
|
||||||
|
Text []string `json:"text,required"`
|
||||||
|
Value []string `json:"value,required"`
|
||||||
|
} `json:"groups,required"`
|
||||||
|
} `json:"m3u,required"`
|
||||||
|
} `json:"playlist,required"`
|
||||||
|
|
||||||
|
StreamPreviewUI struct {
|
||||||
|
Active []string `json:"activeStreams,required"`
|
||||||
|
Inactive []string `json:"inactiveStreams,required"`
|
||||||
|
}
|
||||||
|
} `json:"data,required"`
|
||||||
|
|
||||||
|
Alert string `json:"alert,omitempty"`
|
||||||
|
ConfigurationWizard bool `json:"configurationWizard,required"`
|
||||||
|
Error string `json:"err,omitempty"`
|
||||||
|
Log WebScreenLogStruct `json:"log,required"`
|
||||||
|
LogoURL string `json:"logoURL,omitempty"`
|
||||||
|
OpenLink string `json:"openLink,omitempty"`
|
||||||
|
OpenMenu string `json:"openMenu,omitempty"`
|
||||||
|
Reload bool `json:"reload,omitempty"`
|
||||||
|
Settings SettingsStrcut `json:"settings,required"`
|
||||||
|
Status bool `json:"status,required"`
|
||||||
|
Token string `json:"token,omitempty"`
|
||||||
|
Users map[string]interface{} `json:"users,omitempty"`
|
||||||
|
Wizard int `json:"wizard,omitempty"`
|
||||||
|
XEPG map[string]interface{} `json:"xepg,required"`
|
||||||
|
|
||||||
|
Notification map[string]Notification `json:"notification,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// APIRequestStruct : Anfrage über die API Schnittstelle
|
||||||
|
type APIRequestStruct struct {
|
||||||
|
Cmd string `json:"cmd"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
Token string `json:"token"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// APIResponseStruct : Antwort an den Client (API)
|
||||||
|
type APIResponseStruct struct {
|
||||||
|
EpgSource string `json:"epg.source,omitempty"`
|
||||||
|
Error string `json:"err,omitempty"`
|
||||||
|
Status bool `json:"status,required"`
|
||||||
|
StreamsActive int64 `json:"streams.active,omitempty"`
|
||||||
|
StreamsAll int64 `json:"streams.all,omitempty"`
|
||||||
|
StreamsXepg int64 `json:"streams.xepg,omitempty"`
|
||||||
|
Token string `json:"token,omitempty"`
|
||||||
|
URLDvr string `json:"url.dvr,omitempty"`
|
||||||
|
URLM3U string `json:"url.m3u,omitempty"`
|
||||||
|
URLXepg string `json:"url.xepg,omitempty"`
|
||||||
|
VersionAPI string `json:"version.api,omitempty"`
|
||||||
|
VersionXteve string `json:"version.xteve,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// WebScreenLogStruct : Logs werden im RAM gespeichert und für das Webinterface bereitgestellt
|
||||||
|
type WebScreenLogStruct struct {
|
||||||
|
Errors int `json:"errors,required"`
|
||||||
|
Log []string `json:"log,required"`
|
||||||
|
Warnings int `json:"warnings,required"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
package src
|
||||||
|
|
||||||
|
import "encoding/xml"
|
||||||
|
|
||||||
|
// XMLTV : XMLTV Datei
|
||||||
|
type XMLTV struct {
|
||||||
|
Generator string `xml:"generator-info-name,attr"`
|
||||||
|
Source string `xml:"source-info-name,attr"`
|
||||||
|
XMLName xml.Name `xml:"tv"`
|
||||||
|
|
||||||
|
Channel []*Channel `xml:"channel"`
|
||||||
|
Program []*Program `xml:"programme"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Channel : Kanäle
|
||||||
|
type Channel struct {
|
||||||
|
ID string `xml:"id,attr"`
|
||||||
|
DisplayName []DisplayName `xml:"display-name"`
|
||||||
|
Icon Icon `xml:"icon"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisplayName : Kanalname
|
||||||
|
type DisplayName struct {
|
||||||
|
Value string `xml:",chardata"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Icon : Senderlogo
|
||||||
|
type Icon struct {
|
||||||
|
Src string `xml:"src,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Program : Programme
|
||||||
|
type Program struct {
|
||||||
|
Channel string `xml:"channel,attr"`
|
||||||
|
Start string `xml:"start,attr"`
|
||||||
|
Stop string `xml:"stop,attr"`
|
||||||
|
|
||||||
|
Title []*Title `xml:"title"`
|
||||||
|
SubTitle []*SubTitle `xml:"sub-title"`
|
||||||
|
Desc []*Desc `xml:"desc"`
|
||||||
|
Category []*Category `xml:"category"`
|
||||||
|
Country []*Country `xml:"country"`
|
||||||
|
EpisodeNum []*EpisodeNum `xml:"episode-num"`
|
||||||
|
Poster []Poster `xml:"icon"`
|
||||||
|
Language []*Language `xml:"language"`
|
||||||
|
Video Video `xml:"video"`
|
||||||
|
Date string `xml:"date"`
|
||||||
|
PreviouslyShown *PreviouslyShown `xml:"previously-shown"`
|
||||||
|
New *New `xml:"new"`
|
||||||
|
Live *Live `xml:"live"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Title : Programmtitel
|
||||||
|
type Title struct {
|
||||||
|
Lang string `xml:"lang,attr"`
|
||||||
|
Value string `xml:",chardata"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubTitle : Kurzbeschreibung
|
||||||
|
type SubTitle struct {
|
||||||
|
Lang string `xml:"lang,attr"`
|
||||||
|
Value string `xml:",chardata"`
|
||||||
|
}
|
||||||
|
|
||||||
|
//Desc : Programmbeschreibung
|
||||||
|
type Desc struct {
|
||||||
|
Lang string `xml:"lang,attr"`
|
||||||
|
Value string `xml:",chardata"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Category : Kategorien
|
||||||
|
type Category struct {
|
||||||
|
Lang string `xml:"lang,attr"`
|
||||||
|
Value string `xml:",chardata"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Language : Sprachen
|
||||||
|
type Language struct {
|
||||||
|
Value string `xml:",chardata"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Country : Länder
|
||||||
|
type Country struct {
|
||||||
|
Lang string `xml:"lang,attr"`
|
||||||
|
Value string `xml:",chardata"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// EpisodeNum : Episodennummerierung
|
||||||
|
type EpisodeNum struct {
|
||||||
|
System string `xml:"system,attr"`
|
||||||
|
Value string `xml:",chardata"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Poster : Programmposter / Cover
|
||||||
|
type Poster struct {
|
||||||
|
Height string `xml:"height,attr"`
|
||||||
|
Src string `xml:"src,attr"`
|
||||||
|
Value string `xml:",chardata"`
|
||||||
|
Width string `xml:"width,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Video : Video Metadaten
|
||||||
|
type Video struct {
|
||||||
|
Aspect string `xml:"aspect,omitempty"`
|
||||||
|
Colour string `xml:"colour,omitempty"`
|
||||||
|
Present string `xml:"present,omitempty"`
|
||||||
|
Quality string `xml:"quality,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PreviouslyShown : Widerholung bzw. Erstausstrahlung
|
||||||
|
type PreviouslyShown struct {
|
||||||
|
Start string `xml:"start,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// New : Sendung als neu deklarieren
|
||||||
|
type New struct {
|
||||||
|
Value string `xml:",chardata"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Live : Sendung als Liveübertragung deklarieren
|
||||||
|
type Live struct {
|
||||||
|
Value string `xml:",chardata"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,323 @@
|
|||||||
|
package src
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Entwicklerinfos anzeigen
|
||||||
|
func showDevInfo() {
|
||||||
|
|
||||||
|
if System.Dev == true {
|
||||||
|
|
||||||
|
fmt.Print("\033[31m")
|
||||||
|
fmt.Println("* * * * * D E V M O D E * * * * *")
|
||||||
|
fmt.Println("Version: ", System.Version)
|
||||||
|
fmt.Println("Build: ", System.Build)
|
||||||
|
fmt.Println("* * * * * * * * * * * * * * * * * *")
|
||||||
|
fmt.Print("\033[0m")
|
||||||
|
fmt.Println()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alle Systemordner erstellen
|
||||||
|
func createSystemFolders() (err error) {
|
||||||
|
|
||||||
|
e := reflect.ValueOf(&System.Folder).Elem()
|
||||||
|
|
||||||
|
for i := 0; i < e.NumField(); i++ {
|
||||||
|
|
||||||
|
var folder = e.Field(i).Interface().(string)
|
||||||
|
|
||||||
|
err = checkFolder(folder)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alle Systemdateien erstellen
|
||||||
|
func createSystemFiles() (err error) {
|
||||||
|
|
||||||
|
var debug string
|
||||||
|
for _, file := range SystemFiles {
|
||||||
|
|
||||||
|
var filename = getPlatformFile(System.Folder.Config + file)
|
||||||
|
|
||||||
|
err = checkFile(filename)
|
||||||
|
if err != nil {
|
||||||
|
// Datei existiert nicht, wird jetzt erstellt
|
||||||
|
err = saveMapToJSONFile(filename, make(map[string]interface{}))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
debug = fmt.Sprintf("Create File:%s", filename)
|
||||||
|
showDebug(debug, 1)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
switch file {
|
||||||
|
|
||||||
|
case "authentication.json":
|
||||||
|
System.File.Authentication = filename
|
||||||
|
case "pms.json":
|
||||||
|
System.File.PMS = filename
|
||||||
|
case "settings.json":
|
||||||
|
System.File.Settings = filename
|
||||||
|
case "xepg.json":
|
||||||
|
System.File.XEPG = filename
|
||||||
|
case "urls.json":
|
||||||
|
System.File.URLS = filename
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Einstellungen laden und default Werte setzen (xTeVe)
|
||||||
|
func loadSettings() (settings SettingsStrcut, err error) {
|
||||||
|
|
||||||
|
settingsMap, err := loadJSONFileToMap(System.File.Settings)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deafult Werte setzten
|
||||||
|
var defaults = make(map[string]interface{})
|
||||||
|
var dataMap = make(map[string]interface{})
|
||||||
|
|
||||||
|
dataMap["xmltv"] = make(map[string]interface{})
|
||||||
|
dataMap["m3u"] = make(map[string]interface{})
|
||||||
|
dataMap["hdhr"] = make(map[string]interface{})
|
||||||
|
|
||||||
|
defaults["api"] = false
|
||||||
|
defaults["authentication.api"] = false
|
||||||
|
defaults["authentication.m3u"] = false
|
||||||
|
defaults["authentication.pms"] = false
|
||||||
|
defaults["authentication.web"] = false
|
||||||
|
defaults["authentication.xml"] = false
|
||||||
|
defaults["backup.keep"] = 10
|
||||||
|
defaults["backup.path"] = System.Folder.Backup
|
||||||
|
defaults["buffer"] = false
|
||||||
|
defaults["buffer.size.kb"] = 1024
|
||||||
|
defaults["buffer.timeout"] = 500
|
||||||
|
defaults["cache.images"] = false
|
||||||
|
defaults["epgSource"] = "XEPG"
|
||||||
|
defaults["files"] = dataMap
|
||||||
|
defaults["files.update"] = true
|
||||||
|
defaults["filter"] = make(map[string]interface{})
|
||||||
|
defaults["git.branch"] = System.Branch
|
||||||
|
defaults["language"] = "en"
|
||||||
|
defaults["log.entries.ram"] = 500
|
||||||
|
defaults["mapping.first.channel"] = 1000
|
||||||
|
defaults["xepg.replace.missing.images"] = true
|
||||||
|
defaults["m3u8.adaptive.bandwidth.mbps"] = 10
|
||||||
|
defaults["port"] = "34400"
|
||||||
|
defaults["ssdp"] = true
|
||||||
|
defaults["tuner"] = 1
|
||||||
|
defaults["update"] = []string{"0000"}
|
||||||
|
defaults["user.agent"] = System.Name
|
||||||
|
defaults["uuid"] = createUUID()
|
||||||
|
defaults["version"] = System.Version
|
||||||
|
defaults["xteveAutoUpdate"] = true
|
||||||
|
defaults["temp.path"] = System.Folder.Temp
|
||||||
|
|
||||||
|
// Default Werte setzen
|
||||||
|
for key, value := range defaults {
|
||||||
|
if _, ok := settingsMap[key]; !ok {
|
||||||
|
settingsMap[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal([]byte(mapToJSON(settingsMap)), &settings)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Einstellungen von den Flags übernehmen
|
||||||
|
if len(System.Flag.Port) > 0 {
|
||||||
|
settings.Port = System.Flag.Port
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(System.Flag.Branch) > 0 {
|
||||||
|
settings.Branch = System.Flag.Branch
|
||||||
|
showInfo(fmt.Sprintf("Git Branch:Switching Git Branch to -> %s", settings.Branch))
|
||||||
|
}
|
||||||
|
|
||||||
|
err = saveSettings(settings)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Einstellungen speichern (xTeVe)
|
||||||
|
func saveSettings(settings SettingsStrcut) (err error) {
|
||||||
|
|
||||||
|
if settings.BackupKeep == 0 {
|
||||||
|
settings.BackupKeep = 10
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(settings.BackupPath) == 0 {
|
||||||
|
settings.BackupPath = System.Folder.Backup
|
||||||
|
}
|
||||||
|
|
||||||
|
if settings.BufferTimeout < 0 {
|
||||||
|
settings.BufferTimeout = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
System.Folder.Temp = settings.TempPath + settings.UUID + string(os.PathSeparator)
|
||||||
|
|
||||||
|
err = writeByteToFile(System.File.Settings, []byte(mapToJSON(settings)))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Settings = settings
|
||||||
|
|
||||||
|
if System.Dev == true {
|
||||||
|
Settings.UUID = "2019-01-DEV-xTeVe!"
|
||||||
|
}
|
||||||
|
|
||||||
|
setDeviceID()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zugriff über die Domain ermöglichen
|
||||||
|
func setGlobalDomain(domain string) {
|
||||||
|
|
||||||
|
System.Domain = domain
|
||||||
|
|
||||||
|
switch Settings.AuthenticationPMS {
|
||||||
|
case true:
|
||||||
|
System.Addresses.DVR = "username:password@" + System.Domain
|
||||||
|
case false:
|
||||||
|
System.Addresses.DVR = System.Domain
|
||||||
|
}
|
||||||
|
|
||||||
|
switch Settings.AuthenticationM3U {
|
||||||
|
case true:
|
||||||
|
System.Addresses.M3U = System.ServerProtocol.M3U + "://" + System.Domain + "/m3u/xteve.m3u?username=xxx&password=yyy<br>(Specific groups: [http://...&group-title=foo,bar])"
|
||||||
|
case false:
|
||||||
|
System.Addresses.M3U = System.ServerProtocol.M3U + "://" + System.Domain + "/m3u/xteve.m3u (Specific groups: [http://...?group-title=foo,bar])"
|
||||||
|
}
|
||||||
|
|
||||||
|
switch Settings.AuthenticationXML {
|
||||||
|
case true:
|
||||||
|
System.Addresses.XML = System.ServerProtocol.XML + "://" + System.Domain + "/xmltv/xteve.xml?username=xxx&password=yyy"
|
||||||
|
case false:
|
||||||
|
System.Addresses.XML = System.ServerProtocol.XML + "://" + System.Domain + "/xmltv/xteve.xml"
|
||||||
|
}
|
||||||
|
|
||||||
|
if Settings.EpgSource != "XEPG" {
|
||||||
|
System.Addresses.M3U = getErrMsg(2106)
|
||||||
|
System.Addresses.XML = getErrMsg(2106)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// UUID generieren
|
||||||
|
func createUUID() (uuid string) {
|
||||||
|
uuid = time.Now().Format("2006-01") + "-" + randomString(4) + "-" + randomString(6)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Eindeutige Geräte ID für Plex generieren
|
||||||
|
func setDeviceID() {
|
||||||
|
|
||||||
|
var id = Settings.UUID
|
||||||
|
|
||||||
|
switch Settings.Tuner {
|
||||||
|
case 1:
|
||||||
|
System.DeviceID = id
|
||||||
|
|
||||||
|
default:
|
||||||
|
System.DeviceID = fmt.Sprintf("%s:%d", id, Settings.Tuner)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Provider Streaming-URL zu xTeVe Streaming-URL konvertieren
|
||||||
|
func createStreamingURL(streamingType, playlistID, channelNumber, channelName, url string) (streamingURL string, err error) {
|
||||||
|
|
||||||
|
var streamInfo StreamInfo
|
||||||
|
var serverProtocol string
|
||||||
|
|
||||||
|
if len(Data.Cache.StreamingURLS) == 0 {
|
||||||
|
Data.Cache.StreamingURLS = make(map[string]StreamInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
var urlID = getMD5(fmt.Sprintf("%s-%s", playlistID, url))
|
||||||
|
|
||||||
|
if s, ok := Data.Cache.StreamingURLS[urlID]; ok {
|
||||||
|
|
||||||
|
streamInfo = s
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
streamInfo.URL = url
|
||||||
|
streamInfo.Name = channelName
|
||||||
|
streamInfo.PlaylistID = playlistID
|
||||||
|
streamInfo.ChannelNumber = channelNumber
|
||||||
|
streamInfo.URLid = urlID
|
||||||
|
|
||||||
|
Data.Cache.StreamingURLS[urlID] = streamInfo
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
switch streamingType {
|
||||||
|
|
||||||
|
case "DVR":
|
||||||
|
serverProtocol = System.ServerProtocol.DVR
|
||||||
|
|
||||||
|
case "M3U":
|
||||||
|
serverProtocol = System.ServerProtocol.M3U
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
streamingURL = fmt.Sprintf("%s://%s/stream/%s", serverProtocol, System.Domain, streamInfo.URLid)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getStreamInfo(urlID string) (streamInfo StreamInfo, err error) {
|
||||||
|
|
||||||
|
if len(Data.Cache.StreamingURLS) == 0 {
|
||||||
|
|
||||||
|
tmp, err := loadJSONFileToMap(System.File.URLS)
|
||||||
|
if err != nil {
|
||||||
|
return streamInfo, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal([]byte(mapToJSON(tmp)), &Data.Cache.StreamingURLS)
|
||||||
|
if err != nil {
|
||||||
|
return streamInfo, err
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if s, ok := Data.Cache.StreamingURLS[urlID]; ok {
|
||||||
|
streamInfo = s
|
||||||
|
streamInfo.URL = strings.Trim(streamInfo.URL, "\r\n")
|
||||||
|
} else {
|
||||||
|
err = errors.New("Streaming error")
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -0,0 +1,356 @@
|
|||||||
|
package src
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/md5"
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"os/user"
|
||||||
|
"path/filepath"
|
||||||
|
"text/template"
|
||||||
|
)
|
||||||
|
|
||||||
|
// --- System Tools ---
|
||||||
|
|
||||||
|
// Prüft ob der Ordner existiert, falls nicht, wir der Ordner erstellt
|
||||||
|
func checkFolder(path string) (err error) {
|
||||||
|
|
||||||
|
var debug string
|
||||||
|
_, err = os.Stat(filepath.Dir(path))
|
||||||
|
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
// Ordner existiert nicht, wird jetzt erstellt
|
||||||
|
|
||||||
|
err = os.MkdirAll(getPlatformPath(path), 0755)
|
||||||
|
if err == nil {
|
||||||
|
|
||||||
|
debug = fmt.Sprintf("Create Folder:%s", path)
|
||||||
|
showDebug(debug, 1)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prüft ob die datei im Dateisystem existiert
|
||||||
|
func checkFile(filename string) (err error) {
|
||||||
|
|
||||||
|
var file = getPlatformFile(filename)
|
||||||
|
|
||||||
|
if _, err = os.Stat(file); os.IsNotExist(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserHomeDirectory : Benutzer Homer Verzeichnis
|
||||||
|
func GetUserHomeDirectory() (userHomeDirectory string) {
|
||||||
|
|
||||||
|
usr, err := user.Current()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
|
||||||
|
for _, name := range []string{"HOME", "USERPROFILE"} {
|
||||||
|
|
||||||
|
if dir := os.Getenv(name); dir != "" {
|
||||||
|
userHomeDirectory = dir
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
userHomeDirectory = usr.HomeDir
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkFilePermission(dir string) (err error) {
|
||||||
|
|
||||||
|
var filename = dir + "permission.test"
|
||||||
|
|
||||||
|
err = ioutil.WriteFile(filename, []byte(""), 0644)
|
||||||
|
if err == nil {
|
||||||
|
err = os.RemoveAll(filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ordnerpfad für das laufende OS generieren
|
||||||
|
func getPlatformPath(path string) string {
|
||||||
|
return filepath.Dir(path) + string(os.PathSeparator)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dateipfad für das laufende OS generieren
|
||||||
|
func getPlatformFile(filename string) (osFilePath string) {
|
||||||
|
|
||||||
|
path, file := filepath.Split(filename)
|
||||||
|
var newPath = filepath.Dir(path)
|
||||||
|
osFilePath = newPath + string(os.PathSeparator) + file
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dateinamen aus dem Dateipfad ausgeben
|
||||||
|
func getFilenameFromPath(path string) (file string) {
|
||||||
|
return filepath.Base(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nicht mehr verwendete Systemdaten löschen
|
||||||
|
func removeOldSystemData() {
|
||||||
|
// Temporären Ordner löschen
|
||||||
|
os.RemoveAll(System.Folder.Temp)
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
func removeChildItems(dir string) error {
|
||||||
|
|
||||||
|
files, err := filepath.Glob(filepath.Join(dir, "*"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, file := range files {
|
||||||
|
|
||||||
|
err = os.RemoveAll(file)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// JSON
|
||||||
|
func mapToJSON(tmpMap interface{}) string {
|
||||||
|
|
||||||
|
jsonString, err := json.MarshalIndent(tmpMap, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return "{}"
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(jsonString)
|
||||||
|
}
|
||||||
|
|
||||||
|
func jsonToMap(content string) map[string]interface{} {
|
||||||
|
|
||||||
|
var tmpMap = make(map[string]interface{})
|
||||||
|
json.Unmarshal([]byte(content), &tmpMap)
|
||||||
|
|
||||||
|
return (tmpMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
func jsonToMapInt64(content string) map[int64]interface{} {
|
||||||
|
|
||||||
|
var tmpMap = make(map[int64]interface{})
|
||||||
|
json.Unmarshal([]byte(content), &tmpMap)
|
||||||
|
|
||||||
|
return (tmpMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
func jsonToInterface(content string) (tmpMap interface{}, err error) {
|
||||||
|
|
||||||
|
err = json.Unmarshal([]byte(content), &tmpMap)
|
||||||
|
return
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func saveMapToJSONFile(file string, tmpMap interface{}) error {
|
||||||
|
|
||||||
|
var filename = getPlatformFile(file)
|
||||||
|
jsonString, err := json.MarshalIndent(tmpMap, "", " ")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ioutil.WriteFile(filename, []byte(jsonString), 0644)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadJSONFileToMap(file string) (tmpMap map[string]interface{}, err error) {
|
||||||
|
|
||||||
|
f, err := os.Open(getPlatformFile(file))
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
content, err := ioutil.ReadAll(f)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
err = json.Unmarshal([]byte(content), &tmpMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
f.Close()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Binary
|
||||||
|
func readByteFromFile(file string) (content []byte, err error) {
|
||||||
|
|
||||||
|
f, err := os.Open(getPlatformFile(file))
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
content, err = ioutil.ReadAll(f)
|
||||||
|
f.Close()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeByteToFile(file string, data []byte) (err error) {
|
||||||
|
|
||||||
|
var filename = getPlatformFile(file)
|
||||||
|
err = ioutil.WriteFile(filename, data, 0644)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func readStringFromFile(file string) (str string, err error) {
|
||||||
|
|
||||||
|
var content []byte
|
||||||
|
var filename = getPlatformFile(file)
|
||||||
|
|
||||||
|
err = checkFile(filename)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
content, err = ioutil.ReadFile(filename)
|
||||||
|
if err != nil {
|
||||||
|
ShowError(err, 0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
str = string(content)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Netzwerk
|
||||||
|
func resolveHostIP() (err error) {
|
||||||
|
|
||||||
|
netInterfaceAddresses, err := net.InterfaceAddrs()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, netInterfaceAddress := range netInterfaceAddresses {
|
||||||
|
|
||||||
|
networkIP, ok := netInterfaceAddress.(*net.IPNet)
|
||||||
|
System.IPAddressesList = append(System.IPAddressesList, networkIP.IP.String())
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
|
||||||
|
var ip = networkIP.IP.String()
|
||||||
|
|
||||||
|
if networkIP.IP.To4() != nil {
|
||||||
|
|
||||||
|
System.IPAddressesV4 = append(System.IPAddressesV4, ip)
|
||||||
|
|
||||||
|
if !networkIP.IP.IsLoopback() && ip[0:7] != "169.254" {
|
||||||
|
System.IPAddress = ip
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
System.IPAddressesV6 = append(System.IPAddressesV6, ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
System.Hostname, err = os.Hostname()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sonstiges
|
||||||
|
func randomString(n int) string {
|
||||||
|
|
||||||
|
const alphanum = "AB1CD2EF3GH4IJ5KL6MN7OP8QR9ST0UVWXYZ"
|
||||||
|
|
||||||
|
var bytes = make([]byte, n)
|
||||||
|
|
||||||
|
rand.Read(bytes)
|
||||||
|
|
||||||
|
for i, b := range bytes {
|
||||||
|
bytes[i] = alphanum[b%byte(len(alphanum))]
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseTemplate(content string, tmpMap map[string]interface{}) (result string) {
|
||||||
|
|
||||||
|
t := template.Must(template.New("template").Parse(content))
|
||||||
|
|
||||||
|
var tpl bytes.Buffer
|
||||||
|
|
||||||
|
if err := t.Execute(&tpl, tmpMap); err != nil {
|
||||||
|
ShowError(err, 0)
|
||||||
|
}
|
||||||
|
result = tpl.String()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func indexOfString(element string, data []string) int {
|
||||||
|
|
||||||
|
for k, v := range data {
|
||||||
|
if element == v {
|
||||||
|
return k
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func indexOfFloat64(element float64, data []float64) int {
|
||||||
|
|
||||||
|
for k, v := range data {
|
||||||
|
if element == v {
|
||||||
|
return (k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func indexOfInt(element int, data []int) int {
|
||||||
|
|
||||||
|
for k, v := range data {
|
||||||
|
if element == v {
|
||||||
|
return (k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func getMD5(str string) string {
|
||||||
|
|
||||||
|
md5Hasher := md5.New()
|
||||||
|
md5Hasher.Write([]byte(str))
|
||||||
|
|
||||||
|
return hex.EncodeToString(md5Hasher.Sum(nil))
|
||||||
|
}
|
||||||
@@ -0,0 +1,273 @@
|
|||||||
|
package src
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
up2date "../src/internal/up2date/client"
|
||||||
|
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BinaryUpdate : Binary Update Prozess. Git Branch master und beta wird von GitHub geladen.
|
||||||
|
func BinaryUpdate() (err error) {
|
||||||
|
|
||||||
|
if System.GitHub.Update == false {
|
||||||
|
showWarning(2099)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var debug string
|
||||||
|
|
||||||
|
var updater = &up2date.Updater
|
||||||
|
updater.Name = System.Update.Name
|
||||||
|
updater.Branch = System.Branch
|
||||||
|
|
||||||
|
up2date.Init()
|
||||||
|
|
||||||
|
switch System.Branch {
|
||||||
|
|
||||||
|
// Update von GitHub
|
||||||
|
case "master", "beta":
|
||||||
|
|
||||||
|
var gitInfo = fmt.Sprintf("%s/%s/info.json?raw=true", System.Update.Git, System.Branch)
|
||||||
|
var zipFile = fmt.Sprintf("%s/%s/%s_%s_%s.zip?raw=true", System.Update.Git, System.Branch, System.AppName, System.OS, System.ARCH)
|
||||||
|
var body []byte
|
||||||
|
|
||||||
|
var git GitStruct
|
||||||
|
|
||||||
|
resp, err := http.Get(gitInfo)
|
||||||
|
if err != nil {
|
||||||
|
ShowError(err, 0)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
|
||||||
|
if resp.StatusCode == 404 {
|
||||||
|
err = fmt.Errorf(fmt.Sprintf("Update Server: %s (%s)", http.StatusText(resp.StatusCode), gitInfo))
|
||||||
|
ShowError(err, 6003)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err = fmt.Errorf(fmt.Sprintf("%d: %s (%s)", resp.StatusCode, http.StatusText(resp.StatusCode), gitInfo))
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err = ioutil.ReadAll(resp.Body)
|
||||||
|
|
||||||
|
err = json.Unmarshal(body, &git)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
updater.Response.Status = true
|
||||||
|
updater.Response.UpdateZIP = zipFile
|
||||||
|
updater.Response.Version = git.Version
|
||||||
|
updater.Response.Filename = git.Filename
|
||||||
|
|
||||||
|
// Update vom eigenen Server
|
||||||
|
default:
|
||||||
|
|
||||||
|
updater.URL = Settings.UpdateURL
|
||||||
|
|
||||||
|
if len(updater.URL) == 0 {
|
||||||
|
showInfo(fmt.Sprintf("Update URL:No server URL specified, update will not be performed. Branch: %s", System.Branch))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
showInfo("Update URL:" + updater.URL)
|
||||||
|
fmt.Println("-----------------")
|
||||||
|
|
||||||
|
// Versionsinformationen vom Server laden
|
||||||
|
err = up2date.GetVersion()
|
||||||
|
if err != nil {
|
||||||
|
|
||||||
|
debug = fmt.Sprintf(err.Error())
|
||||||
|
showDebug(debug, 1)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(updater.Response.Reason) > 0 {
|
||||||
|
|
||||||
|
err = fmt.Errorf(fmt.Sprintf("Update Server: %s", updater.Response.Reason))
|
||||||
|
ShowError(err, 6002)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
var currentVersion = System.Version + "." + System.Build
|
||||||
|
|
||||||
|
// Versionsnummer überprüfen
|
||||||
|
if updater.Response.Version > currentVersion && updater.Response.Status == true {
|
||||||
|
|
||||||
|
if Settings.XteveAutoUpdate == true {
|
||||||
|
// Update durchführen
|
||||||
|
var fileType, url string
|
||||||
|
|
||||||
|
showInfo(fmt.Sprintf("Update Available:Version: %s", updater.Response.Version))
|
||||||
|
|
||||||
|
switch System.Branch {
|
||||||
|
|
||||||
|
// Update von GitHub
|
||||||
|
case "master", "beta":
|
||||||
|
showInfo(fmt.Sprintf("Update Server:GitHub"))
|
||||||
|
|
||||||
|
// Update vom eigenen Server
|
||||||
|
default:
|
||||||
|
showInfo(fmt.Sprintf("Update Server:%s", Settings.UpdateURL))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
showInfo(fmt.Sprintf("Start Update:Branch: %s", updater.Branch))
|
||||||
|
|
||||||
|
// Neue Version als BIN Datei herunterladen
|
||||||
|
if len(updater.Response.UpdateBIN) > 0 {
|
||||||
|
url = updater.Response.UpdateBIN
|
||||||
|
fileType = "bin"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Neue Version als ZIP Datei herunterladen
|
||||||
|
if len(updater.Response.UpdateZIP) > 0 {
|
||||||
|
url = updater.Response.UpdateZIP
|
||||||
|
fileType = "zip"
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(url) > 0 {
|
||||||
|
|
||||||
|
err = up2date.DoUpdate(fileType, updater.Response.Filename)
|
||||||
|
if err != nil {
|
||||||
|
ShowError(err, 6002)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Hinweis ausgeben
|
||||||
|
showWarning(6004)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func conditionalUpdateChanges() (err error) {
|
||||||
|
|
||||||
|
checkVersion:
|
||||||
|
settingsMap, err := loadJSONFileToMap(System.File.Settings)
|
||||||
|
if err != nil || len(settingsMap) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if settingsVersion, ok := settingsMap["version"].(string); ok {
|
||||||
|
|
||||||
|
// Letzte Kompatible Version (1.4.4)
|
||||||
|
if settingsVersion < System.Compatibility {
|
||||||
|
err = errors.New(getErrMsg(1013))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch settingsVersion {
|
||||||
|
|
||||||
|
case "1.4.4":
|
||||||
|
// UUID Wert in xepg.json setzen
|
||||||
|
err = setValueForUUID()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Neuer Filter (WebUI). Alte Filtereinstellungen werden konvertiert
|
||||||
|
if oldFilter, ok := settingsMap["filter"].([]interface{}); ok {
|
||||||
|
var newFilterMap = convertToNewFilter(oldFilter)
|
||||||
|
settingsMap["filter"] = newFilterMap
|
||||||
|
|
||||||
|
settingsMap["version"] = "1.9.0"
|
||||||
|
|
||||||
|
err = saveMapToJSONFile(System.File.Settings, settingsMap)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
goto checkVersion
|
||||||
|
|
||||||
|
} else {
|
||||||
|
err = errors.New(getErrMsg(1030))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
case "1.9.0":
|
||||||
|
// Falls es in einem späteren Update Änderungen an der Datenbank gibt, geht es hier weiter
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// settings.json ist zu alt (älter als Version 1.4.4)
|
||||||
|
err = errors.New(getErrMsg(1013))
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertToNewFilter(oldFilter []interface{}) (newFilterMap map[int]interface{}) {
|
||||||
|
|
||||||
|
newFilterMap = make(map[int]interface{})
|
||||||
|
|
||||||
|
switch reflect.TypeOf(oldFilter).Kind() {
|
||||||
|
|
||||||
|
case reflect.Slice:
|
||||||
|
s := reflect.ValueOf(oldFilter)
|
||||||
|
|
||||||
|
for i := 0; i < s.Len(); i++ {
|
||||||
|
|
||||||
|
var newFilter FilterStruct
|
||||||
|
newFilter.Active = true
|
||||||
|
newFilter.Name = fmt.Sprintf("Custom filter %d", i+1)
|
||||||
|
newFilter.Filter = s.Index(i).Interface().(string)
|
||||||
|
newFilter.Type = "custom-filter"
|
||||||
|
newFilter.CaseSensitive = false
|
||||||
|
|
||||||
|
newFilterMap[i] = newFilter
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func setValueForUUID() (err error) {
|
||||||
|
|
||||||
|
xepg, err := loadJSONFileToMap(System.File.XEPG)
|
||||||
|
|
||||||
|
for _, c := range xepg {
|
||||||
|
|
||||||
|
var xepgChannel = c.(map[string]interface{})
|
||||||
|
|
||||||
|
if uuidKey, ok := xepgChannel["_uuid.key"].(string); ok {
|
||||||
|
|
||||||
|
if value, ok := xepgChannel[uuidKey].(string); ok {
|
||||||
|
|
||||||
|
if len(value) > 0 {
|
||||||
|
xepgChannel["_uuid.value"] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
err = saveMapToJSONFile(System.File.XEPG, xepg)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
package src
|
||||||
|
|
||||||
|
var webUI = make(map[string]interface{})
|
||||||
|
|
||||||
|
func loadHTMLMap() {
|
||||||
|
|
||||||
|
webUI["html/img/m3u.png"] = "iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAAXNSR0IArs4c6QAAAAlwSFlzAAAsSwAALEsBpT2WqQAABCRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IlhNUCBDb3JlIDUuNC4wIj4KICAgPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgICAgICAgICAgIHhtbG5zOmV4aWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vZXhpZi8xLjAvIgogICAgICAgICAgICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgICAgICAgICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyI+CiAgICAgICAgIDx0aWZmOlJlc29sdXRpb25Vbml0PjI8L3RpZmY6UmVzb2x1dGlvblVuaXQ+CiAgICAgICAgIDx0aWZmOkNvbXByZXNzaW9uPjU8L3RpZmY6Q29tcHJlc3Npb24+CiAgICAgICAgIDx0aWZmOlhSZXNvbHV0aW9uPjI4ODwvdGlmZjpYUmVzb2x1dGlvbj4KICAgICAgICAgPHRpZmY6T3JpZW50YXRpb24+MTwvdGlmZjpPcmllbnRhdGlvbj4KICAgICAgICAgPHRpZmY6WVJlc29sdXRpb24+Mjg4PC90aWZmOllSZXNvbHV0aW9uPgogICAgICAgICA8ZXhpZjpQaXhlbFhEaW1lbnNpb24+NTA8L2V4aWY6UGl4ZWxYRGltZW5zaW9uPgogICAgICAgICA8ZXhpZjpDb2xvclNwYWNlPjE8L2V4aWY6Q29sb3JTcGFjZT4KICAgICAgICAgPGV4aWY6UGl4ZWxZRGltZW5zaW9uPjUwPC9leGlmOlBpeGVsWURpbWVuc2lvbj4KICAgICAgICAgPGRjOnN1YmplY3Q+CiAgICAgICAgICAgIDxyZGY6QmFnLz4KICAgICAgICAgPC9kYzpzdWJqZWN0PgogICAgICAgICA8eG1wOk1vZGlmeURhdGU+MjAxOC0wNy0yOFQxOTowNzozMTwveG1wOk1vZGlmeURhdGU+CiAgICAgICAgIDx4bXA6Q3JlYXRvclRvb2w+UGl4ZWxtYXRvciAzLjM8L3htcDpDcmVhdG9yVG9vbD4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+CumjVbcAAAGWSURBVGgF7VoxTgJRFGTFaGKBFnbEcABjb0fiBego7D2ABYmn8ARKRWFNQ6gx4QRQGWJJoY2VhXGdl7Dkh7Dsx3ns3yXvJ5P97L6d92bmhwYqlcWK43gK5L2enP51NP8F7pN721wPnOITZx9qG6HxI8QIZO+9XCHeL+VQKKm8QMyxb6+iCpH528AQYs58xIQWknV8mhDxCjEXWWJCC8maT55fAmOIudpUXAYhMn8dGEHMTZqYsgiR+U+BAcTcrhNTJiEy/xHQg5jOqpjD1RsBP3+id8u3P8TUoij6SuoLIwRDfWOofjLYtteyHa1UfcvvcUT1jqpGauVuHnyAdragjv/TAkley3uhj9Y5ZhDQa2+Olgmhz4IygSWibChNZ4nQFioTWCLKhtJ0lghtoTKBJaJsKE1nidAWKhNYIsqG0nSWCG2hMoElomwoTWeJ0BYqE1giyobSdJYIbaEygSWibChNZ4nQFioTWCLKhtJ0biJzmi1/guXMrpDn/OegO3bXMuAH0TtgAvwARV3y57Q34AGoJkL+AErKZ9cqbH7AAAAAAElFTkSuQmCC"
|
||||||
|
webUI["html/img/mapping.png"] = "iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAAXNSR0IArs4c6QAAAAlwSFlzAAAsSwAALEsBpT2WqQAABCRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IlhNUCBDb3JlIDUuNC4wIj4KICAgPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgICAgICAgICAgIHhtbG5zOmV4aWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vZXhpZi8xLjAvIgogICAgICAgICAgICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgICAgICAgICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyI+CiAgICAgICAgIDx0aWZmOlJlc29sdXRpb25Vbml0PjI8L3RpZmY6UmVzb2x1dGlvblVuaXQ+CiAgICAgICAgIDx0aWZmOkNvbXByZXNzaW9uPjU8L3RpZmY6Q29tcHJlc3Npb24+CiAgICAgICAgIDx0aWZmOlhSZXNvbHV0aW9uPjI4ODwvdGlmZjpYUmVzb2x1dGlvbj4KICAgICAgICAgPHRpZmY6T3JpZW50YXRpb24+MTwvdGlmZjpPcmllbnRhdGlvbj4KICAgICAgICAgPHRpZmY6WVJlc29sdXRpb24+Mjg4PC90aWZmOllSZXNvbHV0aW9uPgogICAgICAgICA8ZXhpZjpQaXhlbFhEaW1lbnNpb24+NTA8L2V4aWY6UGl4ZWxYRGltZW5zaW9uPgogICAgICAgICA8ZXhpZjpDb2xvclNwYWNlPjE8L2V4aWY6Q29sb3JTcGFjZT4KICAgICAgICAgPGV4aWY6UGl4ZWxZRGltZW5zaW9uPjUwPC9leGlmOlBpeGVsWURpbWVuc2lvbj4KICAgICAgICAgPGRjOnN1YmplY3Q+CiAgICAgICAgICAgIDxyZGY6QmFnLz4KICAgICAgICAgPC9kYzpzdWJqZWN0PgogICAgICAgICA8eG1wOk1vZGlmeURhdGU+MjAxOC0wOC0wMlQxMjowODo5NzwveG1wOk1vZGlmeURhdGU+CiAgICAgICAgIDx4bXA6Q3JlYXRvclRvb2w+UGl4ZWxtYXRvciAzLjM8L3htcDpDcmVhdG9yVG9vbD4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+CpRxQsEAAAJLSURBVGgF7VoxTgMxEMwBHWlAaeABVCEtBcoD+EBewCdCyQsQtLyAPCBvCBSRIBUPCF1ogqBJjlkrezGr6HTrO98lhy1Z9trr2VmvnbMNjUadUhzHLeQB8hw5LS3QOUbubvIf7X3kKXIZibgS51bCZdWgMT6D8nECgArkngagQN0B8dhbkblalVmLIyheCGUthhjuLBq77MihA0xTjHHBEBBOorHLjjghbNOg4Mg2RYO4hIiEiHiaAV5aSwf8hRgjZdHtTTTc2ZFXpZkY+hMx5k3IZYlr7jgudJHp2JElLaF0K1mirYn8nAWgQB3ibM59ERNCA52d6Nghv9isQiUtn0kURe92I9eBcYA6YZxym8dyDuwRuMw82gjQYQbsPdLDdNCROO0US3uEfp3usTZpjf5J2CNtNFwjl7FHvmBnCB5PCQkQoJudJtGvE23sJEFuI39rQArS7dskXK6nlwkAKiB1VxAxLcyUePAH8cQmlbEul4+UM8LkVjPc2ZHcaFUDBEeqjoC0HyIiZ6RqOUSk6ghI+xyRD9mRQTYfIktPylaX16rhzo48KE29QH8kxjxC/hFtZYiGe70OjWVMW7Dx32bA3iO7//iAC8DOPweZFQhH6O+CmkRvW2f28oV8owEoUHdMPPg70rFJZajTkqT7uZ3ObaHEuuHOjnCpsb8vlKUsur2JhruLA94Y5QEOjuSZPR9jQ0R8zGoezNpFhN5RtUm+/bpgaG1u0jd2OSLDTRopbZ/okxcrLUYKvKprbRfHhXr8m5PK/y1V/gWRKLfiNSmxEAAAAABJRU5ErkJggg=="
|
||||||
|
webUI["html/js/.DS_Store"] = "AAAAAUJ1ZDEAABAAAAAIAAAAEAAAAAAlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAABAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAgLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAACAAAAABAAAAQAAAAAEAAACAAAAAAQAAAQAAAAABAAACAAAAAAEAAAQAAAAAAAAAAAEAABAAAAAAAQAAIAAAAAABAABAAAAAAAEAAIAAAAAAAQABAAAAAAABAAIAAAAAAAEABAAAAAAAAQAIAAAAAAABABAAAAAAAAEAIAAAAAAAAQBAAAAAAAABAIAAAAAAAAEBAAAAAAAAAQIAAAAAAAABBAAAAAAAAAEIAAAAAAAAARAAAAAAAAABIAAAAAAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALine truncated
|
||||||
|
webUI["html/lang/en.json"] = "ewogICJtYWluTWVudSI6IHsKICAgICJpdGVtIjp7CiAgICAgICJwbGF5bGlzdCI6ICJQbGF5bGlzdCIsCiAgICAgICJwbXNJRCI6ICJQTVMgSUQiLAogICAgICAiZmlsdGVyIjogIkZpbHRlciIsCiAgICAgICJ4bWx0diI6ICJYTUxUViIsCiAgICAgICJtYXBwaW5nIjogIk1hcHBpbmciLAogICAgICAidXNlcnMiOiAiVXNlcnMiLAogICAgICAic2V0dGluZ3MiOiAiU2V0dGluZ3MiLAogICAgICAibG9nIjogIkxvZyIsCiAgICAgICJsb2dvdXQiOiAiTG9nb3V0IgogICAgfSwKICAgICJoZWFkbGluZSI6IHsKICAgICAgInBsYXlsaXN0IjogIkxvY2FsIG9yIHJlbW90ZSBwbGF5bGlzdHMiLAogICAgICAiZmlsdGVyIjogIkZpbHRlciBwbGF5bGlzdCIsCiAgICAgICJ4bWx0diI6ICJMb2NhbCBvciByZW1vdGUgWE1MVFYgZmlsZXMiLAogICAgICAibWFwcGluZyI6ICJNYXAgcGxheWxpc3QgY2hhbm5lbHMgdG8gRVBHIGNoYW5uZWxzIiwKICAgICAgInVzZXJzIjogIlVzZXIgbWFuYWdlbWVudCIsCiAgICAgICJzZXR0aW5ncyI6ICJTZXR0aW5ncyIsCiAgICAgICJsb2ciOiAiTG9nIiwKICAgICAgImxvZ291dCI6ICJMb2dvdXQiCiAgICB9CiAgfSwKICAiY29uZmlybSI6ewogICAgInJlc3RvcmUiOiAiQWxsIGRhdGEgd2lsbCBiZSByZXBsYWNlZCB3aXRoIHRob3NlIGZyb20gdGhlIGJhY2t1cC5TaG91bGQgdGhlIGZpbGVzIGJlIHJlc3RvcmVkPyIKICB9LAogICJhbGVydCI6IHsKICAgICJmaWxlTG9hZGluZ0Vycm9yIjogIkZpbGUgY291bGRuJ3QgYmUgbG9hZGVkIiwKICAgICJpbnZhbGlkQ2hhbm5lbE51bWJlciI6ICJJbnZhbGlkIGNoYW5uZWwgbnVtYmVyIgogIH0sCiAgImJ1dHRvbiI6ewogICAgImJhY2siOiAiQmFjayIsCiAgICAiYmFja3VwIjogIkJhY2t1cCIsCiAgICAiYnVsa0VkaXQiOiAiQnVsayBFZGl0IiwKICAgICJjYW5jZWwiOiAiQ2FuY2VsIiwKICAgICJkZWxldGUiOiAiRGVsZXRlIiwKICAgICJkb25lIjogIkRvbmUiLAogICAgImxvZ2luIjogIkxvZ2luIiwKICAgICJuZXciOiAiTmV3IiwKICAgICJuZXh0IjogIk5leHQiLAogICAgInJlc3RvcmUiOiAiUmVzdG9yZSIsCiAgICAic2F2ZSI6ICJTYXZlIiwKICAgICJzZWFyY2giOiAiU2VhcmNoIiwKICAgICJ1cGRhdGUiOiAiVXBkYXRlIiwKICAgICJjcmFldGVBY2NvdW50IjogIkNyZWF0ZSBBY2NvdW50IiwKICAgICJyZXNldGxvZ3MiOiAiUmVzZXQgTG9ncyIsCiAgICAidXBsb2FkTG9nbyI6ICJVcGxvYWQgTG9nbyIKICB9LAogICJmaWx0ZXIiOiB7CiAgICAidGFibGUiOiB7CiAgICAgICJuYW1lIjogIkZpbHRlciBOYW1lIiwKICAgICAgInR5cGUiOiAiRmlsdGVyIFR5cGUiLAogICAgICAiZmlsdGVyIjogIkZpbHRlciIKICAgIH0sCiAgICAiY3VzdG9tIjogIkN1c3RvbSIsCiAgICAiZ3JvdXAiOiAiR3JvdXAiLAogICAgIm5hbWUiOiB7CiAgICAgICJ0aXRsZSI6ICJGaWx0ZXIgTmFtZSIsCiAgICAgICJwbGFjZWhvbGRlciI6ICJGaWx0ZXIgbmFtZSIsCiAgICAgICJkZXNjcmlwdGlvbiI6ICIiCiAgICB9LAogICAgImRlc2NyaXB0aW9uIjogewogICAgICAidGl0bGUiOiAiRGVzY3JpcHRpb24iLAogICAgICAicGxhY2Vob2xkZXIiOiAiRGVzY3JpcHRpb24iLAogICAgICAiZGVzY3JpcHRpb24iOiAiIgogICAgfSwKICAgICJ0eXBlIjogewogICAgICAidGl0bGUiOiAiVHlwZSIsCiAgICAgICJncm91cFRpdGxlIjogIkdyb3VwIFRpdGxlIiwKICAgICAgImN1c3RvbUZpbHRlciI6ICJDdXN0b20gRmlsdGVyIgogICAgfSwKICAgICJjYXNlU2Vuc2l0aXZlIjogewogICAgICAidGl0bGUiOiAiQ2FzZSBTZW5zaXRpdmUiLAogICAgICAicGxhY2Vob2xkZXIiOiAiIiwKICAgICAgImRlc2NyaXB0aW9uIjogIiIKICAgIH0sCiAgICAiZmlsdGVyUnVsZSI6IHsKICAgICAgInRpdGxlIjogIkZpbHRlciBSdWxlIiwKICAgICAgInBsYWNlaG9sZGVyIjogIlNwb3J0IHtIRH0gIXtFUyxJVH0iLAogICAgICAiZGVzY3JpcHRpb24iOiAiIgogICAgfSwKICAgICJmaWx0ZXJHcm91cCI6IHsKICAgICAgInRpdGxlIjogIkdyb3VwIFRpdGxlIiwKICAgICAgInBsYWNlaG9sZGVyIjogIiIsCiAgICAgICJkZXNjcmlwdGlvbiI6ICJTZWxlY3QgYSBNM1UgZ3JvdXAuIChDb3VudGVyKTxicj5DaGFuZ2luZyB0aGUgZ3JvdXAgdGl0bGUgaW4gdGhlIE0zVSBpbnZhbGlkYXRlcyB0aGUgZmlsdGVyLiIKICAgIH0sCiAgICAiaW5jbHVkZSI6IHsKICAgICAgInRpdGxlIjogIkluY2x1ZGUiLAogICAgICAicGxhY2Vob2xkZXIiOiAiRkhELFVIRCIsCiAgICAgICJkZXNjcmlwdGlvbiI6ICJDaGFubmVsIG5hbWUgbXVzdCBpbmNsdWRlLjxicj4oQ29tbWEgc2VwYXJhdGVkKSBDb21tYSBtZWFucyBvciIKICAgIH0sCiAgICAiZXhjbHVkZSI6IHsKICAgICAgInRpdGxlIjogIkV4Y2x1ZGUiLAogICAgICAicGxhY2Vob2xkZXIiOiAiRVMsSVQiLAogICAgICAiZGVzY3JpcHRpb24iOiAiQ2hhbm5lbCBuYW1lIG11c3Qgbm90IGNvbnRhaW4uPGJyPihDb21tYSBzZXBhcmF0ZWQpIENvbW1hIG1lYW5zIG9yIgogICAgfQoKICB9LAogICJwbGF5bGlzdCI6IHsKICAgICJ0YWJsZSI6IHsKICAgICAgInBsYXlsaXN0IjogIlBsYXlsaXN0IiwKICAgICAgInR1bmVyIjogIlR1bmVyIiwKICAgICAgImxhc3RVcGRhdGUiOiAiTGFzdCBVcGRhdGUiLAogICAgICAiYXZhaWxhYmlsaXR5IjogIkF2YWlsYWJpbGl0eSIsCiAgICAgICJ0eXBlIjogIlR5cGUiLAogICAgICAic3RyZWFtcyI6ICJTdHJlYW1zIiwKICAgICAgImdyb3VwVGl0bGUiOiAiZ3JvdXAtdGl0bGUiLAogICAgICAidHZnSUQiOiAidHZnLWlkIiwKICAgICAgInVuaXF1ZUlEIjogIlVuaXF1ZSBJRCIKICAgIH0sCiAgICAicGxheWxpc3RUeXBlIjogewogICAgICAidGl0bGUiOiAiUGxheWxpc3QgdHlwZSIsCiAgICAgICJwbGFjZWhvbGRlciI6ICIiLAogICAgICAiZGVzY3JpcHRpb24iOiAiIgogICAgfSwKICAgICJ0eXBlIjogewogICAgICAidGl0bGUiOiAiVHlwZSIsCiAgICAgICJwbGFjZWhvbGRlciI6ICIiLAogICAgICAiZGVzY3JpcHRpb24iOiAiIgogICAgfSwKICAgICJuYW1lIjogewogICAgICAidGl0bGUiOiAiTmFtZSIsCiAgICAgICJwbGFjZWhvbGRlciI6ICJQbGF5bGlzdCBuYW1lIiwKICAgICAgImRlc2NyaXB0aW9uIjogIiIKICAgIH0sCiAgICAiZGVzY3JpcHRpb24iOiB7CiAgICAgICJ0aXRsZSI6ICJEZXNjcmlwdGlvbiIsCiAgICAgICJwbGFjZWhvbGRlciI6ICJEZXNjcmlwdGlvbiIsCiAgICAgICJkZXNjcmlwdGlvbiI6ICIiCiAgICB9LAogICAgImZpbGVNM1UiOiB7CiAgICAgICJ0aXRsZSI6ICJNM1UgRmlsZSIsCiAgICAgICJwbGFjZWhvbGRlciI6ICJGaWxlIHBhdGggb3IgVVJMIG9mIHRoZSBNM1UiLAogICAgICAiZGVzY3JpcHRpb24iOiAiIgogICAgfSwKICAgICJmaWxlSERIUiI6IHsKICAgICAgInRpdGxlIjogIkhESG9tZVJ1biBJUCIsCiAgICAgICJwbGFjZWhvbGRlciI6ICJJUCBhZGRyZXNzIGFuZCBwb3J0ICgxOTIuMTY4LjEuMTA6NTAwNCkiLAogICAgICAiZGVzY3JpcHRpb24iOiAiIgogICAgfSwKICAgICJ0dW5lciI6IHsKICAgICAgInRpdGxlIjogIlR1bmVyIC8gU3RyZWFtcyIsCiAgICAgICJwbGFjZWhvbGRlciI6ICIiLAogICAgICAiZGVzY3JpcHRpb24iOiAiTnVtYmVyIG9mIHBhcmFsbGVsIGNvbm5lY3Rpb25zIHRoYXQgY2FuIGJlIGVzdGFibGlzaGVkIHRvIHRoZSBwcm92aWRlci4gPGJyPk9ubHkgYXZhaWxLine truncated
|
||||||
|
webUI["html/img/.DS_Store"] = "AAAAAUJ1ZDEAABAAAAAIAAAAEAAAAAAlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAABAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAgLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAACAAAAABAAAAQAAAAAEAAACAAAAAAQAAAQAAAAABAAACAAAAAAEAAAQAAAAAAAAAAAEAABAAAAAAAQAAIAAAAAABAABAAAAAAAEAAIAAAAAAAQABAAAAAAABAAIAAAAAAAEABAAAAAAAAQAIAAAAAAABABAAAAAAAAEAIAAAAAAAAQBAAAAAAAABAIAAAAAAAAEBAAAAAAAAAQIAAAAAAAABBAAAAAAAAAEIAAAAAAAAARAAAAAAAAABIAAAAAAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALine truncated
|
||||||
|
webUI["html/img/filter.png"] = "iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAAXNSR0IArs4c6QAAAAlwSFlzAAAsSwAALEsBpT2WqQAABCRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IlhNUCBDb3JlIDUuNC4wIj4KICAgPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgICAgICAgICAgIHhtbG5zOmV4aWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vZXhpZi8xLjAvIgogICAgICAgICAgICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgICAgICAgICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyI+CiAgICAgICAgIDx0aWZmOlJlc29sdXRpb25Vbml0PjI8L3RpZmY6UmVzb2x1dGlvblVuaXQ+CiAgICAgICAgIDx0aWZmOkNvbXByZXNzaW9uPjU8L3RpZmY6Q29tcHJlc3Npb24+CiAgICAgICAgIDx0aWZmOlhSZXNvbHV0aW9uPjI4ODwvdGlmZjpYUmVzb2x1dGlvbj4KICAgICAgICAgPHRpZmY6T3JpZW50YXRpb24+MTwvdGlmZjpPcmllbnRhdGlvbj4KICAgICAgICAgPHRpZmY6WVJlc29sdXRpb24+Mjg4PC90aWZmOllSZXNvbHV0aW9uPgogICAgICAgICA8ZXhpZjpQaXhlbFhEaW1lbnNpb24+NTA8L2V4aWY6UGl4ZWxYRGltZW5zaW9uPgogICAgICAgICA8ZXhpZjpDb2xvclNwYWNlPjE8L2V4aWY6Q29sb3JTcGFjZT4KICAgICAgICAgPGV4aWY6UGl4ZWxZRGltZW5zaW9uPjUwPC9leGlmOlBpeGVsWURpbWVuc2lvbj4KICAgICAgICAgPGRjOnN1YmplY3Q+CiAgICAgICAgICAgIDxyZGY6QmFnLz4KICAgICAgICAgPC9kYzpzdWJqZWN0PgogICAgICAgICA8eG1wOk1vZGlmeURhdGU+MjAxOC0wNy0yOFQxOTowNzo2OTwveG1wOk1vZGlmeURhdGU+CiAgICAgICAgIDx4bXA6Q3JlYXRvclRvb2w+UGl4ZWxtYXRvciAzLjM8L3htcDpDcmVhdG9yVG9vbD4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+Cs038OQAAAOISURBVGgF5ZpLSBVRGMfvvfmqCKKiKCqKtE3SQ8haJJm1ctPCZYHrFhXtohcUBLVsU1BRBJKLXpsSKs1aCL1oUUhvMgIloodZKmLZ72+OzNVx7jzOXGfqg/89Z875zv/7f35n7px7vanUP2JptzyGhoYKmK8GW0EZmA/mgW7wHrwDN0BTOp0epPVtxBBnLdgIVoE5YBboBR2gDTTA/5DWn0FeDPaBz8CLdeF0EBR5jYRvKbgABoAXa8Opwit/CudK8NoLs4PPM8ZyBsNnB/jpsD7X0C8cjgDXnaQkqkEPCGMSuGWivxxzx8OQj6xtpHWuPhPloHfEMWzTD0HN2GQY2xuW2Lb+/Fh+VaIQPLE5meh+gmSxFYz+BjBogtjGscfiH26Z2GmbNNltVQAIpwDdP6ZNO2iRYqTpZGhfgWUaiMC2w1kCzkbALcqLvDVvUyK6MW9HFES0b8BvsFwXEZi4F+iBtykCcjtlqf0igr52VJ1eqiIgzzdlrRIZfWfJd3SD8ZbqHvkB4XSDpJNB9V0VcX/cT4Ys/zEzSuSj/3WxW9GpRD7ETpZ/QcOJ6LyfdHugitxNehbob9G7lo4PXWBmQhPqQ/fsDOeUfjqNCU1Csi+TQ5+2luzc3yaRr6elevQZwhZr4bomYam0U41yabYqov5hvSTMjlp6RyuiAarSTLPZmox5246+lVREx/isiuh6NxhUJwF2yEpCWu1bK8WEsjyRgCSa0XrVrjNra2mC7TWD5ilYAuJoA4jSlnppF5dVEU3g0ENTD4b3nsZiZsfGJuGqj8qY+CINGqP2GLZCJ+HjtpblxAJ9k3cPrLfGJrnVUaSCarxw0jFua1lOLNBerANx+byya6IkLM2uLZWpAl6/Mcc1EjvjKtLrJNLqI5HnjfQ+bsVeteb0g2y/t7hGvd7CNjenOL8OkJ40KtOdTF+Cl/nV6Mkf4gxocI9vZPYLLKs9iQrqRIACcM2IXGeSboYrg+rztY5ARaDJWUeo0W+sXudLTFhnApaAW6FkZy/+yuXasLoCrSfwVHAnW0+gK93YawKJMLUIAdNAKwhqnSxcYUpPKB6EBE2mg7VR///EX24jybTQerXnOC70FyVP3gjTPXPTQyaP8NFPNeJrCNTPP667JKOq6VNo/A2hes5ccUjmEmPmDoD5+FMgWCcA+3FG57QJP//kQ1PgGBIOToEDgUn+t4V/AJeGknwARIKLAAAAAElFTkSuQmCC"
|
||||||
|
webUI["html/js/authentication_ts.js"] = "ZnVuY3Rpb24gbG9naW4oKSB7CiAgICB2YXIgZXJyID0gZmFsc2U7CiAgICB2YXIgZGF0YSA9IG5ldyBPYmplY3QoKTsKICAgIHZhciBkaXYgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgiY29udGVudCIpOwogICAgdmFyIGZvcm0gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgiYXV0aGVudGljYXRpb24iKTsKICAgIHZhciBpbnB1dHMgPSBkaXYuZ2V0RWxlbWVudHNCeVRhZ05hbWUoIklOUFVUIik7CiAgICBjb25zb2xlLmxvZyhpbnB1dHMpOwogICAgZm9yICh2YXIgaSA9IGlucHV0cy5sZW5ndGggLSAxOyBpID49IDA7IGktLSkgewogICAgICAgIHZhciBrZXkgPSBpbnB1dHNbaV0ubmFtZTsKICAgICAgICB2YXIgdmFsdWUgPSBpbnB1dHNbaV0udmFsdWU7CiAgICAgICAgaWYgKHZhbHVlLmxlbmd0aCA9PSAwKSB7CiAgICAgICAgICAgIGlucHV0c1tpXS5zdHlsZS5ib3JkZXJDb2xvciA9ICJyZWQiOwogICAgICAgICAgICBlcnIgPSB0cnVlOwogICAgICAgIH0KICAgICAgICBkYXRhW2tleV0gPSB2YWx1ZTsKICAgIH0KICAgIGlmIChlcnIgPT0gdHJ1ZSkgewogICAgICAgIGRhdGEgPSBuZXcgT2JqZWN0KCk7CiAgICAgICAgcmV0dXJuOwogICAgfQogICAgaWYgKGRhdGEuaGFzT3duUHJvcGVydHkoImNvbmZpcm0iKSkgewogICAgICAgIGlmIChkYXRhWyJjb25maXJtIl0gIT0gZGF0YVsicGFzc3dvcmQiXSkgewogICAgICAgICAgICBhbGVydCgic2RhZnNkIik7CiAgICAgICAgICAgIGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdwYXNzd29yZCcpLnN0eWxlLmJvcmRlckNvbG9yID0gInJlZCI7CiAgICAgICAgICAgIGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdjb25maXJtJykuc3R5bGUuYm9yZGVyQ29sb3IgPSAicmVkIjsKICAgICAgICAgICAgZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoImVyciIpLmlubmVySFRNTCA9ICJ7ey5hY2NvdW50LmZhaWxlZH19IjsKICAgICAgICAgICAgcmV0dXJuOwogICAgICAgIH0KICAgIH0KICAgIGNvbnNvbGUubG9nKGRhdGEpOwogICAgZm9ybS5zdWJtaXQoKTsKfQo="
|
||||||
|
webUI["html/js/configuration_ts.js"] = "dmFyIF9fZXh0ZW5kcyA9ICh0aGlzICYmIHRoaXMuX19leHRlbmRzKSB8fCAoZnVuY3Rpb24gKCkgewogICAgdmFyIGV4dGVuZFN0YXRpY3MgPSBmdW5jdGlvbiAoZCwgYikgewogICAgICAgIGV4dGVuZFN0YXRpY3MgPSBPYmplY3Quc2V0UHJvdG90eXBlT2YgfHwKICAgICAgICAgICAgKHsgX19wcm90b19fOiBbXSB9IGluc3RhbmNlb2YgQXJyYXkgJiYgZnVuY3Rpb24gKGQsIGIpIHsgZC5fX3Byb3RvX18gPSBiOyB9KSB8fAogICAgICAgICAgICBmdW5jdGlvbiAoZCwgYikgeyBmb3IgKHZhciBwIGluIGIpIGlmIChiLmhhc093blByb3BlcnR5KHApKSBkW3BdID0gYltwXTsgfTsKICAgICAgICByZXR1cm4gZXh0ZW5kU3RhdGljcyhkLCBiKTsKICAgIH07CiAgICByZXR1cm4gZnVuY3Rpb24gKGQsIGIpIHsKICAgICAgICBleHRlbmRTdGF0aWNzKGQsIGIpOwogICAgICAgIGZ1bmN0aW9uIF9fKCkgeyB0aGlzLmNvbnN0cnVjdG9yID0gZDsgfQogICAgICAgIGQucHJvdG90eXBlID0gYiA9PT0gbnVsbCA/IE9iamVjdC5jcmVhdGUoYikgOiAoX18ucHJvdG90eXBlID0gYi5wcm90b3R5cGUsIG5ldyBfXygpKTsKICAgIH07Cn0pKCk7CnZhciBXaXphcmRDYXRlZ29yeSA9IC8qKiBAY2xhc3MgKi8gKGZ1bmN0aW9uICgpIHsKICAgIGZ1bmN0aW9uIFdpemFyZENhdGVnb3J5KCkgewogICAgICAgIHRoaXMuRG9jdW1lbnRJRCA9ICJjb250ZW50IjsKICAgIH0KICAgIFdpemFyZENhdGVnb3J5LnByb3RvdHlwZS5jcmVhdGVDYXRlZ29yeUhlYWRsaW5lID0gZnVuY3Rpb24gKHZhbHVlKSB7CiAgICAgICAgdmFyIGVsZW1lbnQgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCJINCIpOwogICAgICAgIGVsZW1lbnQuaW5uZXJIVE1MID0gdmFsdWU7CiAgICAgICAgcmV0dXJuIGVsZW1lbnQ7CiAgICB9OwogICAgcmV0dXJuIFdpemFyZENhdGVnb3J5Owp9KCkpOwp2YXIgV2l6YXJkSXRlbSA9IC8qKiBAY2xhc3MgKi8gKGZ1bmN0aW9uIChfc3VwZXIpIHsKICAgIF9fZXh0ZW5kcyhXaXphcmRJdGVtLCBfc3VwZXIpOwogICAgZnVuY3Rpb24gV2l6YXJkSXRlbShrZXksIGhlYWRsaW5lKSB7CiAgICAgICAgdmFyIF90aGlzID0gX3N1cGVyLmNhbGwodGhpcykgfHwgdGhpczsKICAgICAgICBfdGhpcy5oZWFkbGluZSA9IGhlYWRsaW5lOwogICAgICAgIF90aGlzLmtleSA9IGtleTsKICAgICAgICByZXR1cm4gX3RoaXM7CiAgICB9CiAgICBXaXphcmRJdGVtLnByb3RvdHlwZS5jcmVhdGVXaXphcmQgPSBmdW5jdGlvbiAoKSB7CiAgICAgICAgdmFyIGhlYWRsaW5lID0gdGhpcy5jcmVhdGVDYXRlZ29yeUhlYWRsaW5lKHRoaXMuaGVhZGxpbmUpOwogICAgICAgIHZhciBrZXkgPSB0aGlzLmtleTsKICAgICAgICB2YXIgY29udGVudCA9IG5ldyBQb3B1cENvbnRlbnQoKTsKICAgICAgICB2YXIgZGVzY3JpcHRpb247CiAgICAgICAgdmFyIGRvYyA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKHRoaXMuRG9jdW1lbnRJRCk7CiAgICAgICAgZG9jLmlubmVySFRNTCA9ICIiOwogICAgICAgIGRvYy5hcHBlbmRDaGlsZChoZWFkbGluZSk7CiAgICAgICAgc3dpdGNoIChrZXkpIHsKICAgICAgICAgICAgY2FzZSAidHVuZXIiOgogICAgICAgICAgICAgICAgdmFyIHRleHQgPSBuZXcgQXJyYXkoKTsKICAgICAgICAgICAgICAgIHZhciB2YWx1ZXMgPSBuZXcgQXJyYXkoKTsKICAgICAgICAgICAgICAgIGZvciAodmFyIGkgPSAxOyBpIDw9IDEwMDsgaSsrKSB7CiAgICAgICAgICAgICAgICAgICAgdGV4dC5wdXNoKGkpOwogICAgICAgICAgICAgICAgICAgIHZhbHVlcy5wdXNoKGkpOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgdmFyIHNlbGVjdCA9IGNvbnRlbnQuY3JlYXRlU2VsZWN0KHRleHQsIHZhbHVlcywgIjEiLCBrZXkpOwogICAgICAgICAgICAgICAgc2VsZWN0LnNldEF0dHJpYnV0ZSgiY2xhc3MiLCAid2l6YXJkIik7CiAgICAgICAgICAgICAgICBzZWxlY3QuaWQgPSBrZXk7CiAgICAgICAgICAgICAgICBkb2MuYXBwZW5kQ2hpbGQoc2VsZWN0KTsKICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uID0gInt7LndpemFyZC50dW5lci5kZXNjcmlwdGlvbn19IjsKICAgICAgICAgICAgICAgIGJyZWFrOwogICAgICAgICAgICBjYXNlICJlcGdTb3VyY2UiOgogICAgICAgICAgICAgICAgdmFyIHRleHQgPSBbIlBNUyIsICJYRVBHIl07CiAgICAgICAgICAgICAgICB2YXIgdmFsdWVzID0gWyJQTVMiLCAiWEVQRyJdOwogICAgICAgICAgICAgICAgdmFyIHNlbGVjdCA9IGNvbnRlbnQuY3JlYXRlU2VsZWN0KHRleHQsIHZhbHVlcywgIlhFUEciLCBrZXkpOwogICAgICAgICAgICAgICAgc2VsZWN0LnNldEF0dHJpYnV0ZSgiY2xhc3MiLCAid2l6YXJkIik7CiAgICAgICAgICAgICAgICBzZWxlY3QuaWQgPSBrZXk7CiAgICAgICAgICAgICAgICBkb2MuYXBwZW5kQ2hpbGQoc2VsZWN0KTsKICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uID0gInt7LndpemFyZC5lcGdTb3VyY2UuZGVzY3JpcHRpb259fSI7CiAgICAgICAgICAgICAgICBicmVhazsKICAgICAgICAgICAgY2FzZSAibTN1IjoKICAgICAgICAgICAgICAgIHZhciBpbnB1dCA9IGNvbnRlbnQuY3JlYXRlSW5wdXQoInRleHQiLCBrZXksICIiKTsKICAgICAgICAgICAgICAgIGlucHV0LnNldEF0dHJpYnV0ZSgiY2xhc3MiLCAid2l6YXJkIik7CiAgICAgICAgICAgICAgICBpbnB1dC5pZCA9IGtleTsKICAgICAgICAgICAgICAgIGRvYy5hcHBlbmRDaGlsZChpbnB1dCk7CiAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbiA9ICJ7ey53aXphcmQubTN1LmRlc2NyaXB0aW9ufX0iOwogICAgICAgICAgICAgICAgYnJlYWs7CiAgICAgICAgICAgIGNhc2UgInhtbHR2IjoKICAgICAgICAgICAgICAgIHZhciBpbnB1dCA9IGNvbnRlbnQuY3JlYXRlSW5wdXQoInRleHQiLCBrZXksICIiKTsKICAgICAgICAgICAgICAgIGlucHV0LnNldEF0dHJpYnV0ZSgiY2xhc3MiLCAid2l6YXJkIik7CiAgICAgICAgICAgICAgICBpbnB1dC5pZCA9IGtleTsKICAgICAgICAgICAgICAgIGRvYy5hcHBlbmRDaGlsZChpbnB1dCk7CiAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbiA9ICJ7ey53aXphcmQueG1sdHYuZGVzY3JpcHRpb259fSI7CiAgICAgICAgICAgICAgICBicmVhazsKICAgICAgICAgICAgZGVmYXVsdDoKICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKGtleSk7CiAgICAgICAgICAgICAgICBicmVhazsKICAgICAgICB9CiAgICAgICAgdmFyIHByZSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoIlBSRSIpOwogICAgICAgIHByZS5pbm5lckhUTUwgPSBkZXNjcmlwdGlvbjsKICAgICAgICBkb2MuYXBwZW5kQ2hpbGQocHJlKTsKICAgICAgICBjb25zb2xlLmxvZyhoZWFkbGluZSwga2V5KTsKICAgIH07CiAgICByZXR1cm4gV2l6YXJkSXRlbTsKfShXaXphcmRDYXRlZ29yeSkpOwpmdW5jdGlvbiByZWFkeUZvckNvbmZpZ3VyYXRpb24od2l6YXJkKSB7CiAgICB2YXIgc2VydmVyID0gbmV3IFNlcnZlcigiZ2V0U2VydmVyQ29uZmlnIik7CiAgICBzZXJ2ZXIucmVxdWVzdChuZXcgT2JqZWN0KCkpOwogICAgc2hvd0VsZW1lbnQoImxvYWRpbmciLCBmYWxzZSk7CiAgICBjb25maWd1cmF0aW9uV2l6YXJkW3dpemFyZF0uY3JlYXRlV2l6YXJkKCk7Cn0KZnVuY3Rpb24gc2F2ZVdpemFyZCgpIHsKICAgIHZhciBjbWQgPSAic2F2ZVdpemFyZCI7CiAgICB2YXIgZGl2ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoImNvbnRlbnQiKLine truncated
|
||||||
|
webUI["html/js/log.js"] = "dmFyIGxvZ0ludGVydmFsCgoKZnVuY3Rpb24gdXBkYXRlTG9nKCkgewogIHZhciBkYXRhID0gbmV3IE9iamVjdCgpOwogIGRhdGFbImNtZCJdID0gImdldExvZyI7CiAgeFRlVmUoZGF0YSk7CiAgd3JpdGVMb2dJbkRpdigpOwogIHJldHVybgp9CgpmdW5jdGlvbiB3cml0ZUxvZ0luRGl2KCkgewogIHZhciBsb2dzID0gbG9nWyJsb2ciXTsKICB2YXIgZGl2ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoInNldHRpbmdzIikubGFzdENoaWxkLmxhc3RDaGlsZDsKICBkaXYuaW5uZXJIVE1MID0gIiI7CgogIHZhciBtYXggPSA1MDsKICAKICAKICBmb3IgKHZhciBpID0gMDsgaSA8IGxvZ3MubGVuZ3RoOyBpKyspIHsKICAgIHZhciBuZXdFbnRyeSA9IG5ldyBPYmplY3QoKTsKICAgIG5ld0VudHJ5WyJfZWxlbWVudCJdICA9ICJQIjsKCiAgICBpZiAobG9nc1tpXS5pbmNsdWRlcygiRVJST1IiKSkgewovLyAgICAgIGNhc2UgIndhcm5pbmdzIjogIG1zZ1R5cGUgPSAid2FybmluZ01zZyI7IGJyZWFrOwogICAgICBuZXdFbnRyeVsiY2xhc3MiXSAgID0gImVycm9yTXNnIjsKICAgIH0KCiAgICBpZiAobG9nc1tpXS5pbmNsdWRlcygiV0FSTklORyIpKSB7Ci8vICAgICAgY2FzZSAid2FybmluZ3MiOiAgbXNnVHlwZSA9ICJ3YXJuaW5nTXNnIjsgYnJlYWs7CiAgICAgIG5ld0VudHJ5WyJjbGFzcyJdICAgPSAid2FybmluZ01zZyI7CiAgICB9CgogICAgbmV3RW50cnlbIl90ZXh0Il0gICAgID0gbG9nc1tpXTsKICAgIAogICAgZGl2LmFwcGVuZENoaWxkKGNyZWF0ZUVsZW1lbnQobmV3RW50cnkpKTsKICB9CgogIGNhbGN1bGF0ZVdyYXBwZXJIZWlnaHQoKTsKICB2YXIgc2Nyb2xsRGl2ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoImJveC13cmFwcGVyIik7CiAgc2Nyb2xsRGl2LnNjcm9sbFRvcCA9IHNjcm9sbERpdi5zY3JvbGxIZWlnaHQ7Cn0KCmZ1bmN0aW9uIHNob3dMb2cob2JqKSB7CiAgLy9sb2dJbnRlcnZhbCA9IHNldEludGVydmFsKHVwZGF0ZUxvZywgNTAwMCk7CgogIHZhciBsb2dzID0gbG9nWyJsb2ciXTsKCiAgdmFyIGRpdiA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJzZXR0aW5ncyIpOwoKICB2YXIgbmV3RW50cnkgPSBuZXcgT2JqZWN0KCk7CiAgbmV3RW50cnlbIl9lbGVtZW50Il0gID0gIkhSIjsKICBkaXYuYXBwZW5kQ2hpbGQoY3JlYXRlRWxlbWVudChuZXdFbnRyeSkpOwogIC8vZGl2ID0gZGl2Lmxhc3RDaGlsZDsKCiAgdmFyIG5ld0VudHJ5ID0gbmV3IE9iamVjdCgpOwogIG5ld0VudHJ5WyJfZWxlbWVudCJdICAgID0gIklOUFVUIjsKICBuZXdFbnRyeVsidHlwZSJdICAgICAgICA9ICJidXR0b24iOwogIG5ld0VudHJ5WyJjbGFzcyJdICAgICAgID0gImJ1dHRvbiI7CiAgbmV3RW50cnlbInZhbHVlIl0gICAgICAgPSAiRW1wdHkgTG9nIjsKICBuZXdFbnRyeVsib25jbGljayJdICAgICA9ICJlbXB0eUxvZygpIjsKICBkaXYuYXBwZW5kQ2hpbGQoY3JlYXRlRWxlbWVudChuZXdFbnRyeSkpOwoKICB2YXIgbmV3RW50cnkgPSBuZXcgT2JqZWN0KCk7CiAgbmV3RW50cnlbIl9lbGVtZW50Il0gICAgPSAiY29kZSI7CiAgbmV3RW50cnlbIl90ZXh0Il0gICAgICAgID0gIlVwZGF0ZSBMb2c6ICI7CiAgZGl2LmFwcGVuZENoaWxkKGNyZWF0ZUVsZW1lbnQobmV3RW50cnkpKTsKCiAgdmFyIG5ld0VudHJ5ID0gbmV3IE9iamVjdCgpOwogIG5ld0VudHJ5WyJfZWxlbWVudCJdICAgID0gIklOUFVUIjsKICBuZXdFbnRyeVsidHlwZSJdICAgICAgICA9ICJjaGVja2JveCI7CiAgLy9uZXdFbnRyeVsiY2hlY2tlZCJdICAgICA9ICJjaGVja2JveCI7CiAgbmV3RW50cnlbIm9uY2xpY2siXSAgICAgPSAibG9nVXBkYXRlcyh0aGlzKSI7CiAgZGl2LmFwcGVuZENoaWxkKGNyZWF0ZUVsZW1lbnQobmV3RW50cnkpKTsKCiAgdmFyIG5ld0VudHJ5ID0gbmV3IE9iamVjdCgpOwogIG5ld0VudHJ5WyJfZWxlbWVudCJdICA9ICJIUiI7CiAgZGl2LmFwcGVuZENoaWxkKGNyZWF0ZUVsZW1lbnQobmV3RW50cnkpKTsKCiAgCgoKICAKICB2YXIgbmV3V3JhcHBlciA9IG5ldyBPYmplY3QoKTsKICBuZXdXcmFwcGVyWyJfZWxlbWVudCJdICA9ICJESVYiOwogIG5ld1dyYXBwZXJbImlkIl0gICAgICAgID0gImJveC13cmFwcGVyIjsKICBkaXYuYXBwZW5kQ2hpbGQoY3JlYXRlRWxlbWVudChuZXdXcmFwcGVyKSk7CiAgZGl2ID0gZGl2Lmxhc3RDaGlsZDsKCiAgdmFyIG5ld1ByZSA9IG5ldyBPYmplY3QoKTsKICBuZXdQcmVbIl9lbGVtZW50Il0gID0gIlBSRSI7CiAgbmV3UHJlWyJpZCJdICAgICAgICA9ICJsb2dTY3JlZW4iOwogIGRpdi5hcHBlbmRDaGlsZChjcmVhdGVFbGVtZW50KG5ld1ByZSkpOwoKICBkaXYgPSBkaXYubGFzdENoaWxkOwoKICB3cml0ZUxvZ0luRGl2KCkKICByZXR1cm4KfQoKZnVuY3Rpb24gZW1wdHlMb2coKSB7CiAgdmFyIGRhdGEgPSBuZXcgT2JqZWN0KCk7CiAgZGF0YVsiY21kIl0gPSAiZW1wdHlMb2ciOwogIHhUZVZlKGRhdGEpOwogIHJldHVybgp9CgpmdW5jdGlvbiBsb2dVcGRhdGVzKGVsbSkgewogIHN3aXRjaChlbG0uY2hlY2tlZCkgewogICAgY2FzZSBmYWxzZTogY2xlYXJJbnRlcnZhbChsb2dJbnRlcnZhbCk7IGJyZWFrOwogICAgY2FzZSB0cnVlOiBsb2dJbnRlcnZhbCA9IHNldEludGVydmFsKHVwZGF0ZUxvZywgNTAwMCk7IGJyZWFrOwogICAgCiAgfQp9"
|
||||||
|
webUI["html/js/logs_ts.js"] = "dmFyIExvZyA9IC8qKiBAY2xhc3MgKi8gKGZ1bmN0aW9uICgpIHsKICAgIGZ1bmN0aW9uIExvZygpIHsKICAgIH0KICAgIExvZy5wcm90b3R5cGUuY3JlYXRlTG9nID0gZnVuY3Rpb24gKGVudHJ5KSB7CiAgICAgICAgdmFyIGVsZW1lbnQgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCJQUkUiKTsKICAgICAgICBpZiAoZW50cnkuaW5kZXhPZigiV0FSTklORyIpICE9IC0xKSB7CiAgICAgICAgICAgIGVsZW1lbnQuY2xhc3NOYW1lID0gIndhcm5pbmdNc2ciOwogICAgICAgIH0KICAgICAgICBpZiAoZW50cnkuaW5kZXhPZigiRVJST1IiKSAhPSAtMSkgewogICAgICAgICAgICBlbGVtZW50LmNsYXNzTmFtZSA9ICJlcnJvck1zZyI7CiAgICAgICAgfQogICAgICAgIGlmIChlbnRyeS5pbmRleE9mKCJERUJVRyIpICE9IC0xKSB7CiAgICAgICAgICAgIGVsZW1lbnQuY2xhc3NOYW1lID0gImRlYnVnTXNnIjsKICAgICAgICB9CiAgICAgICAgZWxlbWVudC5pbm5lckhUTUwgPSBlbnRyeTsKICAgICAgICByZXR1cm4gZWxlbWVudDsKICAgIH07CiAgICByZXR1cm4gTG9nOwp9KCkpOwpmdW5jdGlvbiBzaG93TG9ncyhib3R0b20pIHsKICAgIHZhciBsb2cgPSBuZXcgTG9nKCk7CiAgICB2YXIgbG9ncyA9IFNFUlZFUlsibG9nIl1bImxvZyJdOwogICAgdmFyIGRpdiA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJjb250ZW50X2xvZyIpOwogICAgZGl2LmlubmVySFRNTCA9ICIiOwogICAgdmFyIGtleXMgPSBnZXRPYmpLZXlzKGxvZ3MpOwogICAga2V5cy5mb3JFYWNoKGZ1bmN0aW9uIChsb2dJRCkgewogICAgICAgIHZhciBlbnRyeSA9IGxvZy5jcmVhdGVMb2cobG9nc1tsb2dJRF0pOwogICAgICAgIGRpdi5hcHBlbmQoZW50cnkpOwogICAgfSk7CiAgICBzZXRUaW1lb3V0KGZ1bmN0aW9uICgpIHsKICAgICAgICBpZiAoYm90dG9tID09IHRydWUpIHsKICAgICAgICAgICAgdmFyIHdyYXBwZXIgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgiYm94LXdyYXBwZXIiKTsKICAgICAgICAgICAgd3JhcHBlci5zY3JvbGxUb3AgPSB3cmFwcGVyLnNjcm9sbEhlaWdodDsKICAgICAgICB9CiAgICB9LCAxMCk7Cn0KZnVuY3Rpb24gcmVzZXRMb2dzKCkgewogICAgdmFyIGNtZCA9ICJyZXNldExvZ3MiOwogICAgdmFyIGRhdGEgPSBuZXcgT2JqZWN0KCk7CiAgICB2YXIgc2VydmVyID0gbmV3IFNlcnZlcihjbWQpOwogICAgc2VydmVyLnJlcXVlc3QoZGF0YSk7Cn0K"
|
||||||
|
webUI["html/css/base.css"] = "KiB7CiAgLXdlYmtpdC1hcHBlYXJhbmNlOiBub25lOwogIC1tb3otYXBwZWFyYW5jZTogbm9uZTsKICAtbXMtYXBwZWFyYW5jZTogbm9uZTsKICBmb250LWZhbWlseTogIkFyaWFsIiwgc2Fucy1zZXJpZjsKICBsZXR0ZXItc3BhY2luZzogMnB4OyAKfQoKLyoKOjotd2Via2l0LXNjcm9sbGJhciB7IAogICAgZGlzcGxheTogbm9uZTsgCn0KKi8KCjo6LXdlYmtpdC1zY3JvbGxiYXIgewogIHdpZHRoOiAxMnB4OwogIGhlaWdodDogMTJweDsKfQoKIAo6Oi13ZWJraXQtc2Nyb2xsYmFyLXRyYWNrIHsKICAtd2Via2l0LWJveC1zaGFkb3c6IGluc2V0IDAgMCA2cHggcmdiYSgwLDAsMCwwLjMpOyAKICBib3JkZXItcmFkaXVzOiA1cHg7CiAgICAKfQogCjo6LXdlYmtpdC1zY3JvbGxiYXItdGh1bWIgewogIGJvcmRlci1yYWRpdXM6IDVweDsKICAtd2Via2l0LWJveC1zaGFkb3c6IGluc2V0IDAgMCA2cHggcmdiYSgwLCAwLCAwLDAuNik7IAogIGJhY2tncm91bmQtY29sb3I6ICM0NDQ7Cn0KCjo6LXdlYmtpdC1zY3JvbGxiYXItdGh1bWI6aG92ZXIgewogIGJhY2tncm91bmQ6ICMzMzM7IAp9Cgo6Oi13ZWJraXQtc2Nyb2xsYmFyLWNvcm5lciB7IAogIGJhY2tncm91bmQ6IHRyYW5zcGFyZW50OyAKfQoKYSB7CiAgY29sb3I6ICMwMEU2RkY7Cn0KCmh0bWwsIGJvZHkgewogIGNvbG9yOiAjZmZmOwogIG1hcmdpbjogMHB4IGF1dG87CiAgaGVpZ2h0OiAxMDAlOwogIGZvbnQtc2l6ZTogMTRweDsKfQoKaDIgewogIGZvbnQtc2l6ZTogMjRweDsKICBsZXR0ZXItc3BhY2luZzogMnB4Owp9CgpoMyB7CiAgZm9udC1zaXplOiAyMnB4OwogIGxldHRlci1zcGFjaW5nOiAxcHg7Cn0KCmg0IHsKICBmb250LXNpemU6IDIwcHg7CiAgbGV0dGVyLXNwYWNpbmc6IDFweDsKICBsaW5lLWhlaWdodDogMS41ZW07Cgp9CgpoNSB7CiAgZm9udC1zaXplOiAxNnB4OwogIGxldHRlci1zcGFjaW5nOiAxcHg7CiAgbGluZS1oZWlnaHQ6IDEuMmVtOwogIG1hcmdpbjogMjVweCAwcHggMTBweCAwcHg7Cn0KCmhyIHsKICBib3JkZXI6IDA7CiAgaGVpZ2h0OiAxcHg7CiAgYmFja2dyb3VuZDogIzMzMzsKICBtYXJnaW46IDEwcHggMHB4Owp9CgpwIHsKICBtYXJnaW46IDJweDsKICBwYWRkaW5nOiAycHggNXB4Owp9CgpwcmUgewogIG1hcmdpbjogMHB4IDBweCA1cHggMHB4OwogIGZvbnQtc2l6ZTogMTJweDsKICBjb2xvcjogI2RkZDsKICBsZXR0ZXItc3BhY2luZzogMXB4OwogIHdoaXRlLXNwYWNlOiBwcmUtd3JhcDsKICBmb250LWZhbWlseTogbW9ub3NwYWNlOyAKICBmb250LXNpemU6IDEycHg7IAogIGZvbnQtc3R5bGU6IG5vcm1hbDsgCiAgZm9udC12YXJpYW50OiBub3JtYWw7IAogIGxpbmUtaGVpZ2h0OiAxLjZlbTsgCn0KCmxhYmVsIHsKICBtYXJnaW4tYm90dG9tOiAyMHB4OwogIGRpc3BsYXk6IGJsb2NrOwp9CgpsaSB7CiAgbGlzdC1zdHlsZS10eXBlOiBub25lOwogIGJhY2tncm91bmQtY29sb3I6ICMxMTE7CiAgcGFkZGluZzogMTBweCAyMHB4OwogIGN1cnNvcjogcG9pbnRlcjsKICBib3JkZXItbGVmdDogc29saWQgMnB4ICMxMTE7CiAgdHJhbnNpdGlvbjogYWxsIDAuMzsKfQoKbGk6aG92ZXIgewogIGJvcmRlci1jb2xvcjogIzAwRTZGRgp9CgpzZWxlY3QgewogIGN1cnNvcjogcG9pbnRlcjsKICB3aWR0aDogY2FsYygxMDAlICsgMnB4KTsKICBib3JkZXI6IHNvbGlkIDBweCAjMDBFNkZGOwogIGJvcmRlci1yYWRpdXM6IDBweDsKICBvdXRsaW5lOiBub25lOwogIGNvbG9yOiAjZmZmOwogIHBhZGRpbmc6IDlweCAxMHB4OwogIGRpc3BsYXk6YmxvY2s7CiAgYmFja2dyb3VuZC1jb2xvcjogIzMzMzsKICBmb250LXNpemU6IDE0cHg7CiAgbWFyZ2luOiA1cHggMHB4IDVweCAwcHg7Cn0KCnNlbGVjdDpmb2N1cyB7CiAgb3V0bGluZTogbm9uZTsKfQoKaW5wdXQgewogIC13ZWJraXQtYXBwZWFyYW5jZTogbm9uZTsKICBtYXJnaW46IDVweCAwcHg7CiAgcGFkZGluZzogMi41cHggMTBweDsKICBvdXRsaW5lOiBub25lOwogIGZvbnQtc2l6ZTogMTRweDsKfQoKaW5wdXRbdHlwZT1idXR0b25dLCBpbnB1dFt0eXBlPXN1Ym1pdF0gewogIGN1cnNvcjogcG9pbnRlcjsKICBiYWNrZ3JvdW5kLWNvbG9yOiAjMDAwOwogIG1hcmdpbjogMTBweCAxMHB4OwogIHBhZGRpbmc6IDEwcHggMjVweDsKICBib3JkZXI6IHNvbGlkIDBweDsKICBib3JkZXItY29sb3I6ICMwMDA7CiAgYm9yZGVyLXJhZGl1czogM3B4OwogIG91dGxpbmU6IG5vbmU7CiAgY29sb3I6ICNmZmY7Cn0KCmlucHV0W3R5cGU9YnV0dG9uXTpmb2N1cyB7IAogICBvdXRsaW5lOiBub25lOwp9CgppbnB1dFt0eXBlPWJ1dHRvbl06aG92ZXIgewogIGJhY2tncm91bmQtY29sb3I6ICMwMEU2RkY7CiAgY29sb3I6ICMwMDA7Cn0KCmlucHV0W3R5cGU9YnV0dG9uXTpob3Zlci5kZWxldGUgIHsKICBiYWNrZ3JvdW5kLWNvbG9yOiByZWQ7CiAgY29sb3I6ICNmZmY7Cn0KCmlucHV0W3R5cGU9dGV4dF0sIGlucHV0W3R5cGU9c2VhcmNoXSwgaW5wdXRbdHlwZT1wYXNzd29yZF0gewogIGNvbG9yOiAjZmZmOwogIHdpZHRoOiAtd2Via2l0LWNhbGMoMTAwJSAtIDBweCk7CiAgd2lkdGg6IC1tb3otY2FsYygxMDAlIC0gMHB4KTsKICB3aWR0aDogY2FsYygxMDAlIC0gMHB4KTsKICBvdXRsaW5lOiBub25lOwogIGJvcmRlcjogc29saWQgMXB4IHRyYW5zcGFyZW50OwogIGJhY2tncm91bmQtY29sb3I6IHRyYW5zcGFyZW50OwogIGJvcmRlci1ib3R0b20tY29sb3I6ICM1NTU7CiAgYm9yZGVyLXJhZGl1czogMHB4OwogIHBhZGRpbmc6IDhweCAxMHB4Owp9CgppbnB1dFt0eXBlPSJjaGVja2JveCJdIHsKICBib3JkZXI6IHNvbGlkIDFweCAjMDBFNkZGOwogIGJhY2tncm91bmQtY29sb3I6ICMzMzM7CiAgaGVpZ2h0OiAyNXB4OwogIHdpZHRoOiAyNXB4OwogIGN1cnNvcjogcG9pbnRlcjsKICAvKgogIC13ZWJraXQtYXBwZWFyYW5jZTogY2hlY2tib3g7CiAgKi8KfQoKaW5wdXRbdHlwZT0iY2hlY2tib3giXTpjaGVja2VkIHsKICBjb2xvcjogI2ZmZjsKICBiYWNrZ3JvdW5kLWNvbG9yOiAjMDBFNkZGOwogIC8qZGlzcGxheTogaW5saW5lLWJsb2NrOyovCn0KCmlucHV0W3R5cGU9ImNoZWNrYm94Il06YmVmb3JlIHsKICBwb3NpdGlvbjogaW5pdGlhbDsKICBsZWZ0OiAwcHg7CiAgbWFyZ2luLWxlZnQ6IC00cHg7CiAgY29udGVudDogIiAiOwp9CgppbnB1dFt0eXBlPSJjaGVja2JveCJdOmNoZWNrZWQ6YmVmb3JlIHsKICBwb3NpdGlvbjogaW5pdGlhbDsKICBsZWZ0OiAwcHg7CiAgbWFyZ2luLWxlZnQ6IC0zcHg7CiAgY29udGVudDogIuKckyI7CiAgY29sb3I6ICMwMDA7Cn0KCgppbnB1dFt0eXBlPWJ1dHRvbl0uY2FuY2VsIHsKICAKICBiYWNrZ3JvdW5kLWNvbG9yOiB0cmFuc3BhcmVudDsKICBib3JkZXItY29sb3I6IHJlZDsKfQoKaW5wdXRbdHlwZT1idXR0b25dLnNhdmV7CiAgYmFja2dyb3VuZC1jb2xvcjogIzExMTsKICBmbG9hdDogcmlnaHQ7Cn0KCgppbnB1dFt0eXBlPWJ1dHRvbl0uYmxhY2ssIGlucHV0W3R5cGU9c3VibWl0XS5ibGFja3sKICBiYWNrZ3JvdW5kLWNvbG9yOiAjMDAwOwogIGJvcmRlci1jb2xvcjogIzAwMDsKfQoKaW5wdXRbdHlwZT1idXR0b25dLmNlbnRlcnsKICBtYXJnaW4tcmlnaHQ6IGF1dG87CiAgbWFyZ2luLWxlZnQ6IGF1dG87CiAgYmFja2dyb3VuZC1jb2xvcjogIzAwMDsKICBib3JkZXItY29sb3I6ICMwMDA7Cn0KCi5wb2ludGVyIHsKICBjdXJzb3I6IHBvaW50ZXI7Cn0KCi5wb2ludGVyOmhvdmVyIHsKICBjb2xLine truncated
|
||||||
|
webUI["html/css/screen.css"] = "bmF2IGltZyB7CiAgZGlzcGxheTogYmxvY2s7CiAgbWF4LWhlaWdodDogMjBweDsKICBtYXgtd2lkdGg6IDIwcHg7CiAgZmxvYXQ6IGxlZnQ7Cn0KCm5hdiBwIHsKICB0ZXh0LWFsaWduOiBsZWZ0OwogIHBhZGRpbmc6IDBweCAzMHB4Owp9CgojbGF5b3V0IHsKICBkaXNwbGF5OiBibG9jazsKICBoZWlnaHQ6IDEwMCU7Cn0KCgoubGF5b3V0LWxlZnQgewogIGRpc3BsYXk6IGJsb2NrOwogIG1pbi13aWR0aDogMTUwcHg7CiAgbWF4LXdpZHRoOiAyMCU7CiAgYmFja2dyb3VuZC1jb2xvcjogIzExMTsKICBoZWlnaHQ6IGluaGVyaXQ7CiAgZmxvYXQ6IGxlZnQ7Cn0KCi5sYXlvdXQtcmlnaHQgewogIGRpc3BsYXk6IGJsb2NrOwogIGJhY2tncm91bmQtY29sb3I6ICM0NDQ7Cn0KCiNtZW51LXdyYXBwZXIgewogIGhlaWdodDogMTAwJTsKfQoKCiNsb2dvIHsKICBkaXNwbGF5OiBibG9jazsKICBtaW4td2lkdGg6IDE4MHB4OwogIHdpZHRoOiAxMDAlOwogIGhlaWdodDogMTAwcHg7CiAgYmFja2dyb3VuZDogdXJsKCIuLi9pbWcvbG9nb193XzYwMHgyMDAucG5nIik7CiAgYmFja2dyb3VuZC1yZXBlYXQ6IG5vLXJlcGVhdDsKICBiYWNrZ3JvdW5kLXBvc2l0aW9uOiBjZW50ZXI7IAogIGJhY2tncm91bmQtc2l6ZTogMTAwJTsKfQoKCiNwYWdlIHsKICBtYXgtd2lkdGg6IDk1MHB4OwogIG1hcmdpbjogYXV0bzsKICBiYWNrZ3JvdW5kLWNvbG9yOiAjNDQ0OwoKICAvKgogIGhlaWdodDogLXdlYmtpdC1jYWxjKDEwMCUgLSAxMzBweCk7CiAgaGVpZ2h0OiAtbW96LWNhbGMoMTAwJSAtIDEzMHB4KTsKICBoZWlnaHQ6IGNhbGMoMTAwJSAtIDEzMHB4KTsKICAqLwogIAogIG1pbi1oZWlnaHQ6IC13ZWJraXQtY2FsYygxMDAlIC0gMTIwcHgpOwogIG1pbi1oZWlnaHQ6IC1tb3otY2FsYygxMDAlIC0gMTIwcHgpOwogIG1pbi1oZWlnaHQ6IGNhbGMoMTAwJSAtIDEyMHB4KTsKICAKICAKICBib3gtc2hhZG93OiAwcHggNXB4IDVweCAjMjIyOwogIAp9CgojdWlTZXR0aW5nIHsKICBmbG9hdDogcmlnaHQ7CiAgbWFyZ2luLXJpZ2h0OiAyNXB4Owp9CgojYm94IGlucHV0W3R5cGU9dGV4dF0sICNib3ggaW5wdXRbdHlwZT1wYXNzd29yZF0gewogIHdpZHRoOiAtd2Via2l0LWNhbGMoMTAwJSAtIDIwcHgpOwogIHdpZHRoOiAtbW96LWNhbGMoMTAwJSAtIDIwcHgpOwogIHdpZHRoOiBjYWxjKDEwMCUgLSAyMHB4KTsKfQoKI2JveCBpbnB1dFt0eXBlPXN1Ym1pdF17CiAgbWFyZ2luOiA1MHB4IGF1dG87Cn0KCiNzZXR0aW5ncyB7CiAgZGlzcGxheTogYmxvY2s7CiAgcGFkZGluZzogMTBweCAxMHB4Owp9Cgojc2V0dGluZ3MgaDUgewogIG1hcmdpbjogNTBweCAwcHggMTBweCAwcHg7Cn0KCiNjb250ZW50LWludGVyYWN0aW9uIC5zZWFyY2ggewogIHdpZHRoOiAyMDBweDsKICBib3JkZXI6IDFweCBzb2xpZCAjMDAwOwogIHBhZGRpbmc6IDlweDsKICBiYWNrZ3JvdW5kLWNvbG9yOiAjMzMzOwogIG1hcmdpbjogMTBweDsKICBmbG9hdDogcmlnaHQ7CiAgYm9yZGVyLXJhZGl1czogM3B4OwoKfQoKI215U3RyZWFtcyB7CiAgcG9zaXRpb246IGZpeGVkOwogIGJvdHRvbTogMHB4OwogIGJhY2tncm91bmQtY29sb3I6ICMxMTE7CiAgd2lkdGg6IDEwMCU7CiAgbWF4LXdpZHRoOiA5NTBweDsKCiAgLyoKICBtYXgtaGVpZ2h0OiAxMDBweDsKICAqLwogIG1hcmdpbi1ib3R0b206IDBweDsKfQoKI215U3RyZWFtcyBpbWcgewogIHdpZHRoOiA0JTsKICBwYWRkaW5nOiAycHggNXB4OwogIGN1cnNvcjogcG9pbnRlcjsKICBmbG9hdDogcmlnaHQ7Cn0KCiNzZXR0aW5ncy1mb290ZXIgewoKfQoKCi8qIFdpemFyZCovCiNib3ggewogIGJhY2tncm91bmQtY29sb3I6ICM0NDQ7CiAgbWluLWhlaWdodDogNDAwcHg7CiAgCiAgZGlzcGxheTogZmxleDsKICBmbGV4LWRpcmVjdGlvbjogY29sdW1uOwogIGp1c3RpZnktY29udGVudDogc3BhY2UtYmV0d2VlbjsKfQoKI2JveCBwewogIHBhZGRpbmc6IDEwcHggMHB4Owp9CgojYm94LWZvb3RlciB7CiAgbWFyZ2luLXRvcDogYXV0bzsKfQoKI2JveC1mb290ZXIgewogIG1hcmdpbjogYXV0bzsKICBwYWRkaW5nOiAxMHB4Owp9CgojaGVhZGxpbmUgewogIGJhY2tncm91bmQtY29sb3I6ICMyMjI7CiAgYm9yZGVyLWJvdHRvbTogc29saWQgMnB4ICMyMjI7CiAgdHJhbnNpdGlvbjogYWxsIDAuNXM7CiAgcGFkZGluZzogMTBweCAwcHg7CiAgZGlzcGxheTogYmxvY2s7Cn0KCiNjb250ZW50IHsKICBkaXNwbGF5OiBibG9jazsKICBvdmVyZmxvdzogYXV0bzsKICBwYWRkaW5nOiAxMHB4Owp9CgovKiAtLS0gKi8KCgojY2xpZW50SW5mbywgI2FjdGl2ZVN0cmVhbXMsICNpbmFjdGl2ZVN0cmVhbXMgewogIGZvbnQtZmFtaWx5OiBtb25vc3BhY2U7CiAgZGlzcGxheTogYmxvY2s7CiAgZm9udC1zaXplOiA5cHg7CiAgYmFja2dyb3VuZC1jb2xvcjogIzExMTsKICBjb2xvcjogIzAwRTZGRjsKICBib3JkZXItYm90dG9tOiBzb2xpZCAwcHg7OwogIHBhZGRpbmc6IDBweDsKICBsZXR0ZXItc3BhY2luZzogMXB4OwogIG92ZXJmbG93LXg6IGhpZGRlbjsKICBib3JkZXItc3BhY2luZzogNHB4IDRweDsKICBib3JkZXItYm90dG9tOiBzb2xpZCAxcHggIzQ0NDsKfQoKI215U3RyZWFtc0JveCB7CiAgcG9zaXRpb246IHJlbGF0aXZlOwogIHBhZGRpbmc6IDBweDsKICAvKmhlaWdodDogMTAwcHg7Ki8KICBtYXgtaGVpZ2h0OiAxNTBweDsKICBiYWNrZ3JvdW5kLWNvbG9yOiAjMTExOwogIGNvbG9yOiB3aGl0ZTsKICBkaXNwbGF5OmZsZXg7CiAganVzdGlmeS1jb250ZW50OmNlbnRlcjsKICBhbGlnbi1pdGVtczpjZW50ZXI7Cn0KCiNvcGVuU3RyZWFtcyB7CiAgd2lkdGg6IDIwcHg7CiAgaGVpZ2h0OiAyMHB4OwogIGN1cnNvcjogcG9pbnRlcjsKICBmbG9hdDogcmlnaHQ7CiAgcG9zaXRpb246IGFic29sdXRlOwogIHJpZ2h0OiAwcHg7CiAgYm90dG9tOiAwcHg7CiAgYmFja2dyb3VuZDogdXJsKCIuLi9pbWcvdG91Y2gucG5nIik7CiAgYmFja2dyb3VuZC1jb2xvcjogIzExMTsKICAKICBiYWNrZ3JvdW5kLXBvc2l0aW9uOiBib3R0b20gcmlnaHQ7ICAKfQoKI2FsbFN0cmVhbXMgewogIHdpZHRoOiAxMDAlOwogIGhlaWdodDogMTAwJTsKICBwYWRkaW5nOiAycHg7ICAgICAKfQoKI2FjdGl2ZVN0cmVhbXMsICNpbmFjdGl2ZVN0cmVhbXMgewogIG92ZXJmbG93LXk6IHNjcm9sbDsKICB3aWR0aDogNTAlOwogIG1heC1oZWlnaHQ6IDEwMHB4OwogIGZsb2F0OiBsZWZ0Owp9CgojYWN0aXZlU3RyZWFtcyAudGRLZXksICNpbmFjdGl2ZVN0cmVhbXMgLnRkS2V5IHsKICB3aWR0aDogNzVweDsKfQoKCgoKI2luYWN0aXZlU3RyZWFtcyAudGRLZXkgewogIGNvbG9yOiByZWQ7Cn0KCiNjbGllbnRJbmZvIC50ZFZhbCwgI2xvZ0luZm8gLnRkVmFsLCAjYWN0aXZlU3RyZWFtcyAudGRWYWwsICNpbmFjdGl2ZVN0cmVhbXMgLnRkVmFsLCAjbWFwcGluZ0luZm8gLnRkVmFsewogIGNvbG9yOiAjYWFhOwogIHdoaXRlLXNwYWNlOiBpbmhlcml0Owp9CgojYm94LXdyYXBwZXIgewogIGRpc3BsYXk6IGlubGluZS1ibG9jazsKICB3aWR0aDogMTAwJTsKICAKICBvdmVyZmxvdy15OiBzY3JvbGw7Cn0KCiNjb250ZW50X3RhYmxlLCAjbWFwcGluZy1kZXRhaWwtdGFibGUsICNjb250ZW50X3RhYmxlIHsKICBkaXNwbGF5OiB0YWJsZTsKICAKICBib3JkZXItY29sbGFwc2U6IGNvbGxhcHNlOwogIG92ZXJmbG93LXk6IHNjcm9sbDsKICB3aWR0aDogMTAwJTsKfQoKCiNjb250ZW50X3RhYmxlIC5jb250ZW50X3RhYLine truncated
|
||||||
|
webUI["html/img/log.png"] = "iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAAXNSR0IArs4c6QAAAAlwSFlzAAAsSwAALEsBpT2WqQAABCRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IlhNUCBDb3JlIDUuNC4wIj4KICAgPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgICAgICAgICAgIHhtbG5zOmV4aWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vZXhpZi8xLjAvIgogICAgICAgICAgICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgICAgICAgICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyI+CiAgICAgICAgIDx0aWZmOlJlc29sdXRpb25Vbml0PjI8L3RpZmY6UmVzb2x1dGlvblVuaXQ+CiAgICAgICAgIDx0aWZmOkNvbXByZXNzaW9uPjU8L3RpZmY6Q29tcHJlc3Npb24+CiAgICAgICAgIDx0aWZmOlhSZXNvbHV0aW9uPjI4ODwvdGlmZjpYUmVzb2x1dGlvbj4KICAgICAgICAgPHRpZmY6T3JpZW50YXRpb24+MTwvdGlmZjpPcmllbnRhdGlvbj4KICAgICAgICAgPHRpZmY6WVJlc29sdXRpb24+Mjg4PC90aWZmOllSZXNvbHV0aW9uPgogICAgICAgICA8ZXhpZjpQaXhlbFhEaW1lbnNpb24+NTA8L2V4aWY6UGl4ZWxYRGltZW5zaW9uPgogICAgICAgICA8ZXhpZjpDb2xvclNwYWNlPjE8L2V4aWY6Q29sb3JTcGFjZT4KICAgICAgICAgPGV4aWY6UGl4ZWxZRGltZW5zaW9uPjUwPC9leGlmOlBpeGVsWURpbWVuc2lvbj4KICAgICAgICAgPGRjOnN1YmplY3Q+CiAgICAgICAgICAgIDxyZGY6QmFnLz4KICAgICAgICAgPC9kYzpzdWJqZWN0PgogICAgICAgICA8eG1wOk1vZGlmeURhdGU+MjAxOC0xMC0xNFQxMToxMDo0MjwveG1wOk1vZGlmeURhdGU+CiAgICAgICAgIDx4bXA6Q3JlYXRvclRvb2w+UGl4ZWxtYXRvciAzLjM8L3htcDpDcmVhdG9yVG9vbD4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+CkP32mEAAANASURBVGgF7ZlPiE1RHMffw1AYhiL5k1HEijJiYaFZiHoyFspW2RFRkkSZGgtFWJIwi9FYmo0VOywkKTbKgjSlWcifRCPX51dv6s2d33md77vvXjfmV9859/7u93x/v98959x73p1KRbAkSargAhgDH0E/qIYkuHYEvAPGvwbmNOEerXM/0Q6BRSFuZj/ix0DaDnvCkGppIucDAe4OhzvkcUO+GaELAf8ux7/b8Zlrr+Pvc3zm6nX8NccXdKmFLHaUPJ/Ruhyu5zNap8Nd6PiCLrUQTyi0Rjy/5/M0Zd+smB7M32XwtgBr07aC6yfSTs7XO77OALfH4VbgHsD/HLytVquJx4nyIdQFBsHftscksK5Z0sGhpuN8Or4Aa5sJFHjtO7G2MjKvvZjN1sglOpSlCMt9LrDZ4S4Hd0Qg24vrK+gAZbPtjMqTdFKhEdkIsYxFWP720JlioUK6pzDL41jtpRIqxOOWxecuB3fhRGT8E85YBK8VygI6GSRTCxlH3TaJgyw4O2678aCxO94LbNPovYDdmOrUukIBN/MqwjJEOwGPOHR31W4VONVCLEBR9lAJpBbSrYhn5K5R+quFnGUOr1ICtMIlxjz6XVb6qot9JeKvCPSAdlQJJHDtd8hOIN0wtRDLxx6Ntr0ulalTq1TJNyYzXUjj3SjD8X87Iva7uR8s5+2bi6FtX1oOgi8g2tSn1g2yPx+t3gIR/c90s1+C1vuO/YkxdWrdjxFtE2dE0VELWaqIZ+QuUfqrhZxmyKUvgEoyE1xizOR4YOI8plXXyAZEbYsyTJvXFsUW+x6wGUSbWogJ237rZHSEgojq1CooLT3MdCH6Pcu3xz8zIq0s9lvc2+sgz6dWH/rnQPB/jlybZGoh99hCHJqk0P6TD0jaI/4b7dVYeXVq2bemokyKpRZiHwWKMimWWshxhryjoEpOKXHUNbIN8WcUc5s2r8Vue7l9oAaiTS3EhDeB6EUYnUlGojq1MobLr3uokB/5hcys7OYWKuRl5nD5Cbi5uYXw0ntPHoay2S8Seuol5RZSJ0r/n/DEc/BdrN9kTZrH7BkwDspgwyQxW6uggU3nHnAXvAG/QZE2SrARsL8hJffwDxM0mNDPvT8IAAAAAElFTkSuQmCC"
|
||||||
|
webUI["html/img/settings.png"] = "iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAAXNSR0IArs4c6QAAAAlwSFlzAAAsSwAALEsBpT2WqQAABCRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IlhNUCBDb3JlIDUuNC4wIj4KICAgPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgICAgICAgICAgIHhtbG5zOmV4aWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vZXhpZi8xLjAvIgogICAgICAgICAgICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgICAgICAgICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyI+CiAgICAgICAgIDx0aWZmOlJlc29sdXRpb25Vbml0PjI8L3RpZmY6UmVzb2x1dGlvblVuaXQ+CiAgICAgICAgIDx0aWZmOkNvbXByZXNzaW9uPjU8L3RpZmY6Q29tcHJlc3Npb24+CiAgICAgICAgIDx0aWZmOlhSZXNvbHV0aW9uPjI4ODwvdGlmZjpYUmVzb2x1dGlvbj4KICAgICAgICAgPHRpZmY6T3JpZW50YXRpb24+MTwvdGlmZjpPcmllbnRhdGlvbj4KICAgICAgICAgPHRpZmY6WVJlc29sdXRpb24+Mjg4PC90aWZmOllSZXNvbHV0aW9uPgogICAgICAgICA8ZXhpZjpQaXhlbFhEaW1lbnNpb24+NTA8L2V4aWY6UGl4ZWxYRGltZW5zaW9uPgogICAgICAgICA8ZXhpZjpDb2xvclNwYWNlPjE8L2V4aWY6Q29sb3JTcGFjZT4KICAgICAgICAgPGV4aWY6UGl4ZWxZRGltZW5zaW9uPjUwPC9leGlmOlBpeGVsWURpbWVuc2lvbj4KICAgICAgICAgPGRjOnN1YmplY3Q+CiAgICAgICAgICAgIDxyZGY6QmFnLz4KICAgICAgICAgPC9kYzpzdWJqZWN0PgogICAgICAgICA8eG1wOk1vZGlmeURhdGU+MjAxOC0wOC0xMFQxODowODo4OTwveG1wOk1vZGlmeURhdGU+CiAgICAgICAgIDx4bXA6Q3JlYXRvclRvb2w+UGl4ZWxtYXRvciAzLjM8L3htcDpDcmVhdG9yVG9vbD4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+Ckxt87EAAAS7SURBVGgFzZpdiFVVGIbn5N+IMgVhaRLqWI7mQIJoIVRgOhQZNDAhWcbcVuPV3BRE6I14440iQQSaN0p6kz8zI0k5inUhZSSaYOVYODMo5eiMOOPf+LzH2cPMPmvttdbe53jOBy9rr2+938/ae51vr733qaoqgQwPD68B/wDJfXAP3AE94JMShCy+SxJ9CvSDJFlU7MiPFdsh/hrBdIffBsd48HApJjLPI4tnPThBlFJMZI5HBuWZCIt9AmgEzeAJR6JzHeMadnKI8wbYCz4Fkz18JlNwMgf8BCL5j4O3bVaMdUfEhPZqgv1U7HbEbDvoT7HZOPUYNwAlHheV09a4A3RTgMqtj9QY7JdgeM5i3I4+fDIYrQeq/UnyNYOTlBDtS+BwEjk2doz+KyO2OY5bwVCME+/qylTHT4C1D3k+uB33Yul3ov/eMuajlu1RH+II5wtT4hNNSnQ6U/kzbRkfq351bCfF8apAm7kmvq38njWRK0S305SHcSK5XO4U5O9MBmXWbSe3E6YccialdKzH2TTnQEF10XgZ5DdivsxEhkyxjVdERAwu03xmMiqD7hYx19km4cyHq6KyeHKkWpSz+ciVrHVpRYZk/wLHp0H2bULkNKzt4Eq86TKxLq3IECf6nXwV9QPbAfjHQSfoD7SN6L63gYhvbrkiTwKf/dPYpbdPVxKMnigdg0VAG8FQye8AzBl6aIk2A+wPiNoHd63LNZx3wbUAvz/CfTzJb/43AmkGpLfAi0DPE/NALQgtve+xFPdi5xRiNkHa5ySOJ/TR7RrBJdqL4AAxL+p+8ToYAFklNCnF3pM1KPba2L6v8vons5oPsko9ZyZoa0PsOoKezxoY+y5N5DYHWSvDTXzUMJH7IUkRW0tbyyV0CcfDDKmqDMe1KfqnQyehGNgo9q8p4hWYFGsiBY4fsSK/BRkkaPgj5PhMy720BnVFesfnlKo3Das0bw8XYJf196GEuzWRLSDoRypLg2w06FyqTS6Cx7iK1eeqGqrnugmuBgvB8+A5UAuqQYiU+oZ4h2T+BV3gAlDp/pai0U1rFpVGMAv8AHzFd4vShMOQLcoB+DNB/sSbM3ZoMV4BQsW0adSJSbNp1Hu1mY40/YZxpPdJaUSfFzrBMXAjjQNsdvll6cHCWVvKJIphdhcny11pqmolCk5aIDif0BKdZBucgPlO8kh/r8O4HtwClSCbk86HtQqQuUrvKVCf5OARjt0lll4H/WKKmbS0tmJQKZNQ7nq9+w0n2LhTN04E8jKMPpZ1hcli8tGTZYEYJwJrZQGzchTGz3a2ifwdkHc73LYAfpx6DcWXQK1LeiDsdpFGx7UOwUGQJKpmGyIjjvWh50iSQWzsZ/ofgvx+jnYp+B/YpJcB7QXDBKPJ4JDF6+/oCwoBuhoL36R+Jp4RJH16u2IgaxJpHhMehsBY3wTjV2YbOuuumLHrwCW6msbSj74O/DHGgV4Ohl8Jw1nSMmsB+tL6Wnw83odzBrhEr2GtgvFE8A74ADxtJZZygMC25cjQqBwqdg62qpUlziUP45Cq6OGuqqoUE/FJ8i+v7MpJYvEsHl1A5gNty2vLmaN3bBLdAvqB3svqXxLRvyEuc9zs7SiA+ACpw05pJx8SoAAAAABJRU5ErkJggg=="
|
||||||
|
webUI["html/js/authentication.js"] = "ZnVuY3Rpb24gY3JlYXRlRmlyc3RBY2NvdW50KGVsbSkgewogIHZhciBlcnIgPSBmYWxzZTsKICB2YXIgZGl2ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoZWxtKTsKICBjb25zb2xlLmxvZyhkaXYpOwoKICB2YXIgZm9ybSA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdhdXRoZW50aWNhdGlvbicpOwogIAogIGNvbnN0IHVzZXJuYW1lICA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCd1c2VybmFtZScpOwogIGNvbnN0IHBhc3N3b3JkICA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdwYXNzd29yZCcpOwogIGNvbnN0IGNvbmZpcm0gICA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdjb25maXJtJyk7CgogIHZhciBpbnB1dHMgPSBkaXYuZ2V0RWxlbWVudHNCeVRhZ05hbWUoJ0lOUFVUJykKICBjb25zb2xlLmxvZyhjb25maXJtKTsKCiAgc3dpdGNoKGNvbmZpcm0pIHsKICAgIGNhc2UgbnVsbDogYnJlYWs7CiAgICAKICAgIGRlZmF1bHQ6IAogICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGlucHV0cy5sZW5ndGg7IGkrKykgewogICAgICAgIGlmIChpbnB1dHNbaV0udmFsdWUubGVuZ3RoID09IDApIHsKICAgICAgICAgIGlucHV0c1tpXS5zdHlsZS5ib3JkZXJDb2xvciA9ICdyZWQnOwogICAgICAgICAgZXJyID0gdHJ1ZQogICAgICAgIH0KICAgICAgfQoKICAgICAgc3dpdGNoKGVycikgewogICAgICAgIGNhc2UgdHJ1ZTogcmV0dXJuOyBicmVhazsKICAgICAgICBjYXNlIGZhbHNlOiAKICAgICAgICAgIGlmIChwYXNzd29yZC52YWx1ZSAhPSBjb25maXJtLnZhbHVlKSB7CiAgICAgICAgICAgIGNvbmZpcm0uc3R5bGUuYm9yZGVyQ29sb3IgPSAncmVkJzsKICAgICAgICAgICAgcmV0dXJuOwogICAgICAgICAgfQogICAgICAgICAgYnJlYWs7CiAgICAgIH0KICB9CgoKICAKCiAgZm9ybS5zdWJtaXQoKTsKICByZXR1cm47Cn0="
|
||||||
|
webUI["html/js/data.js"] = "ZnVuY3Rpb24gc2hvd0NvbmZpZyhvYmopIHsKICBjb25maWcgPSBvYmo7CiAgLy9zZXRNZW51SXRlbSgpOwogIGNyZWF0ZU1lbnUoKTsKICAvL2RvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJwYWdlIikuY2xhc3NOYW1lID0gIiI7Cn0KCnNob3dNeVN0cmVhbXMKCmZ1bmN0aW9uIHNob3dNeVN0cmVhbXMoYWxsU3RyZWFtc09iaikgewoKICB2YXIgc3RyZWFtVHlwZUtleXMgPSBnZXRPYmpLZXlzKGFsbFN0cmVhbXNPYmopCgogIGZvciAodmFyIHMgPSAwOyBzIDwgc3RyZWFtVHlwZUtleXMubGVuZ3RoOyBzKyspIHsKICAgIHZhciBzdHJlYW1UeXBlID0gc3RyZWFtVHlwZUtleXNbc107CiAgICB2YXIgb2JqID0gbmV3IE9iamVjdCgpOwogICAgb2JqID0gYWxsU3RyZWFtc09ialtzdHJlYW1UeXBlXTsKICAgIHN3aXRjaChzdHJlYW1UeXBlKSB7CiAgICAgIGNhc2UgImFjdGl2ZVN0cmVhbXMiOiBhY3RpdmVTdHJlYW1zICAgICA9IG9iajsgYnJlYWs7CiAgICB9CgogICAgZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoc3RyZWFtVHlwZSkuaW5uZXJIVE1MID0gIiI7CgogICAgdmFyIHN0cmVhbXNPYmogICAgPSBuZXcgT2JqZWN0KCk7CiAgICB2YXIgc3RyZWFtc05hbWVzICA9IG5ldyBBcnJheSgpOwogICAgCiAgICB2YXIga2V5cyA9IGdldE9iaktleXMob2JqKQogICAgCiAgICAvLyBDcmVhdGUgT2JqZWN0IChzdHJlYW1zT2JqKSBmb3IgdGhlIHN0cmVhbXMgYW5kIHNvcnQgYnkgbmFtZSAoc3RyZWFtc05hbWVzKQogICAgZm9yICh2YXIgaSA9IDA7IGkgPCBrZXlzLmxlbmd0aDsgaSsrKSB7CiAgICAgIHZhciBuYW1lICAgID0gb2JqW2tleXNbaV1dWyJuYW1lIl07CiAgICAgIHZhciB0bXAgPSBuZXcgT2JqZWN0KCk7CiAgICAgIHZhciBzdHJlYW1LZXkgPSBnZXRPYmpLZXlzKG9ialtrZXlzW2ldXSk7CgogICAgICBmb3IgKHZhciBqID0gMDsgaiA8IHN0cmVhbUtleS5sZW5ndGg7IGorKykgewogICAgICAgIHRtcFtzdHJlYW1LZXlbal1dID0gb2JqW2tleXNbaV1dW3N0cmVhbUtleVtqXV07ICAgICAgCiAgICAgIH0KCiAgICAgIHN0cmVhbXNPYmpbbmFtZV0gPSB0bXA7CiAgICAgIHN0cmVhbXNOYW1lcy5wdXNoKG5hbWUpCiAgICB9CgogICAgc3RyZWFtc05hbWVzLnNvcnQoKTsKICAgIAogICAgLy8gQ3JlYXRlIFRhYmxlIGZvciBhY3RpdmVTdHJlYW1zCiAgICB2YXIgdGFibGUgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZChzdHJlYW1UeXBlKTsKCiAgICBmb3IgKHZhciBpID0gMDsgaSA8IHN0cmVhbXNOYW1lcy5sZW5ndGg7IGkrKykgewogICAgICB2YXIgbmV3RW50cnkgPSBuZXcgT2JqZWN0KCk7CiAgICAgIG5ld0VudHJ5WyJfZWxlbWVudCJdID0gIlRSIjsKICAgICAgdGFibGUuYXBwZW5kQ2hpbGQoY3JlYXRlRWxlbWVudChuZXdFbnRyeSkpOwogICAgICB2YXIgbGluZSA9IHRhYmxlLmxhc3RDaGlsZDsKCiAgICAgIHZhciB0bXAgPSBzdHJlYW1zT2JqW3N0cmVhbXNOYW1lc1tpXV0KICAgICAgdmFyIGtleXMgPSBnZXRPYmpLZXlzKHRtcCkKCiAgICAgIHZhciBuZXdLZXkgPSBuZXcgT2JqZWN0KCkKICAgICAgbmV3S2V5WyJfZWxlbWVudCJdICA9ICJURCI7CiAgICAgIC8vbmV3S2V5WyJfdGV4dCJdICAgICA9IHN0cmVhbXNOYW1lc1tpXTsKICAgICAgc3dpdGNoKHN0cmVhbVR5cGUpIHsKICAgICAgICBjYXNlICJhY3RpdmVTdHJlYW1zIjogbmV3S2V5WyJfdGV4dCJdICAgICA9ICJDaGFubmVsICgrKToiOyBicmVhazsKICAgICAgICBjYXNlICJpbmFjdGl2ZVN0cmVhbXMiOiBuZXdLZXlbIl90ZXh0Il0gICA9ICJDaGFubmVsICgtKToiOyBicmVhazsKICAgICAgfQogICAgICAKICAgICAgbmV3S2V5WyJjbGFzcyJdICAgICA9ICJ0ZEtleSI7CiAgICAgIGNvbnNvbGUubG9nKCk7CgoKICAgICAgdmFyIG5ld1ZhbCA9IG5ldyBPYmplY3QoKQogICAgICBuZXdWYWxbIl9lbGVtZW50Il0gID0gIlREIjsKICAgICAgbmV3VmFsWyJfdGV4dCJdICAgICA9IHN0cmVhbXNOYW1lc1tpXTsKICAgICAgbmV3VmFsWyJjbGFzcyJdICAgICA9ICJ0ZFZhbCI7CiAgICAgIC8vbmV3VmFsWyJfdGV4dCJdICAgICA9IHZhbHVlOwogICAgICAKICAgICAgbGluZS5hcHBlbmRDaGlsZChjcmVhdGVFbGVtZW50KG5ld0tleSkpOwogICAgICBsaW5lLmFwcGVuZENoaWxkKGNyZWF0ZUVsZW1lbnQobmV3VmFsKSk7CiAgICAgIAogICAgfQoKICB9CgogIHJldHVybgp9CgpmdW5jdGlvbiBzaG93QWN0aXZlU3RyZWFtcyhvYmopIHsKICBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgiYWN0aXZlU3RyZWFtcyIpLmlubmVySFRNTCA9ICIiOwogIGFjdGl2ZVN0cmVhbXMgICAgID0gb2JqOwogIHZhciBzdHJlYW1zT2JqICAgID0gbmV3IE9iamVjdCgpOwogIHZhciBzdHJlYW1zTmFtZXMgID0gbmV3IEFycmF5KCk7CiAgCiAgdmFyIGtleXMgPSBnZXRPYmpLZXlzKG9iaikKICAKICAvLyBDcmVhdGUgT2JqZWN0IChzdHJlYW1zT2JqKSBmb3IgdGhlIHN0cmVhbXMgYW5kIHNvcnQgYnkgbmFtZSAoc3RyZWFtc05hbWVzKQogIGZvciAodmFyIGkgPSAwOyBpIDwga2V5cy5sZW5ndGg7IGkrKykgewogICAgdmFyIG5hbWUgICAgPSBvYmpba2V5c1tpXV1bIm5hbWUiXTsKICAgIHZhciB0bXAgPSBuZXcgT2JqZWN0KCk7CiAgICB2YXIgc3RyZWFtS2V5ID0gZ2V0T2JqS2V5cyhvYmpba2V5c1tpXV0pOwoKICAgIGZvciAodmFyIGogPSAwOyBqIDwgc3RyZWFtS2V5Lmxlbmd0aDsgaisrKSB7CiAgICAgIHRtcFtzdHJlYW1LZXlbal1dID0gb2JqW2tleXNbaV1dW3N0cmVhbUtleVtqXV07ICAgICAgCiAgICB9CgogICAgc3RyZWFtc09ialtuYW1lXSA9IHRtcDsKICAgIHN0cmVhbXNOYW1lcy5wdXNoKG5hbWUpCiAgfQoKICBzdHJlYW1zTmFtZXMuc29ydCgpOwogIAogIC8vIENyZWF0ZSBUYWJsZSBmb3IgYWN0aXZlU3RyZWFtcwogIHZhciB0YWJsZSA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJhY3RpdmVTdHJlYW1zIik7CgogIGZvciAodmFyIGkgPSAwOyBpIDwgc3RyZWFtc05hbWVzLmxlbmd0aDsgaSsrKSB7CiAgICB2YXIgbmV3RW50cnkgPSBuZXcgT2JqZWN0KCk7CiAgICBuZXdFbnRyeVsiX2VsZW1lbnQiXSA9ICJUUiI7CiAgICB0YWJsZS5hcHBlbmRDaGlsZChjcmVhdGVFbGVtZW50KG5ld0VudHJ5KSk7CiAgICB2YXIgbGluZSA9IHRhYmxlLmxhc3RDaGlsZDsKCiAgICB2YXIgdG1wID0gc3RyZWFtc09ialtzdHJlYW1zTmFtZXNbaV1dCiAgICB2YXIga2V5cyA9IGdldE9iaktleXModG1wKQoKICAgIHZhciBuZXdLZXkgPSBuZXcgT2JqZWN0KCkKICAgIG5ld0tleVsiX2VsZW1lbnQiXSAgPSAiVEQiOwogICAgLy9uZXdLZXlbIl90ZXh0Il0gICAgID0gc3RyZWFtc05hbWVzW2ldOwogICAgbmV3S2V5WyJfdGV4dCJdICAgICA9ICJDaGFubmVsOiI7CiAgICBuZXdLZXlbImNsYXNzIl0gICAgID0gInRkS2V5IjsKICAgIGNvbnNvbGUubG9nKCk7CgoKICAgIHZhciBuZXdWYWwgPSBuZXcgT2JqZWN0KCkKICAgIG5ld1ZhbFsiX2VsZW1lbnQiXSAgPSAiVEQiOwogICAgbmV3VmFsWyJfdGV4dCJdICAgICA9IHN0cmVhbXNOYW1lc1tpXTsKICAgIG5ld1ZhbFsiY2xhc3MiXSAgICAgPSAidGRWYWwiOwogICAgLy9uZXdWYWxbIl90ZXh0Il0gICAgID0gdmFsdWU7CiAgICAKICAgIGxpbmUuYXBwZW5kQ2hpbGQoY3JlYXRlRWxlbWVudChuZXdLZXkpKTsKICAgIGxpbmUuYXBwZW5kQ2hpbGQoY3JlYXRlRWxlbWVudChuZXdWYWwpKTsKICAgIAogIH0KCn0KCmZ1bmN0aW9uIHBhcnNlTG9ncyhvYmopIHsKILine truncated
|
||||||
|
webUI["html/js/users.js"] = "ZnVuY3Rpb24gb3BlblVzZXJzKGVsbSkgewogIGNvbG9tblNvcnQgPSAwOwoKICB2YXIgbmV3RGl2ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoInNldHRpbmdzIik7CiAgCiAgdmFyIG5ld0VudHJ5ID0gbmV3IE9iamVjdCgpOwogIG5ld0VudHJ5WyJfZWxlbWVudCJdICA9ICJIUiI7CiAgbmV3RGl2LmFwcGVuZENoaWxkKGNyZWF0ZUVsZW1lbnQobmV3RW50cnkpKTsKCiAgdmFyIG5ld0VudHJ5ID0gbmV3IE9iamVjdCgpOwogIG5ld0VudHJ5WyJfZWxlbWVudCJdICA9ICJJTlBVVCI7CiAgbmV3RW50cnlbInR5cGUiXSA9ICJidXR0b24iOwogIG5ld0VudHJ5WyJjbGFzcyJdID0gImJ1dHRvbiI7CiAgbmV3RW50cnlbInZhbHVlIl0gPSAiTmV3IjsKICBuZXdFbnRyeVsib25jbGljayJdID0gInVzZXJEZXRhaWwoMCkiOwogIG5ld0Rpdi5hcHBlbmRDaGlsZChjcmVhdGVFbGVtZW50KG5ld0VudHJ5KSk7CgogIHZhciBkaXYgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgic2V0dGluZ3MiKTsKCiAgIC8vIEJ1aWxkIHRhYmxlCiAgdmFyIG5ld1RhYmxlID0gbmV3IE9iamVjdCgpOwogIG5ld1RhYmxlWyJfZWxlbWVudCJdICA9ICJUQUJMRSI7CiAgbmV3VGFibGVbImlkIl0gICAgICAgID0gImlkX21hcHBpbmciOwogIG5ld1RhYmxlWyJjbGFzcyJdICAgICA9ICJ0YWJsZS1tYXBwaW5nIjsKICBkaXYuYXBwZW5kQ2hpbGQoY3JlYXRlRWxlbWVudChuZXdUYWJsZSkpOwoKICBzZXRUaW1lb3V0KGZ1bmN0aW9uKCl7IAogICAgY3JlYXRlVXNlcnNUYWJsZSgpOyAKICB9LCAxMCk7Cn0KCmZ1bmN0aW9uIGNyZWF0ZVVzZXJzVGFibGUoKSB7CiAgdmFyIHRhYmxlID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoImlkX21hcHBpbmciKTsKICB0YWJsZS5pbm5lckhUTUwgPSAiIjsKICB2YXIgbmV3VFIgPSBuZXcgT2JqZWN0KCk7CiAgbmV3VFJbIl9lbGVtZW50Il0gPSAiVFIiOwogIG5ld1RSWyJjbGFzcyJdICAgID0gInRhYmxlLW1hcHBpbmctaGVhZGVyIjsKICB0YWJsZS5hcHBlbmRDaGlsZChjcmVhdGVFbGVtZW50KG5ld1RSKSk7CgogIHZhciB0ciA9IHRhYmxlLmxhc3RDaGlsZDsKICB2YXIgdHJIZWFkbGluZXMgPSBuZXcgQXJyYXkoIlVzZXJuYW1lIiwgIlBhc3N3b3JkIiwgIldFQiIsICJQTVMiLCAiTTNVIiwgIlhNTCIsICJBUEkiKQoKICBmb3IgKHZhciBpID0gMDsgaSA8IHRySGVhZGxpbmVzLmxlbmd0aDsgaSsrKSB7CiAgICB2YXIgbmV3VEQgPSBuZXcgT2JqZWN0KCk7CiAgICBuZXdURFsiX2VsZW1lbnQiXSA9ICJURCI7CiAgICBuZXdURFsiX3RleHQiXSAgICA9IHRySGVhZGxpbmVzW2ldOwogICAgdHIuYXBwZW5kQ2hpbGQoY3JlYXRlRWxlbWVudChuZXdURCkpOwogIH0KCgogIC8vIFNvcnQgdXNlcnMKICB2YXIgdXNlcklkcyA9IGdldE9iaktleXModXNlcnMpOwoKICB2YXIgdXNlck9iaiA9IG5ldyBPYmplY3QoKTsKCiAgZm9yICh2YXIgaSA9IDA7IGkgPCB1c2VySWRzLmxlbmd0aDsgaSsrKSB7CiAgICB2YXIgdXNlcm5hbWUgPSB1c2Vyc1t1c2VySWRzW2ldXVsiZGF0YSJdWyJ1c2VybmFtZSJdOwogICAgdXNlck9ialt1c2VybmFtZV0gPSB1c2VySWRzW2ldOwogIH0KCiAgdmFyIGFsbFVzZXJzID0gZ2V0T2JqS2V5cyh1c2VyT2JqKTsKICBhbGxVc2Vycy5zb3J0KCk7CiAgLy8gLS0KCiAgZm9yICh2YXIgaSA9IDA7IGkgPCBhbGxVc2Vycy5sZW5ndGg7IGkrKykgewogICAgdmFyIHRhYmxlICAgICA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJpZF9tYXBwaW5nIik7CiAgICB2YXIgdXNlcklEICAgID0gdXNlck9ialthbGxVc2Vyc1tpXV07CiAgICB2YXIgdXNlcm5hbWUgID0gYWxsVXNlcnNbaV07CiAgICB2YXIgaXRlbSAgICAgID0gdXNlcnNbdXNlcklEXVsiZGF0YSJdOwoKICAgIC8vIENyZWF0ZSBUUgogICAgdmFyIG5ld1RSID0gbmV3IE9iamVjdCgpOwogICAgbmV3VFJbIl9lbGVtZW50Il0gICAgICAgPSAiVFIiOwogICAgbmV3VFJbImNsYXNzIl0gICAgICAgICAgPSAiIjsKICAgIG5ld1RSWyJpZCJdICAgICAgICAgICAgID0gdXNlcklEOwogICAgbmV3VFJbIm9uY2xpY2siXSAgICAgICAgPSAnamF2YXNjcmlwdDogdXNlckRldGFpbCgiJyArIHVzZXJJRCArICciKTsnOwogICAgdGFibGUuYXBwZW5kQ2hpbGQoY3JlYXRlRWxlbWVudChuZXdUUikpOwoKICAgIHZhciB0ciA9IHRhYmxlLmxhc3RDaGlsZDsKCiAgICAvLyBDcmVhdGUgdXNlcm5hbWUgVEQKICAgIHZhciBuZXdURCA9IG5ldyBPYmplY3QoKTsKICAgIG5ld1REWyJfZWxlbWVudCJdID0gIlAiOwogICAgbmV3VERbIl90ZXh0Il0gICAgPSB1c2VybmFtZTsKICAgIGNyZWF0ZU5ld1REKG5ld1RELCB0cik7CgogICAgLy8gQ3JlYXRlIHBhc3N3b3JkIFRECiAgICB2YXIgbmV3VEQgPSBuZXcgT2JqZWN0KCk7CiAgICBuZXdURFsiX2VsZW1lbnQiXSA9ICJQIjsKICAgIG5ld1REWyJfdGV4dCJdICAgID0gIi4uLi4uIjsKICAgIGNyZWF0ZU5ld1REKG5ld1RELCB0cik7CgogICAgLy8gQ3JlYXRlIHdlYiBhY2Nlc3MKICAgIHZhciBuZXdURCA9IG5ldyBPYmplY3QoKTsKICAgIG5ld1REWyJfZWxlbWVudCJdID0gIlAiOwogICAgc3dpdGNoKGl0ZW1bImF1dGhlbnRpY2F0aW9uLndlYiJdKXsKICAgICAgY2FzZSB0cnVlOiBuZXdURFsiX3RleHQiXSAgICA9ICLinJMiOyBicmVhazsKICAgICAgZGVmYXVsdDogICBuZXdURFsiX3RleHQiXSAgICA9ICItIjsgYnJlYWs7CiAgICB9CiAgICBjcmVhdGVOZXdURChuZXdURCwgdHIpOwoKICAgIC8vIENyZWF0ZSBQTVMgYWNjZXNzCiAgICB2YXIgbmV3VEQgPSBuZXcgT2JqZWN0KCk7CiAgICBuZXdURFsiX2VsZW1lbnQiXSA9ICJQIjsKICAgIHN3aXRjaChpdGVtWyJhdXRoZW50aWNhdGlvbi5wbXMiXSl7CiAgICAgIGNhc2UgdHJ1ZTogbmV3VERbIl90ZXh0Il0gICAgPSAi4pyTIjsgYnJlYWs7CiAgICAgIGRlZmF1bHQ6ICAgbmV3VERbIl90ZXh0Il0gICAgPSAiLSI7IGJyZWFrOwogICAgfQogICAgY3JlYXRlTmV3VEQobmV3VEQsIHRyKTsKCiAgICAvLyBDcmVhdGUgTTNVIGFjY2VzcwogICAgdmFyIG5ld1REID0gbmV3IE9iamVjdCgpOwogICAgbmV3VERbIl9lbGVtZW50Il0gPSAiUCI7CiAgICBzd2l0Y2goaXRlbVsiYXV0aGVudGljYXRpb24ubTN1Il0pewogICAgICBjYXNlIHRydWU6IG5ld1REWyJfdGV4dCJdICAgID0gIuKckyI7IGJyZWFrOwogICAgICBkZWZhdWx0OiAgIG5ld1REWyJfdGV4dCJdICAgID0gIi0iOyBicmVhazsKICAgIH0KICAgIGNyZWF0ZU5ld1REKG5ld1RELCB0cik7CgogICAgLy8gQ3JlYXRlIFhNTFRWIGFjY2VzcwogICAgdmFyIG5ld1REID0gbmV3IE9iamVjdCgpOwogICAgbmV3VERbIl9lbGVtZW50Il0gPSAiUCI7CiAgICBzd2l0Y2goaXRlbVsiYXV0aGVudGljYXRpb24ueG1sIl0pewogICAgICBjYXNlIHRydWU6IG5ld1REWyJfdGV4dCJdICAgID0gIuKckyI7IGJyZWFrOwogICAgICBkZWZhdWx0OiAgIG5ld1REWyJfdGV4dCJdICAgID0gIi0iOyBicmVhazsKICAgIH0KICAgIGNyZWF0ZU5ld1REKG5ld1RELCB0cik7CgogICAgLy8gQ3JlYXRlIEFQSSBhY2Nlc3MKICAgIHZhciBuZXdURCA9IG5ldyBPYmplY3QoKTsKICAgIG5ld1REWyJfZWxlbWVudCJdID0gIlAiOwoKICAgIHN3aXRjaChpdGVtWyJhdXRoZW50aWNhdGlvbi5hcGkiXSl7CiAgICAgIGNhc2UgdHJ1ZTogbmV3VERbIl90ZXh0Il0gICAgPSAi4pyTIjsgYnJlYWs7CiAgICAgIGRlZmF1bHQ6ICAgbmV3VERbIl90ZXh0Il0gICAgPSAiLSI7Line truncated
|
||||||
|
webUI["html/configuration.html"] = "PCFkb2N0eXBlIGh0bWw+CjxodG1sPgogIDxoZWFkPgogICAgPG1ldGEgY2hhcnNldD0idXRmLTgiPgogICAgPG1ldGEgbmFtZT0idmlld3BvcnQiIGNvbnRlbnQ9IndpZHRoPWRldmljZS13aWR0aCwgaW5pdGlhbC1zY2FsZT0xLjAiIC8+IAogICAgPHRpdGxlPnhUZVZlPC90aXRsZT4KICAgIDxsaW5rIHJlbD0ic3R5bGVzaGVldCIgaHJlZj0iY3NzL3NjcmVlbi5jc3MiIHR5cGU9InRleHQvY3NzIj4KICAgIDxsaW5rIHJlbD0ic3R5bGVzaGVldCIgaHJlZj0iY3NzL2Jhc2UuY3NzIiB0eXBlPSJ0ZXh0L2NzcyI+CiAgICA8c2NyaXB0IGxhbmd1YWdlPSJqYXZhc2NyaXB0IiB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiIHNyYz0ianMvY29uZmlndXJhdGlvbl90cy5qcyI+PC9zY3JpcHQ+CiAgICA8c2NyaXB0IGxhbmd1YWdlPSJqYXZhc2NyaXB0IiB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiIHNyYz0ianMvbmV0d29ya190cy5qcyI+PC9zY3JpcHQ+CiAgICA8c2NyaXB0IGxhbmd1YWdlPSJqYXZhc2NyaXB0IiB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiIHNyYz0ianMvbWVudV90cy5qcyI+PC9zY3JpcHQ+CiAgICA8c2NyaXB0IGxhbmd1YWdlPSJqYXZhc2NyaXB0IiB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiIHNyYz0ianMvc2V0dGluZ3NfdHMuanMiPjwvc2NyaXB0PgogICAgPHNjcmlwdCBsYW5ndWFnZT0iamF2YXNjcmlwdCIgdHlwZT0idGV4dC9qYXZhc2NyaXB0IiBzcmM9ImpzL2Jhc2VfdHMuanMiPjwvc2NyaXB0PgogIDwvaGVhZD4KCiAgICA8Ym9keSBvbmxvYWQ9ImphdmFzY3JpcHQ6IHJlYWR5Rm9yQ29uZmlndXJhdGlvbigwKTsiPgogICAgICAgICAgCiAgICAgIDxkaXYgaWQ9ImxvYWRpbmciIGNsYXNzPSJibG9jayI+CiAgICAgICAgPGRpdiBjbGFzcz0ibG9hZGVyIj48L2Rpdj4KICAgICAgPC9kaXY+CgogICAgICA8ZGl2IGlkPSJoZWFkZXIiIGNsYXNzPSJpbWdDZW50ZXIiPjwvZGl2PgogICAgICA8ZGl2IGlkPSJib3giPgogICAgICAgIAogICAgICAgIDx0YWJsZSBpZD0iY2xpZW50SW5mbyIgY2xhc3M9InZpc2libGUiPgogICAgICAgICAgPHRyPgogICAgICAgICAgICA8dGQgY2xhc3M9InRkS2V5Ij5WZXJzaW9uOjwvdGQ+CiAgICAgICAgICAgIDx0ZCBpZD0idmVyc2lvbiIgY2xhc3M9InRkVmFsIj4mbmJzcDs8L3RkPgogICAgICAgICAgICA8dGQgY2xhc3M9InRkS2V5Ij5PUzo8L3RkPgogICAgICAgICAgICA8dGQgaWQ9Im9zIiBjbGFzcz0idGRWYWwiPiZuYnNwOzwvdGQ+CiAgICAgICAgICA8L3RyPgogICAgICAgICAgPHRyPgogICAgICAgICAgICA8dGQgY2xhc3M9InRkS2V5Ij5VVUlEOjwvdGQ+CiAgICAgICAgICAgIDx0ZCBpZD0idXVpZCIgY2xhc3M9InRkVmFsIj4mbmJzcDs8L3RkPgogICAgICAgICAgICA8dGQgY2xhc3M9InRkS2V5Ij5BcmNoOjwvdGQ+CiAgICAgICAgICAgIDx0ZCBpZD0iYXJjaCIgY2xhc3M9InRkVmFsIj4mbmJzcDs8L3RkPgogICAgICAgICAgPC90cj4KICAgICAgICAgIDx0cj4KICAgICAgICAgICAgPHRkIGNsYXNzPSJ0ZEtleSI+U3RyZWFtczo8L3RkPgogICAgICAgICAgICA8dGQgaWQ9InN0cmVhbXMiIGNsYXNzPSJ0ZFZhbCI+Jm5ic3A7PC90ZD4KICAgICAgICAgICAgPHRkIGNsYXNzPSJ0ZEtleSI+RFZSOjwvdGQ+CiAgICAgICAgICAgIDx0ZCBpZD0iRFZSIiBjbGFzcz0idGRWYWwiPiZuYnNwOzwvdGQ+CiAgICAgICAgICA8L3RyPgogICAgICAgIDwvdGFibGU+CiAgICAgICAgCiAgICAgICAgPGRpdiBpZD0iaGVhZGxpbmUiPgogICAgICAgICAgPGgxIGlkPSJoZWFkLXRleHQiIGNsYXNzPSJjZW50ZXIiPkNvbmZpZ3VyYXRpb248L2gxPiAgICAgIAogICAgICAgIDwvZGl2PgogICAgICAgIDxwIGlkPSJlcnIiIGNsYXNzPSJlcnJvck1zZyBjZW50ZXIiPjwvcD4gICAKICAgICAgICA8ZGl2IGlkPSJjb250ZW50Ij4KICAgICAgICAgICAgCiAgICAgICAgPC9kaXY+CiAgICAgICAgPGRpdiBpZD0iYm94LWZvb3RlciI+CiAgICAgICAgICA8aW5wdXQgaWQ9Im5leHQiIGNsYXNzPSIiIHR5cGU9ImJ1dHRvbiIgbmFtZT0ibmV4dCIgdmFsdWU9Ik5leHQiIG9uY2xpY2s9ImphdmFzY3JpcHQ6IHNhdmVXaXphcmQoKTsiPgogICAgICAgIDwvZGl2PgogICAgICA8L2Rpdj4KICAgIDwvYm9keT4KPC9odG1sPg=="
|
||||||
|
webUI["html/css/.DS_Store"] = "AAAAAUJ1ZDEAABAAAAAIAAAAEAAAAAAlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAABAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAgLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAACAAAAABAAAAQAAAAAEAAACAAAAAAQAAAQAAAAABAAACAAAAAAEAAAQAAAAAAAAAAAEAABAAAAAAAQAAIAAAAAABAABAAAAAAAEAAIAAAAAAAQABAAAAAAABAAIAAAAAAAEABAAAAAAAAQAIAAAAAAABABAAAAAAAAEAIAAAAAAAAQBAAAAAAAABAIAAAAAAAAEBAAAAAAAAAQIAAAAAAAABBAAAAAAAAAEIAAAAAAAAARAAAAAAAAABIAAAAAAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALine truncated
|
||||||
|
webUI["html/css/screen2.css"] = "aDEgewogIGNvbG9yOiBncmVlbjsKfQ=="
|
||||||
|
webUI["html/img/logo_w_600x200.png"] = "iVBORw0KGgoAAAANSUhEUgAAAlgAAADICAYAAAA0n5+2AAAAAXNSR0IArs4c6QAAAAlwSFlzAAAuIwAALiMBeKU/dgAABCZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IlhNUCBDb3JlIDUuNC4wIj4KICAgPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgICAgICAgICAgIHhtbG5zOmV4aWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vZXhpZi8xLjAvIgogICAgICAgICAgICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgICAgICAgICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyI+CiAgICAgICAgIDx0aWZmOlJlc29sdXRpb25Vbml0PjI8L3RpZmY6UmVzb2x1dGlvblVuaXQ+CiAgICAgICAgIDx0aWZmOkNvbXByZXNzaW9uPjU8L3RpZmY6Q29tcHJlc3Npb24+CiAgICAgICAgIDx0aWZmOlhSZXNvbHV0aW9uPjMwMDwvdGlmZjpYUmVzb2x1dGlvbj4KICAgICAgICAgPHRpZmY6T3JpZW50YXRpb24+MTwvdGlmZjpPcmllbnRhdGlvbj4KICAgICAgICAgPHRpZmY6WVJlc29sdXRpb24+MzAwPC90aWZmOllSZXNvbHV0aW9uPgogICAgICAgICA8ZXhpZjpQaXhlbFhEaW1lbnNpb24+NjAwPC9leGlmOlBpeGVsWERpbWVuc2lvbj4KICAgICAgICAgPGV4aWY6Q29sb3JTcGFjZT4xPC9leGlmOkNvbG9yU3BhY2U+CiAgICAgICAgIDxleGlmOlBpeGVsWURpbWVuc2lvbj4yMDA8L2V4aWY6UGl4ZWxZRGltZW5zaW9uPgogICAgICAgICA8ZGM6c3ViamVjdD4KICAgICAgICAgICAgPHJkZjpCYWcvPgogICAgICAgICA8L2RjOnN1YmplY3Q+CiAgICAgICAgIDx4bXA6TW9kaWZ5RGF0ZT4yMDE5LTA4LTAxVDEyOjA4OjQzPC94bXA6TW9kaWZ5RGF0ZT4KICAgICAgICAgPHhtcDpDcmVhdG9yVG9vbD5QaXhlbG1hdG9yIDMuMzwveG1wOkNyZWF0b3JUb29sPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KaDoJhwAAJyJJREFUeAHtnQm8XUV9x+ckIQHZEkKWF4UCAlZZZSuyuICRRRCQtmxWwYoohYIial0qrljcgbYqCmhRjEEoAaIGFShirLJ8gCI0CBqweS8JYYkQlkBOf/+X98J9793lnHtm5px773c+n8m7Z87M//+f70zu+d+ZOTPOESAAAQhAAAIQgAAEIAABCEAAAhCAAAQgAAEIQAACEIAABCAAAQhAAAIQgAAEIAABCEAAAhCAAAQgAAEIQAACEIAABCAAAQhAAAIQgAAEIAABCEAAAhCAAAQgAAEIQAACEIAABCAAAQhAAAIQgAAEIAABCEAAAhCAAAQgAAEIQAACEIAABCAAAQhAAAIQgAAEIAABCEAAAhCAAAQgAAEIQAACEIAABCAAAQhAAAIQgAAEIAABCEAAAhCAAAQgAAEIQAACEIAABCAAAQhAAAIQgAAEIAABCEAAAhCAAAQgAAEIQAACEIAABCAAAQhAAAIQgAAEIAABCEAAAhCAAAQgAAEIQAACEIAABCAAAQhAAAIQgAAEIAABCEAAAhCAAAQgAAEIQAACEIAABCAAAQhAAAIQgAAEIAABCEAAAhCAAAQgAAEIQAACEIAABCAAAQhAAAIQgAAEIAABCEAAAhCAAAQgAAEIQAACEIAABCAAAQhAAAIQgAAEIAABCEAAAhCAAAQgAAEIQAACEIAABCAAAQhAAAIQgAAEIAABCEAAAhCAAAQgAAEIQAACEIAABCAAAQhAAAIQgAAEIAABCEAAAhCAAAQgAAEIQAACEIAABCAAAQhAAAIQgAAEIAABCEAAAhCAAAQgAAEIQAACEIAABCAAAQhAAAIQgAAEIAABCEAAAhCAAAQgAAEIQAACEIAABCAAAQhAAAIQgAAEIAABCEAAAhCAAAQgAAEIQAACEIAABCAAAQhAAAIQgAAEIAABCEAAAhCAAAQgAAEIQAACEIAABCAAAQhAAAIQgAAEIAABCEAAAhCAAAQgAAEIQAACEIAABCAAAQhAAAIQgAAEIAABCEAAAhCAAAQgAAEIQAACEIAABCAAgV4ikPRSZakrBNI03UwUjoKEdwKrkiS53LvUHhMYuX8uV5vN6xTEYvM62bptBHsfFJcbIuhBRZcTwMHq8gameiMJ6Ev6MKVcMzKVK08EJurBtNqTrJ4Uo/45WxVfEKnyz0jPNLXZk5H0FVIjNr+XgJcXEpKt8A1ickC2rOSCQGMC4xrf4g4EIAABCEQmcKP0rYikc33pOTSSrkJq5FztLAExnCuz80eFjKUwBIYI4GDRFSAAAQhUhMDQCOCVEc15a0RdRVTFsvMFGTm3iKGUhcAwARysYRL8hQAEIFANAj+IaMahGh2aFFFfu6piOVg2PbisXSMpB4FaAjhYtTT4DAEIQKB8AjfJhIFIZmwsPW+KpKstNXIAt1PBndoqnL9QTOc2v3WU6CgCOFgd1VwYCwEIdDsBjaDEnqaKNTrUbtPFsu85GRhzerZdHpTrEAI4WB3SUJgJAQj0FIGYIymHa5RoQoXpxnKwFsi5fazCHDCtwwjgYHVYg2EuBCDQEwQWqpYPRarpVOmxPaYqF+T4bSGj9oxkWEynNlKVUFMmARysMumjGwIQgEAdAhpJSZU8p86tUEmxRony2n+kCsTYr3GV9Fyd1zjyQ6AZARysZnS4BwEIQKA8AjFHVI7UaFEMRyYvzaPzFmgz/3w5tR2x4Wqb9aNYCQRwsEqAjkoIQAACrQjogX+78ixqlc/T/VmS8xpPsryIkcM3TYL28yKstZCYzmxra8jRFQRwsLqiGakEBCDQpQR6eZrwCLXp+AjtulI6rougBxU9RgAHq8canOpCAAIdRSCmg1W1Q9BjrQubp9FCO5eRAAGvBHCwvOJEGAQgAAF/BPTgv0fS7vYnsamkbTQtt2vTHJFuyo5NperASOqYHowEutfU4GD1WotTXwhAoNMIxHQAYo0atWqDw5RhYqtMHu7bwdoLPMhBBATGEMDBGoOEBAhAAAKVIhBzmjDWW3utAMdy9K7SKOHqVsZwHwLtEMDBaocaZSAAAQhEIiAH4AGp+m0kda/S9NwrIumqq0b6X6IbB9e96T8x5uigf+uRWGkCOFiVbh6MgwAEIDBIIKYjEGv0qFHTHqQb5mSFDgNScGNoJcjvXQI4WL3b9tQcAhDoHAJzZart7h4jlO1gxdJ/hUYH7WBtAgSCEKjyAZ9BKozQnidwnwh8pwQKW0lnrPPeyqifHTXCwypQx5Ij8LCmzn4p8fsHUlErdg/p2lI6H6pNjPFZem1h++ExdElHzFHBSFVCDQQgAIEeI6AHx96KMcITPYa2Z6qrznNqjA40pOPMMsBK98GR6vhH6ani0UBlYEdnIAJMEQYCi1gIQAACnglcIXmxRgljTdONRhRL71yN0MWach1dR657hABThD3S0FQTAr1EQKMT01XfzYfi1FF/Ld3SNlG0qU3bC+nRmr+1nx/Ug/gR3Ss9yI5lqtcvZMjsCMbsK10zpHNpBF2DKqTPjsWx43FihMpPD3ZjH47RsFXSgYNVpdbAFghAoC0Cehj9hQrazt8HDMW+tgSNLSTR6e+UfKPiTRbN0dHfsoI5BjEcLJvdMGfnmxEruq90mWMcOixSG94WWkle+T3Uh/Oi6dj8vT0HvSQ92iVuw2itN979wU1Pbo6mb3m6vXve7R1NX+z6RatYcUX68rR2WFhcUksJK/XwsGNGujqIpz2Ih50pc6y2iVjhe6XrRsVrxPrHEfU61XuK9Nn2ArYYPHT4qep3cGglw/JVt6/q8xnD1wH/fkb1+nhA+ZlE92ofzgSnSzL1roNlw9H9zr4ot4vYlk+7CW4XOVn3B9eZppP0NXyHXux+ZXBdwwoSvZ3Xl5w4fMnfFwnoyxQH60UcbX8Sxz1U+BzFN7ctxG/BOyTuM4q2I3iUNT1iME/6YrxpZzucT1e9HtffoEF1smfRYsUtgipaK3xH1cnOeCwl0IdLwV6KUhsG7s2wdv+T4zSCFfOYhA00onSxfoaG5z7gPhnZuXpQv6n/sTc7E7UOTUAPpT0Vr5Ue29G8Ks6VVfvVij9SvEv2HasY/v92vO0F1lO9YjhyUuPMcY7hXN1VlnNFH7Zm7q0Q48ugukRnDc7DfySygfu5pe60oDr7070k/wNBddQKX+ukHuumJitrk/kMgaIEah5Kv5GsKjlWo6u2oxIuV/ydbLa1SyGDjWA9HVJBjexYb/XF0vPDmrpF+UgfjoK5kkp628GyJpnpvqR/r4/aOmvcuW5p+vIgOm1q0LlLNHo1Poj8+kI/oqlBG1kgQMALAT2UtlK0EauqO1aj6/sKJfynbD9HMcgSDI3APCkdxiZGOEj12DCColgOVrS3B8WNPhyh41RZBQ6WrZuY4N6uqcLlERvqJW6N+7amCv1/AfdrfUrqXhWtLombP+SkRlOJoLine truncated
|
||||||
|
webUI["html/img/logout.png"] = "iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAAXNSR0IArs4c6QAAAAlwSFlzAAAsSwAALEsBpT2WqQAABCRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IlhNUCBDb3JlIDUuNC4wIj4KICAgPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgICAgICAgICAgIHhtbG5zOmV4aWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vZXhpZi8xLjAvIgogICAgICAgICAgICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgICAgICAgICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyI+CiAgICAgICAgIDx0aWZmOlJlc29sdXRpb25Vbml0PjI8L3RpZmY6UmVzb2x1dGlvblVuaXQ+CiAgICAgICAgIDx0aWZmOkNvbXByZXNzaW9uPjU8L3RpZmY6Q29tcHJlc3Npb24+CiAgICAgICAgIDx0aWZmOlhSZXNvbHV0aW9uPjI4ODwvdGlmZjpYUmVzb2x1dGlvbj4KICAgICAgICAgPHRpZmY6T3JpZW50YXRpb24+MTwvdGlmZjpPcmllbnRhdGlvbj4KICAgICAgICAgPHRpZmY6WVJlc29sdXRpb24+Mjg4PC90aWZmOllSZXNvbHV0aW9uPgogICAgICAgICA8ZXhpZjpQaXhlbFhEaW1lbnNpb24+NTA8L2V4aWY6UGl4ZWxYRGltZW5zaW9uPgogICAgICAgICA8ZXhpZjpDb2xvclNwYWNlPjE8L2V4aWY6Q29sb3JTcGFjZT4KICAgICAgICAgPGV4aWY6UGl4ZWxZRGltZW5zaW9uPjUwPC9leGlmOlBpeGVsWURpbWVuc2lvbj4KICAgICAgICAgPGRjOnN1YmplY3Q+CiAgICAgICAgICAgIDxyZGY6QmFnLz4KICAgICAgICAgPC9kYzpzdWJqZWN0PgogICAgICAgICA8eG1wOk1vZGlmeURhdGU+MjAxOC0xMC0xM1QxMToxMDoxODwveG1wOk1vZGlmeURhdGU+CiAgICAgICAgIDx4bXA6Q3JlYXRvclRvb2w+UGl4ZWxtYXRvciAzLjM8L3htcDpDcmVhdG9yVG9vbD4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+Cg27QeEAAANQSURBVGgF7Zk9aBRBGIZzSRSN8YdoCpEkghCCqK1o4g9IsBCCNmIniCAiWAhqI8G02qiIoqCCWggSEcFKEFPYpVAU8QdFUyiKRpRokBjP55OdZDLZvZnZ7N3Ngh887M7sN/O9797t7c5eTc3/8D8DxWKxGS5Ai//oAEYgvB4OwleQaAtAlp8ERG+Bp6Jei/wYQfRy6NfE67vhG0FtA/TBqK7c2A/bCGJ3wpAhOq4ZphGUroEHcYoT+sIygsgmOAu/EwQndYdhBHV1sB8+Jym19Lf6/faVIRuBG+GRRajtcPWMoKwFbtgUOh6vvBGEzYFj8MNRpEtaZY2gaAe8cVHmmXOc/K3QXIZv/+SUFFgJ96AS8ZwiZ6Ab6iZVzGCPiRbCKRiDasQHip6EdD/PDKyFvfAJQgg5kVfB/VoieT0MQojxE1G9UG/7otWS0A7pPkrb7DM/Ppcp+mAAM/bFGEmL4RL8gVDjI8JWOZ0bEjvhSahO0CXXsLMZWZYegREIMV4jaoHTJyNJJLfC7RCdoOm6sxGVyKAeeBegoU6l0XmLiXlwAqp1o4w7j3d1AwW9YdtnNrnQzkOXLddyfAPHv8Ey6ABpd8N88In2QqHwymfARC5mCrAH0i6qGDr9jk3fbNgFz8A1DkwIS7tDpSVwGdLcexIfPZhPHpf2wS+wRX9a/dPGUakLzBdwNgGJRlQBJtgMw5aJ3qr8TLYUmwVHwXXxZTUiwphvHYxDUnzPxIA5CdXa4E5SVa3fyUhkRtYqpSKbNYxpJiq+ncpDJar7GGlknvcl5mqM05BZH4Xl3iOLpLh7j7OR6MScSzAykplg20QIWA0PDSG+RuTdQVy8sNXP9DgK5N4jK9AvkRpfI4sYF/c2836mQl0nQ4zce66AfZFkTMqYl2DGaSMt/CYOHpsuaPco5bLUzUuMGkLHaQ+ovjwZUZrVdpAHRnnw/Bd5MrJCiY62t4x2+E2uBfkfRg95TdSkK8/LJ7JWF83+Nb5Ww0Zf+E3OviwXVMgT8dLwVRsKEd0A+uP8biMlH01MHAYVF/Oh2lCJenkDqpbUN9nPyzU91QnCRbyEPEXn1sQhxMv7tG1T7cW3rK/r44eVtxfxm6gwBh38zJqPJuUtXu3Z/wLwuBaBLgMkKwAAAABJRU5ErkJggg=="
|
||||||
|
webUI["html/js/menu_ts.js"] = "dmFyIF9fZXh0ZW5kcyA9ICh0aGlzICYmIHRoaXMuX19leHRlbmRzKSB8fCAoZnVuY3Rpb24gKCkgewogICAgdmFyIGV4dGVuZFN0YXRpY3MgPSBmdW5jdGlvbiAoZCwgYikgewogICAgICAgIGV4dGVuZFN0YXRpY3MgPSBPYmplY3Quc2V0UHJvdG90eXBlT2YgfHwKICAgICAgICAgICAgKHsgX19wcm90b19fOiBbXSB9IGluc3RhbmNlb2YgQXJyYXkgJiYgZnVuY3Rpb24gKGQsIGIpIHsgZC5fX3Byb3RvX18gPSBiOyB9KSB8fAogICAgICAgICAgICBmdW5jdGlvbiAoZCwgYikgeyBmb3IgKHZhciBwIGluIGIpIGlmIChiLmhhc093blByb3BlcnR5KHApKSBkW3BdID0gYltwXTsgfTsKICAgICAgICByZXR1cm4gZXh0ZW5kU3RhdGljcyhkLCBiKTsKICAgIH07CiAgICByZXR1cm4gZnVuY3Rpb24gKGQsIGIpIHsKICAgICAgICBleHRlbmRTdGF0aWNzKGQsIGIpOwogICAgICAgIGZ1bmN0aW9uIF9fKCkgeyB0aGlzLmNvbnN0cnVjdG9yID0gZDsgfQogICAgICAgIGQucHJvdG90eXBlID0gYiA9PT0gbnVsbCA/IE9iamVjdC5jcmVhdGUoYikgOiAoX18ucHJvdG90eXBlID0gYi5wcm90b3R5cGUsIG5ldyBfXygpKTsKICAgIH07Cn0pKCk7CnZhciBNYWluTWVudSA9IC8qKiBAY2xhc3MgKi8gKGZ1bmN0aW9uICgpIHsKICAgIGZ1bmN0aW9uIE1haW5NZW51KCkgewogICAgICAgIHRoaXMuRG9jdW1lbnRJRCA9ICJtYWluLW1lbnUiOwogICAgICAgIHRoaXMuSFRNTFRhZyA9ICJMSSI7CiAgICAgICAgdGhpcy5JbWFnZVBhdGggPSAiaW1nLyI7CiAgICB9CiAgICBNYWluTWVudS5wcm90b3R5cGUuY3JlYXRlSU1HID0gZnVuY3Rpb24gKHNyYykgewogICAgICAgIHZhciBlbGVtZW50ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgiSU1HIik7CiAgICAgICAgZWxlbWVudC5zZXRBdHRyaWJ1dGUoInNyYyIsIHRoaXMuSW1hZ2VQYXRoICsgc3JjKTsKICAgICAgICByZXR1cm4gZWxlbWVudDsKICAgIH07CiAgICBNYWluTWVudS5wcm90b3R5cGUuY3JlYXRlVmFsdWUgPSBmdW5jdGlvbiAodmFsdWUpIHsKICAgICAgICB2YXIgZWxlbWVudCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoIlAiKTsKICAgICAgICBlbGVtZW50LmlubmVySFRNTCA9IHZhbHVlOwogICAgICAgIHJldHVybiBlbGVtZW50OwogICAgfTsKICAgIHJldHVybiBNYWluTWVudTsKfSgpKTsKdmFyIE1haW5NZW51SXRlbSA9IC8qKiBAY2xhc3MgKi8gKGZ1bmN0aW9uIChfc3VwZXIpIHsKICAgIF9fZXh0ZW5kcyhNYWluTWVudUl0ZW0sIF9zdXBlcik7CiAgICBmdW5jdGlvbiBNYWluTWVudUl0ZW0obWVudUtleSwgdmFsdWUsIGltYWdlLCBoZWFkbGluZSkgewogICAgICAgIHZhciBfdGhpcyA9IF9zdXBlci5jYWxsKHRoaXMpIHx8IHRoaXM7CiAgICAgICAgX3RoaXMubWVudUtleSA9IG1lbnVLZXk7CiAgICAgICAgX3RoaXMudmFsdWUgPSB2YWx1ZTsKICAgICAgICBfdGhpcy5pbWdTcmMgPSBpbWFnZTsKICAgICAgICBfdGhpcy5oZWFkbGluZSA9IGhlYWRsaW5lOwogICAgICAgIHJldHVybiBfdGhpczsKICAgIH0KICAgIE1haW5NZW51SXRlbS5wcm90b3R5cGUuY3JlYXRlSXRlbSA9IGZ1bmN0aW9uICgpIHsKICAgICAgICB2YXIgaXRlbSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoIkxJIik7CiAgICAgICAgaXRlbS5zZXRBdHRyaWJ1dGUoIm9uY2xpY2siLCAiamF2YXNjcmlwdDogb3BlblRoaXNNZW51KHRoaXMpIik7CiAgICAgICAgaXRlbS5zZXRBdHRyaWJ1dGUoImlkIiwgdGhpcy5pZCk7CiAgICAgICAgdmFyIGltZyA9IHRoaXMuY3JlYXRlSU1HKHRoaXMuaW1nU3JjKTsKICAgICAgICB2YXIgdmFsdWUgPSB0aGlzLmNyZWF0ZVZhbHVlKHRoaXMudmFsdWUpOwogICAgICAgIGl0ZW0uYXBwZW5kQ2hpbGQoaW1nKTsKICAgICAgICBpdGVtLmFwcGVuZENoaWxkKHZhbHVlKTsKICAgICAgICB2YXIgZG9jID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQodGhpcy5Eb2N1bWVudElEKTsKICAgICAgICBkb2MuYXBwZW5kQ2hpbGQoaXRlbSk7CiAgICAgICAgc3dpdGNoICh0aGlzLm1lbnVLZXkpIHsKICAgICAgICAgICAgY2FzZSAicGxheWxpc3QiOgogICAgICAgICAgICAgICAgdGhpcy50YWJsZUhlYWRlciA9IFsie3sucGxheWxpc3QudGFibGUucGxheWxpc3R9fSIsICJ7ey5wbGF5bGlzdC50YWJsZS50dW5lcn19IiwgInt7LnBsYXlsaXN0LnRhYmxlLmxhc3RVcGRhdGV9fSIsICJ7ey5wbGF5bGlzdC50YWJsZS5hdmFpbGFiaWxpdHl9fSAlIiwgInt7LnBsYXlsaXN0LnRhYmxlLnR5cGV9fSIsICJ7ey5wbGF5bGlzdC50YWJsZS5zdHJlYW1zfX0iLCAie3sucGxheWxpc3QudGFibGUuZ3JvdXBUaXRsZX19ICUiLCAie3sucGxheWxpc3QudGFibGUudHZnSUR9fSAlIiwgInt7LnBsYXlsaXN0LnRhYmxlLnVuaXF1ZUlEfX0gJSJdOwogICAgICAgICAgICAgICAgYnJlYWs7CiAgICAgICAgICAgIGNhc2UgInhtbHR2IjoKICAgICAgICAgICAgICAgIHRoaXMudGFibGVIZWFkZXIgPSBbInt7LnhtbHR2LnRhYmxlLmd1aWRlfX0iLCAie3sueG1sdHYudGFibGUubGFzdFVwZGF0ZX19IiwgInt7LnhtbHR2LnRhYmxlLmF2YWlsYWJpbGl0eX19ICUiLCAie3sueG1sdHYudGFibGUuY2hhbm5lbHN9fSIsICJ7ey54bWx0di50YWJsZS5wcm9ncmFtc319Il07CiAgICAgICAgICAgICAgICBicmVhazsKICAgICAgICAgICAgY2FzZSAiZmlsdGVyIjoKICAgICAgICAgICAgICAgIHRoaXMudGFibGVIZWFkZXIgPSBbInt7LmZpbHRlci50YWJsZS5uYW1lfX0iLCAie3suZmlsdGVyLnRhYmxlLnR5cGV9fSIsICJ7ey5maWx0ZXIudGFibGUuZmlsdGVyfX0iXTsKICAgICAgICAgICAgICAgIGJyZWFrOwogICAgICAgICAgICBjYXNlICJ1c2VycyI6CiAgICAgICAgICAgICAgICB0aGlzLnRhYmxlSGVhZGVyID0gWyJ7ey51c2Vycy50YWJsZS51c2VybmFtZX19IiwgInt7LnVzZXJzLnRhYmxlLnBhc3N3b3JkfX0iLCAie3sudXNlcnMudGFibGUud2VifX0iLCAie3sudXNlcnMudGFibGUucG1zfX0iLCAie3sudXNlcnMudGFibGUubTN1fX0iLCAie3sudXNlcnMudGFibGUueG1sfX0iLCAie3sudXNlcnMudGFibGUuYXBpfX0iXTsKICAgICAgICAgICAgICAgIGJyZWFrOwogICAgICAgICAgICBjYXNlICJtYXBwaW5nIjoKICAgICAgICAgICAgICAgIHRoaXMudGFibGVIZWFkZXIgPSBbIkJVTEsiLCAie3subWFwcGluZy50YWJsZS5jaE5vfX0iLCAie3subWFwcGluZy50YWJsZS5sb2dvfX0iLCAie3subWFwcGluZy50YWJsZS5jaGFubmVsTmFtZX19IiwgInt7Lm1hcHBpbmcudGFibGUucGxheWxpc3R9fSIsICJ7ey5tYXBwaW5nLnRhYmxlLmdyb3VwVGl0bGV9fSIsICJ7ey5tYXBwaW5nLnRhYmxlLnhtbHR2RmlsZX19IiwgInt7Lm1hcHBpbmcudGFibGUueG1sdHZJRH19Il07CiAgICAgICAgICAgICAgICBicmVhazsKICAgICAgICB9CiAgICAgICAgLy9jb25zb2xlLmxvZyh0aGlzLm1lbnVLZXksIHRoaXMudGFibGVIZWFkZXIpOwogICAgfTsKICAgIHJldHVybiBNYWluTWVudUl0ZW07Cn0oTWFpbk1lbnUpKTsKdmFyIENvbnRlbnQgPSAvKiogQGNsYXNzICovIChmdW5jdGlvbiAoKSB7CiAgICBmdW5jdGlvbiBDb250ZW50KCkgewogICAgICAgIHRoaXMuRG9jdW1lbnRJRCA9ICJjb250ZW50IjsKICAgICAgICB0aGlzLlRhYmxlSUQgPSAiY29udGVudF90YWJsZSI7CiAgICAgICAgdGhpcy5oZWFkZXJDbGFzcyA9ICJjb250ZW50X3RhYmxlX2hlYWRlciI7CiAgICAgICAgdGhpcy5pbnRlcmFjdGlvbklEID0gImNvbnRlbnQtaW50ZXJhY3Line truncated
|
||||||
|
webUI["html/login.html"] = "PCFkb2N0eXBlIGh0bWw+CjxodG1sPgogIDxoZWFkPgogICAgPG1ldGEgY2hhcnNldD0idXRmLTgiPgogICAgPG1ldGEgbmFtZT0idmlld3BvcnQiIGNvbnRlbnQ9IndpZHRoPWRldmljZS13aWR0aCwgaW5pdGlhbC1zY2FsZT0xLjAiIC8+IAogICAgPHRpdGxlPnhUZVZlPC90aXRsZT4KICAgIDxsaW5rIHJlbD0ic3R5bGVzaGVldCIgaHJlZj0iY3NzL3NjcmVlbi5jc3MiIHR5cGU9InRleHQvY3NzIj4KICAgIDxsaW5rIHJlbD0ic3R5bGVzaGVldCIgaHJlZj0iY3NzL2Jhc2UuY3NzIiB0eXBlPSJ0ZXh0L2NzcyI+CiAgICA8c2NyaXB0IGxhbmd1YWdlPSJqYXZhc2NyaXB0IiB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiIHNyYz0ianMvbmV0d29ya190cy5qcyI+PC9zY3JpcHQ+CiAgICA8c2NyaXB0IGxhbmd1YWdlPSJqYXZhc2NyaXB0IiB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiIHNyYz0ianMvYXV0aGVudGljYXRpb25fdHMuanMiPjwvc2NyaXB0PgogIDwvaGVhZD4KCiAgICA8Ym9keT4KICAgICAgICAgIAogICAgICA8ZGl2IGlkPSJoZWFkZXIiIGNsYXNzPSJpbWdDZW50ZXIiPjwvZGl2PgogICAgICA8ZGl2IGlkPSJib3giPgogICAgICAgIDxkaXYgaWQ9ImhlYWRsaW5lIj4KICAgICAgICAgIDxoMSBpZD0iaGVhZC10ZXh0IiBjbGFzcz0iY2VudGVyIj57ey5sb2dpbi5oZWFkbGluZX19PC9oMT4KICAgICAgICA8L2Rpdj4KICAgICAgICA8cCBpZD0iZXJyIiBjbGFzcz0iZXJyb3JNc2cgY2VudGVyIj57ey5hdXRoZW50aWNhdGlvbkVycn19PC9wPiAgIAogICAgICAgIDxkaXYgaWQ9ImNvbnRlbnQiPgogICAgICAgICAgICA8Zm9ybSBpZD0iYXV0aGVudGljYXRpb24iIGFjdGlvbj0iL3dlYi8iIG1ldGhvZD0icG9zdCI+CiAgICAgICAgICAgICAgPGg1Pnt7LmxvZ2luLnVzZXJuYW1lLnRpdGxlfX06PC9oNT4KICAgICAgICAgICAgICA8aW5wdXQgaWQ9InVzZXJuYW1lIiB0eXBlPSJ0ZXh0IiBuYW1lPSJ1c2VybmFtZSIgcGxhY2Vob2xkZXI9IlVzZXJuYW1lIiB2YWx1ZT0iIj4KICAgICAgICAgICAgICA8aDU+e3subG9naW4ucGFzc3dvcmQudGl0bGV9fTo8L2g1PgogICAgICAgICAgICAgIDxpbnB1dCBpZD0icGFzc3dvcmQiIHR5cGU9InBhc3N3b3JkIiBuYW1lPSJwYXNzd29yZCIgcGxhY2Vob2xkZXI9IlBhc3N3b3JkIiB2YWx1ZT0iIj4KICAgICAgICAgICAgPC9mb3JtPgogICAgICAgIDwvZGl2PgogICAgICAgIDxkaXYgaWQ9ImJveC1mb290ZXIiPgogICAgICAgICAgICA8aW5wdXQgaWQ9InN1Ym1pdCIgY2xhc3M9IiIgdHlwZT0iYnV0dG9uIiB2YWx1ZT0ie3suYnV0dG9uLmxvZ2lufX0iIG9uY2xpY2s9ImphdmFzY3JpcHQ6IGxvZ2luKCk7Ij4KICAgICAgICAgIDwvZGl2PgogICAgICAgIAogICAgICA8L2Rpdj4KICAgIDwvYm9keT4KPC9odG1sPg=="
|
||||||
|
webUI["html/maintenance.html"] = "PCFkb2N0eXBlIGh0bWw+CjxodG1sPgogIDxoZWFkPgogICAgPG1ldGEgY2hhcnNldD0idXRmLTgiPgogICAgPG1ldGEgbmFtZT0idmlld3BvcnQiIGNvbnRlbnQ9IndpZHRoPWRldmljZS13aWR0aCwgaW5pdGlhbC1zY2FsZT0xLjAiIC8+IAogICAgPHRpdGxlPnhUZVZlPC90aXRsZT4KICAgIDxsaW5rIHJlbD0ic3R5bGVzaGVldCIgaHJlZj0iY3NzL3NjcmVlbi5jc3MiIHR5cGU9InRleHQvY3NzIj4KICAgIDxsaW5rIHJlbD0ic3R5bGVzaGVldCIgaHJlZj0iY3NzL2Jhc2UuY3NzIiB0eXBlPSJ0ZXh0L2NzcyI+CiAgPC9oZWFkPgoKICAgIDxib2R5PgogICAgICAgICAgCiAgICAgIDxkaXYgaWQ9ImhlYWRlciIgY2xhc3M9ImltZ0NlbnRlciI+PC9kaXY+CiAgICAgIDxkaXYgaWQ9ImJveCI+CiAgICAgICAgPGRpdiBpZD0iaGVhZGxpbmUiPgogICAgICAgICAgPGgxIGlkPSJoZWFkLXRleHQiIGNsYXNzPSJjZW50ZXIiPk1haW50ZW5hbmNlPC9oMT4gICAgICAKICAgICAgICA8L2Rpdj4gIAoKICAgICAgICA8ZGl2IGlkPSJjb250ZW50Ij4KICAgICAgICAgIHhUZVZlIGlzIHVwZGF0aW5nIHRoZSBkYXRhYmFzZSwgcGxlYXNlIHRyeSBhZ2FpbiBsYXRlci4KICAgICAgICA8L2Rpdj4KCiAgICAgICAgPGRpdiBpZD0iYm94LWZvb3RlciI+CiAgICAgICAgICAKICAgICAgICA8L2Rpdj4KICAgICAgICAKICAgICAgPC9kaXY+CiAgICA8L2JvZHk+CjwvaHRtbD4="
|
||||||
|
webUI["html/.DS_Store"] = "AAAAAUJ1ZDEAACAAAAAIAAAAIAAAABAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAA0AAAABAAAQAHNwYmxvYgAAAMpicAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAgLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAACAAAAABAAAAQAAAAAEAAACAAAAAAQAAAQAAAAABAAACAAAAAAEAAAQAAAAAAAAAAAEAABAAAAAAAQAAIAAAAAABAABAAAAAAAEAAIAAAAAAAQABAAAAAAABAAIAAAAAAAEABAAAAAAAAQAIAAAAAAABABAAAAAAAAEAIAAAAAAAAQBAAAAAAAABAIAAAAAAAAEBAAAAAAAAAQIAAAAAAAABBAAAAAAAAAEIAAAAAAAAARAAAAAAAAABIAAAAAAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALine truncated
|
||||||
|
webUI["html/img/x_ transparent.png"] = "iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAYAAABccqhmAAAAAXNSR0IArs4c6QAAAAlwSFlzAAAWJQAAFiUBSVIk8AAAA6ppVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IlhNUCBDb3JlIDUuNC4wIj4KICAgPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIgogICAgICAgICAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgICAgICAgICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iPgogICAgICAgICA8eG1wOk1vZGlmeURhdGU+MjAxOC0xMS0xNVQxNzoxMTo3NjwveG1wOk1vZGlmeURhdGU+CiAgICAgICAgIDx4bXA6Q3JlYXRvclRvb2w+UGl4ZWxtYXRvciAzLjM8L3htcDpDcmVhdG9yVG9vbD4KICAgICAgICAgPHRpZmY6T3JpZW50YXRpb24+MTwvdGlmZjpPcmllbnRhdGlvbj4KICAgICAgICAgPHRpZmY6Q29tcHJlc3Npb24+NTwvdGlmZjpDb21wcmVzc2lvbj4KICAgICAgICAgPHRpZmY6UmVzb2x1dGlvblVuaXQ+MjwvdGlmZjpSZXNvbHV0aW9uVW5pdD4KICAgICAgICAgPHRpZmY6WVJlc29sdXRpb24+MTQ0PC90aWZmOllSZXNvbHV0aW9uPgogICAgICAgICA8dGlmZjpYUmVzb2x1dGlvbj4xNDQ8L3RpZmY6WFJlc29sdXRpb24+CiAgICAgICAgIDxleGlmOlBpeGVsWERpbWVuc2lvbj4yNTY8L2V4aWY6UGl4ZWxYRGltZW5zaW9uPgogICAgICAgICA8ZXhpZjpDb2xvclNwYWNlPjE8L2V4aWY6Q29sb3JTcGFjZT4KICAgICAgICAgPGV4aWY6UGl4ZWxZRGltZW5zaW9uPjI1NjwvZXhpZjpQaXhlbFlEaW1lbnNpb24+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgoeXQb1AAAaJElEQVR4Ae2dCZhcVZXHz+1KSFiGkISkqyIoOgI68EkgOBNkEcQRlIFRZDGABAQEhInGbxw+RUZRNhWRLawDYR0GHAaHUVkGvwGURUcgwIiAOGxjV3UHAslHQrbuM/9TlQ6d7qruWt527/u/7+uuqvvucu7vvnveuffde54IDxIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARLwiIDzQtayHi0q23shaydCOnlISu7nnWThRdrFOkPWyCleyNqJkF1yjhTd8k6yiDvtuLgLiCR/lVnIZ14keWU7k2Xyuu4oU92r2RazQ+nWypXI4W86zCXbyZ28KBvJ97MtpEhX1gWsyleS08TJU17I2omQKpvLarmqkywyn7ZHPw9rLvTOvwbX6xEyxS3Nenv4oQCcWwmQc/D3dtaBdiyfyv5S1mM7zieLGSzWEsS6KIuiRSzTGTD9H404z1iy80MBWNVL7hn8nx8LhaxlqnKBvKbvyppYHctTM/0nd5xPljNwcrcUs2/6DyL0RwGYxDOcjR3vGBQ+4M8tMElmdQ3n6NGjYPofGE6F6tTESRmm/1xxTuuczWSQH08BhqJbqlNkuTyJoK2GBgf53S6mkrvB+7qZ6b9Gfod6hHv3dzKAGbX9pNvd51N7+WUBGNlJbgm07FH4G/AJdFuyqlwotXFzW8kzk2itXAFZwu38BlrlPN86v4ntnwIwqUvuASiAc+1r4MdkqXUef6tZM/0P8rcCTUmO9RvyraZiZiySf0OAQYCq46QiD0Lz7jYYFOxnARZPt7vZu/r1aREKzEz/Kd7J3rzAS2Si7IxHfq80nyQ7Mf20AIyfc2thBRyJv2XZwRmTJP14dNar3THlHl+2Nesl5M4vUpDjfe381vD+KgCTvuhehAI4yb4GfkzFjMdlXtWxV4+EvH/rlcytCutkASwzr59K+TsEGNpYZb0OQ4G5Q4OC/N4ln4PSuzXzdcuH6b8I4/7ZsERXZb49RhHQbwtgsGIFORWWwAuDP4P9HJBLsUpwWubrt1Yuh4zhmv5O3pLxUMaed367jsJQANPdW7AA5kAJrMl85+hMwC2RfEFnWcScuqJHoIRPx1xK2tmfKtPcc2kLEUX5YSgAIzHD/Rb/T48CSqbzUDkUVsAhmZTRTP8BuTiTskUllJMb8Bj6+qiySzufMOYABimq2mLMe/DzrweDgvx00iebyg6yuXstU/XrUZsQC/nu/5yMk13FLM5AjnAsAGsQW4M93tZiy+JA2qd+NVSmYzn0JfVPphRa0TkoOeTOvwqdf05Ind+ulLAUgNVomivj/7H2NehDMQnVq5/JRB1tjcJAxhRS1GC65Gvo/E9EnW3a+YWnAIxoyf0M/8Mei1o9bW2AbY5K++ivzvpPTVuMGMv/CR6/ZsviiqiyYSoAg1OSf8BQ4KmIOGUzG8XO8+UpK7qKfg5wsmGJxNNKr2C+5bh4sk4/17AmAYfzLOtf4PGgPR3YePipoH53YcVd0d2ZeJ3M9O+vrvUP8+7vsJOhIPvA9P9V4mwTKjBcC8AA5sWLkGK77Zua/Hbb2vLkMDu/XT8qZ4bc+a2KYSsAq6F5EXLyb/Y12EMx4HkbvgOSPCp6ODrIwUkWmWhZTn4BquckWmYKhYU9BBgEmhcvQk4OgNUT/3sFKjodE5C2zddWJoZ32DqLcTJz3ROl8Oo3pEbhWwBW2bx4EVK4FF+ik4a0bzxftbozMdTOr7Bs5uah89vFkQ8FYDWteREK3aR7l6yCR+E4j7Iehg7y2TiLSDnv8zFsvDtlGRIrPh9DgEGcefEiVMC7BbqdLYmO9gjf9H8UD1b3worS0DeVrb8u8mMBWJXNi5DgjS1Olq4nEOKXfrkarxjbPPKqaXUnYpimv8ibuC5wbeSn89v1kS8FYDUuuZfQ0KF7EdoaQ4HzrbqRHWU9FKb/IZHll7WMnHyx6mEqa3LFLE++hgBDYZZ1IS7oY4YGBfe9gF2RUfiprzkh+R14Zd8ZSXuNeBXG/Se2l9TvVPlVAH26GdZ5PY7m29bvJhxFeicvYyXbjh3vYCvrbej8h45Skr+nnDyNcf9fwfQP/72TdVopf0OAQQi1Pd1hexFSeQ+W6v5gsMptfdZM/zA7v8gKMDHXXrns/HY95FcBWO1nuMfw/xv2NeDjRHgQ2qet+tVM/wVtpfUhUZfMW7dc3AdpY5Ex3wrAkBblh/j/n7HQzUKmiilPkWukopu2Ic6lAY/7b8Gk3zVtMAkqCRWAeREaJ0ejm4TrRUjlvejI57V05ZrfQZXDWkrjS2Qnf5QJwT8Jaqo18jsJOBxPWQ/ABf/T4cHB/HaonWBrq62IHOtYplvCz4DN+k8fK6qH51dD5t0x/LNt4rk/aAEMXgKhexEaHAqobjJY5Yafy/H+gTA7v1X56+z877Q8LYB3WGD/t06AV+FfI2inocGBfb8IHeArDevUo7bO/18bnvf5hIOFV5SDMOtv1hAPEKACGH4Z9OgHEWTm4dh3yuFpffjtsJG3IB+t6+gibNP/T7IZtvhmzZV6ytcMhwDDG2CG+z2C5g8PDua34tHvWjwVUB3pJi1U099hNYTDK9bZ+UdcxlQAI5AgYIa7ChdMyF6EtpOKnLVB1Xv0YIz7D98gLJwfZ2Hy8/5wqhNdTTgEaMTSfOytkCdxeutGUbwOt6FAl+yBvQKPSMimv5MHMO7fF+P+fq/bKybhaQE0AruFewOnjoIlMNAoitfhNhTol2sxFJhYfctQmLP+r+FNUUey8ze+UmkBNGZTO1PW78A0PmOsaN6et0UxKn/urfyjCd4lB2K1X7hrO0are5PnqADGAqVawKPBBxHtI2NF5flMEbgQcznhTuZGhJoKoBmQZd0G0RbhThm/w81m5GGc0Qk4PMYtYrWfc7bqj8coBDgHMAqc9adqXoRy6TBiPQNfvjhZhnUO2ObNzt9Mk1EBNEPJ4hTdrZgQXNhsdMZLiYCTk7HI6YWUSveuWA4BWmky21I7UPUitF0ryRg3IQIOTzVKLtgXecZBkQqgVao9OgtJHsbfRq0mZfwYCTh5BuP+D8P0Ny8/PJokwCFAk6DWRzMvQi54L0Lrq+vJl7fRJubai52/xQajAmgRWDV6sfr2nXvbSco0sRD4KuZono4l58Az5RCg3Qbu0yJW0j2JR4MhOs1ol0ry6Zz8GOP+w5IvOIwSaQG0247TXQVJj4Xpyb3l7TLsNJ2Tl2QiXujBo20CVABto0NCexW3ysWdZMG0bRJwsgbKdLine truncated
|
||||||
|
webUI["html/img/xmltv.png"] = "iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAAXNSR0IArs4c6QAAAAlwSFlzAAAsSwAALEsBpT2WqQAABCRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IlhNUCBDb3JlIDUuNC4wIj4KICAgPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgICAgICAgICAgIHhtbG5zOmV4aWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vZXhpZi8xLjAvIgogICAgICAgICAgICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgICAgICAgICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyI+CiAgICAgICAgIDx0aWZmOlJlc29sdXRpb25Vbml0PjI8L3RpZmY6UmVzb2x1dGlvblVuaXQ+CiAgICAgICAgIDx0aWZmOkNvbXByZXNzaW9uPjU8L3RpZmY6Q29tcHJlc3Npb24+CiAgICAgICAgIDx0aWZmOlhSZXNvbHV0aW9uPjI4ODwvdGlmZjpYUmVzb2x1dGlvbj4KICAgICAgICAgPHRpZmY6T3JpZW50YXRpb24+MTwvdGlmZjpPcmllbnRhdGlvbj4KICAgICAgICAgPHRpZmY6WVJlc29sdXRpb24+Mjg4PC90aWZmOllSZXNvbHV0aW9uPgogICAgICAgICA8ZXhpZjpQaXhlbFhEaW1lbnNpb24+NTA8L2V4aWY6UGl4ZWxYRGltZW5zaW9uPgogICAgICAgICA8ZXhpZjpDb2xvclNwYWNlPjE8L2V4aWY6Q29sb3JTcGFjZT4KICAgICAgICAgPGV4aWY6UGl4ZWxZRGltZW5zaW9uPjUwPC9leGlmOlBpeGVsWURpbWVuc2lvbj4KICAgICAgICAgPGRjOnN1YmplY3Q+CiAgICAgICAgICAgIDxyZGY6QmFnLz4KICAgICAgICAgPC9kYzpzdWJqZWN0PgogICAgICAgICA8eG1wOk1vZGlmeURhdGU+MjAxOC0wNy0yOFQyMDowNzozMzwveG1wOk1vZGlmeURhdGU+CiAgICAgICAgIDx4bXA6Q3JlYXRvclRvb2w+UGl4ZWxtYXRvciAzLjM8L3htcDpDcmVhdG9yVG9vbD4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+Co6j9bsAAAGgSURBVGgF7VqxTsNADL0gYGEon8DOwsbAAH8BYmJn6cYIn8DC3h0+oixISFRiYYCJL0AwMMBAeK5w2rpV6uikxHfYknW98+vds18TK21DWGBlWR7CH+BfcEv2AzLP8HP42gLqkyUATuEp2PWEdQjF9ATs1zF/g29Mrxt+vVcUxR3xWxEktzFPJQmivsv8ZSI9DiQyVnxlIonwn6fpiczXpNuVVXH8O+a3Ys3y9NUyuf/NTTbEHZTjUlGSRzSiPhroUIENwB4AS/vS/susDwDhTpYBER9g7wHh5DWyibV9CiitCZbIafDEYUuJHQI3Nr/9ciWsjK6IFSWYhyvClbAyZqOIlYJG84jq7E1Ot97Zm+TinV1TrWwudk9EI3ebGFekzWprzspGEU2ySWCiOrs/s9dr7M/s9fVJJJrNXcsTsfaJc0WsKZINH+/sf1Jqvl1n1f2ZnStRN/pdq646XcSkIh9dkIg4s+IrE3nCpp8RG7f91ns+cCYR/LD4jcAZB42PN+A7/mcQ8ZxJhBYQvMJwBB/BKTFLVoLMC/wCfgyv7BesTKUC2LKM3wAAAABJRU5ErkJggg=="
|
||||||
|
webUI["html/js/configuaration.js"] = "dmFyIGNvbmZpZ01lbnUgPSBuZXcgT2JqZWN0KCk7CnZhciB3aXphcmQgPSBuZXcgQXJyYXkoImtleSIsICJ0dW5lciIsICJlcGdTb3VyY2UiLCAibTN1IiwgImNvbXBsZXRlIik7CnZhciBhY3RpdmVXaXphcmQ7CnZhciBkdnJJUAoKdmFyIGNvbmZpZ01lbnVfdHVuZXIgPSBuZXcgT2JqZWN0KCk7CmNvbmZpZ01lbnVfdHVuZXJbIl9lbGVtZW50Il0gICAgICA9ICJTRUxFQ1QiOwpjb25maWdNZW51X3R1bmVyWyJfbWVudVR5cGUiXSAgICAgPSAic2luZ2xlSW5wdXQiOwpjb25maWdNZW51X3R1bmVyWyJfY29uZmlnS2V5Il0gICAgPSAidHVuZXIiOwpjb25maWdNZW51X3R1bmVyWyJfbGFiZWwiXSAgICAgICAgPSAiQXZhaWxhYmxlIHR1bmVycyI7CmNvbmZpZ01lbnVfdHVuZXJbIm5hbWUiXSAgICAgICAgICA9ICJ0dW5lciI7CmNvbmZpZ01lbnVfdHVuZXJbImlkIl0gICAgICAgICAgICA9ICJUdW5lciI7CmNvbmZpZ01lbnVfdHVuZXJbInBsYWNlaG9sZGVyIl0gICA9ICJUdW5lciI7CmNvbmZpZ01lbnVfdHVuZXJbIl91c2FnZSJdICAgICAgICA9ICJUaGlzIHNldHRpbmcgaXMgb25seSB1c2VkIGJ5IFBsZXggYW5kIEVtYnkuPGJyPlRoZSBudW1iZXIgb2YgY29uY3VycmVudCBzdHJlYW1zIGFsbG93ZWQgYnkgdGhlIElQVFYgcHJvdmlkZXIuIgoKCnZhciBvcHRpb25WYWx1ZXMgPSBuZXcgQXJyYXkoKTsKZm9yICh2YXIgaSA9IDE7IGkgPD0gMTAwOyBpKyspIHsKICBvcHRpb25WYWx1ZXMucHVzaChpKQp9CmNvbmZpZ01lbnVfdHVuZXJbIl9vcHRpb25WYWx1ZXMiXSA9IG9wdGlvblZhbHVlczsKCnZhciBjb25maWdNZW51X2VwZyA9IG5ldyBPYmplY3QoKTsKY29uZmlnTWVudV9lcGdbIl9lbGVtZW50Il0gICAgICA9ICJTRUxFQ1QiOwpjb25maWdNZW51X2VwZ1siX21lbnVUeXBlIl0gICAgID0gInNpbmdsZUlucHV0IjsKY29uZmlnTWVudV9lcGdbIl9jb25maWdLZXkiXSAgICA9ICJlcGdTb3VyY2UiOwpjb25maWdNZW51X2VwZ1siX2xhYmVsIl0gICAgICAgID0gIlNlbGVjdGlvbiBvZiB0aGUgRVBHIHNvdXJjZSI7CmNvbmZpZ01lbnVfZXBnWyJuYW1lIl0gICAgICAgICAgPSAiZXBnU291cmNlIjsKY29uZmlnTWVudV9lcGdbImlkIl0gICAgICAgICAgICA9ICJFUEcgc291cmNlIjsKY29uZmlnTWVudV9lcGdbInBsYWNlaG9sZGVyIl0gICA9ICJFUEcgc291cmNlIjsKY29uZmlnTWVudV9lcGdbIl9vcHRpb25WYWx1ZXMiXSA9IG5ldyBBcnJheSgiUE1TIiwgIlhFUEciKTsKY29uZmlnTWVudV9lcGdbIl91c2FnZSJdICAgICAgICA9ICJQTVM6ICAgVXNlIEVQRyBkYXRhIGZyb20gUGxleCBvciBFbWJ5PGJyPlhFUEc6ICBVc2Ugb2YgZXh0ZXJuYWwgRVBHIGRhdGEgKFhNTFRWKTxicj4gICAgICAgU2V2ZXJhbCBYTUxUViBzb3VyY2VzIHBvc3NpYmxlPGJyPiAgICAgICBBbGxvd3MgZWRpdGluZyBhbmQgb3JkZXIgY2hhbm5lbHM8YnI+ICAgICAgIE0zVSAvIFhNTFRWIGV4cG9ydCAoSFRUUCBsaW5rIGZvciBJUFRWIGFwcHMpIgoKdmFyIGNvbmZpZ01lbnVfbTN1ID0gbmV3IE9iamVjdCgpOwpjb25maWdNZW51X20zdVsiX2VsZW1lbnQiXSAgICAgICAgPSAiSU5QVVQiOwpjb25maWdNZW51X20zdVsiX21lbnVUeXBlIl0gICAgICAgPSAiaW5wdXRBcnJheSI7CmNvbmZpZ01lbnVfbTN1WyJfY29uZmlnS2V5Il0gICAgICA9ICJmaWxlIjsKY29uZmlnTWVudV9tM3VbIl9sYWJlbCJdICAgICAgICAgID0gIk0zVSBGaWxlOiBsb2NhbCBvciByZW1vdGUiOwpjb25maWdNZW51X20zdVsibmFtZSJdICAgICAgICAgICAgPSAiZmlsZSI7CmNvbmZpZ01lbnVfbTN1WyJpZCJdICAgICAgICAgICAgICA9ICJtM3UiOwpjb25maWdNZW51X20zdVsidHlwZSJdICAgICAgICAgICAgPSAidGV4dCI7CmNvbmZpZ01lbnVfbTN1WyJwbGFjZWhvbGRlciJdICAgICA9ICJNM1UgRmlsZSI7CmNvbmZpZ01lbnVfbTN1WyJfdXNhZ2UiXSAgICAgICAgICA9ICJSZW1vdGUgcGxheWxpc3Q6IGh0dHA6Ly95b3VyLnByb3ZpZGVyLmNvbS9maWxlLm0zdTxicj5Mb2NhbCAgcGxheWxpc3Q6IC9wYXRoL3RvL2ZpbGUubTN1IgoKCmNvbmZpZ01lbnVfbTN1WyJ2YWx1ZSJdICAgICAgICAgICA9ICJodHRwOi8vd2Vic3J2LmxvY2FsOjgwODAva2FiZWwubTN1IjsKCnZhciBjb25maWdNZW51X2NvbXBsZXRlID0gbmV3IE9iamVjdCgpOwpjb25maWdNZW51X2NvbXBsZXRlWyJfZWxlbWVudCJdICAgICAgICA9ICJIMiI7CmNvbmZpZ01lbnVfY29tcGxldGVbIl9tZW51VHlwZSJdICAgICAgID0gImlucHV0QXJyYXkiOwpjb25maWdNZW51X2NvbXBsZXRlWyJfY29uZmlnS2V5Il0gICAgICA9ICJmaWxlIjsKY29uZmlnTWVudV9jb21wbGV0ZVsiX3RleHQiXSAgICAgICAgICAgPSAieFRlVmUgd2FzIHN1Y2Nlc3NmdWxseSBzZXQgdXAiOwpjb25maWdNZW51X2NvbXBsZXRlWyJuYW1lIl0gICAgICAgICAgICA9ICJjb21wbGV0ZSI7CmNvbmZpZ01lbnVfY29tcGxldGVbImlkIl0gICAgICAgICAgICAgID0gImNvbXBsZXRlIjsKY29uZmlnTWVudV9jb21wbGV0ZVsidHlwZSJdICAgICAgICAgICAgPSAidGV4dCI7CmNvbmZpZ01lbnVfY29tcGxldGVbImNsYXNzIl0gICAgICAgICAgID0gImNlbnRlciI7Cgpjb25maWdNZW51WyJ0dW5lciJdICAgICA9IGNvbmZpZ01lbnVfdHVuZXI7CmNvbmZpZ01lbnVbImVwZ1NvdXJjZSJdID0gY29uZmlnTWVudV9lcGc7CmNvbmZpZ01lbnVbIm0zdSJdICAgICAgID0gY29uZmlnTWVudV9tM3U7CmNvbmZpZ01lbnVbImNvbXBsZXRlIl0gID0gY29uZmlnTWVudV9jb21wbGV0ZTsKCmZ1bmN0aW9uIHJlYWR5Rm9yQ29uZmlndXJhdGlvbigpIHsKICB2YXIgZGF0YSA9IG5ldyBPYmplY3QoKTsKICBkYXRhWyJjbWQiXSA9ICJnZXRTZXJ2ZXJDb25maWciOwogIHhUZVZlKGRhdGEpOwogIHNob3dMb2FkaW5nU2NyZWVuKGZhbHNlKTsKfQoKZnVuY3Rpb24gY3JlYXRlQ29uZmlndXJhdGlvbihlbG0pIHsKCiAgYWN0aXZlV2l6YXJkID0gZWxtOwogIHZhciBpdGVtICA9IGNvbmZpZ01lbnVbZWxtXTsKCiAgdmFyIGRpdiAgID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoImNvbnRlbnQiKTsKICBkaXYuaW5uZXJIVE1MID0gIiI7CiAgZGl2LnNldEF0dHJpYnV0ZSgiZGF0YS1jb25maWdLZXkiLCBpdGVtWyJfY29uZmlnS2V5Il0pOwogIGRpdi5zZXRBdHRyaWJ1dGUoImRhdGEtbWVudVR5cGUiLCBpdGVtWyJfbWVudVR5cGUiXSk7CiAgCiAgc3dpdGNoKGl0ZW0uaGFzT3duUHJvcGVydHkoIl9sYWJlbCIpKSB7CiAgICBjYXNlIHRydWU6CiAgICAgIHZhciBuZXdJdGVtID0gbmV3IE9iamVjdCgpOwogICAgICBuZXdJdGVtWyJfZWxlbWVudCJdID0gIkxBQkVMIjsKICAgICAgbmV3SXRlbVsiX3RleHQiXSAgICA9IGl0ZW1bIl9sYWJlbCJdOyAKICAgICAgbmV3SXRlbVsiZm9yIl0gICAgICA9IGl0ZW1bImlkIl07CiAgICAgIGRpdi5hcHBlbmRDaGlsZChjcmVhdGVFbGVtZW50KG5ld0l0ZW0pKTsKICAgICAgYnJlYWsKICB9CgogIHN3aXRjaChpdGVtWyJfZWxlbWVudCJdKSB7CiAgICBjYXNlICJTRUxFQ1QiOgogICAgICBkaXYuYXBwZW5kQ2hpbGQoY3JlYXRlRWxlbWVudChpdGVtKSk7CiAgICAgIHZhciBzZWxlY3RFbGVtZW50ID0gZGl2LmdldEVsZW1lbnRzQnlUYWdOYW1lKCJTRUxFQ1QiKVswXTsKICAgICAgdmFyIHZhbHVlcyA9IGl0ZW1bIl9vcHRpb25WYWx1ZXMiXTsKICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCB2YWx1ZXMubGVuZ3RoOyBpKyspIHsKICAgICAgICBLine truncated
|
||||||
|
webUI["html/js/files.js"] = "ZnVuY3Rpb24gb3BlbkZpbGVzKGVsbSwgZmlsZVR5cGUpIHsKICAvL2RvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJzZXR0aW5ncyIpLmlubmVySFRNTCA9ICJUZXN0IjsKICAKICBjb2x1bW5Ub1NvcnQgPSAwOyAKICB2YXIgbmV3RGl2ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoInNldHRpbmdzIik7CiAgCiAgdmFyIG5ld0VudHJ5ID0gbmV3IE9iamVjdCgpOwogIG5ld0VudHJ5WyJfZWxlbWVudCJdICA9ICJIUiI7CiAgbmV3RGl2LmFwcGVuZENoaWxkKGNyZWF0ZUVsZW1lbnQobmV3RW50cnkpKTsKCiAgdmFyIG5ld0VudHJ5ID0gbmV3IE9iamVjdCgpOwogIG5ld0VudHJ5WyJfZWxlbWVudCJdICA9ICJJTlBVVCI7CiAgbmV3RW50cnlbInR5cGUiXSA9ICJidXR0b24iOwogIG5ld0VudHJ5WyJjbGFzcyJdID0gImJ1dHRvbiI7CiAgbmV3RW50cnlbInZhbHVlIl0gPSAiTmV3IjsKICBuZXdFbnRyeVsib25jbGljayJdID0gJ2ZpbGVEZXRhaWwoIi0iLCAiJyArIGZpbGVUeXBlICsgJyIpJzsKICBuZXdEaXYuYXBwZW5kQ2hpbGQoY3JlYXRlRWxlbWVudChuZXdFbnRyeSkpOwoKICB2YXIgbmV3RW50cnkgPSBuZXcgT2JqZWN0KCk7CiAgbmV3RW50cnlbIl9lbGVtZW50Il0gID0gIklOUFVUIjsKICBuZXdFbnRyeVsidHlwZSJdID0gImJ1dHRvbiI7CiAgbmV3RW50cnlbImNsYXNzIl0gPSAiYnV0dG9uIjsKICBuZXdFbnRyeVsidmFsdWUiXSA9ICJVcGRhdGUiOwogIG5ld0VudHJ5WyJvbmNsaWNrIl0gPSAiZmlsZURldGFpbCgwKSI7CiAgLy9uZXdEaXYuYXBwZW5kQ2hpbGQoY3JlYXRlRWxlbWVudChuZXdFbnRyeSkpOwoKICB2YXIgZGl2ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoInNldHRpbmdzIik7CgogICAvLyBCdWlsZCB0YWJsZQogIHZhciBuZXdUYWJsZSA9IG5ldyBPYmplY3QoKTsKICBuZXdUYWJsZVsiX2VsZW1lbnQiXSAgPSAiVEFCTEUiOwogIG5ld1RhYmxlWyJpZCJdICAgICAgICA9ICJpZF9tYXBwaW5nIjsKICBuZXdUYWJsZVsiY2xhc3MiXSAgICAgPSAidGFibGUtbWFwcGluZyI7CiAgZGl2LmFwcGVuZENoaWxkKGNyZWF0ZUVsZW1lbnQobmV3VGFibGUpKTsKCiAgc2V0VGltZW91dChmdW5jdGlvbigpeyAKICAgIGNyZWF0ZUZpbGVzVGFibGUoZmlsZVR5cGUpOyAKICB9LCAxMCk7Cgp9CgpmdW5jdGlvbiBjcmVhdGVGaWxlc1RhYmxlKGZpbGVUeXBlKSB7CiAgdmFyIHRhYmxlID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoImlkX21hcHBpbmciKTsKICB2YXIgYXZhaWxhYmxlRmlsZVR5cGVzID0gbmV3IEFycmF5KCk7CiAgCiAgdGFibGUuaW5uZXJIVE1MID0gIiI7CiAgdmFyIG5ld1RSID0gbmV3IE9iamVjdCgpOwogIG5ld1RSWyJfZWxlbWVudCJdID0gIlRSIjsKICBuZXdUUlsiY2xhc3MiXSAgICA9ICJ0YWJsZS1tYXBwaW5nLWhlYWRlciI7CiAgdGFibGUuYXBwZW5kQ2hpbGQoY3JlYXRlRWxlbWVudChuZXdUUikpOwoKICB2YXIgdHIgPSB0YWJsZS5sYXN0Q2hpbGQ7CgogIHN3aXRjaChmaWxlVHlwZSkgewogICAgY2FzZSAieG1sdHYiOiAKICAgICAgYXZhaWxhYmxlRmlsZVR5cGVzID0gbmV3IEFycmF5KCJ4bWx0diIpOyAKICAgICAgdmFyIHRySGVhZGxpbmVzID0gbmV3IEFycmF5KCJHdWlkZSIsICJMYXN0IFVwZGF0ZSIsICJBdmFpbGFiaWxpdHkgJSIsICJDaGFubmVscyIsICJQcm9ncmFtcyIpCiAgICAgIHZhciBjb21wYXRpYmlsaXR5S2V5cyA9IG5ldyBBcnJheSgieG1sdHYuY2hhbm5lbHMiLCAieG1sdHYucHJvZ3JhbXMiKQogICAgICBicmVhazsKCiAgICBjYXNlICJtM3UiOgogICAgICBhdmFpbGFibGVGaWxlVHlwZXMgPSBuZXcgQXJyYXkoIm0zdSIsICJoZGhyIik7IAogICAgICB2YXIgdHJIZWFkbGluZXMgPSBuZXcgQXJyYXkoIlBsYXlsaXN0IiwgIkxhc3QgVXBkYXRlIiwgIkF2YWlsYWJpbGl0eSAlIiwgIlR5cGUiLCAiU3RyZWFtcyIsICJncm91cC10aXRsZSAlIiwgInR2Zy1pZCAlIiwgIlVuaXF1ZSBJRCAlIik7CiAgICAgIHZhciBjb21wYXRpYmlsaXR5S2V5cyA9IG5ldyBBcnJheSgic3RyZWFtcyIsICJncm91cC50aXRsZSIsICJ0dmcuaWQiLCAic3RyZWFtLmlkIik7CiAgICAgIGJyZWFrOwogIH0KCiAgZm9yICh2YXIgaSA9IDA7IGkgPCB0ckhlYWRsaW5lcy5sZW5ndGg7IGkrKykgewogICAgdmFyIG5ld1REID0gbmV3IE9iamVjdCgpOwogICAgbmV3VERbIl9lbGVtZW50Il0gPSAiVEQiOwogICAgbmV3VERbIl90ZXh0Il0gICAgPSB0ckhlYWRsaW5lc1tpXTsKICAgIHRyLmFwcGVuZENoaWxkKGNyZWF0ZUVsZW1lbnQobmV3VEQpKTsKICB9CiAgCiAgZm9yICh2YXIgaSA9IDA7IGkgPCBhdmFpbGFibGVGaWxlVHlwZXMubGVuZ3RoOyBpKyspIHsKICAgIAogICAgdmFyIGZpbGVUeXBlID0gYXZhaWxhYmxlRmlsZVR5cGVzW2ldCgogICAgdmFyIGRhdGEgPSBjb25maWdbImZpbGVzIl1bZmlsZVR5cGVdOwogICAgCiAgICB2YXIgYWxsRmlsZXMgPSBnZXRPYmpLZXlzKGRhdGEpCiAgCiAgICBmb3IgKHZhciBmID0gMDsgZiA8IGFsbEZpbGVzLmxlbmd0aDsgZisrKSB7CiAgICAgIHZhciBlbG0gICAgICAgICAgID0gZGF0YVthbGxGaWxlc1tmXV07CiAgICAgIHZhciB0YWJsZSAgICAgICAgID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoImlkX21hcHBpbmciKTsKICAgICAgdmFyIGZpbGVJRCAgICAgICAgPSBlbG1bImlkLnByb3ZpZGVyIl07CiAgICAgIHZhciBuYW1lICAgICAgICAgID0gZWxtWyJuYW1lIl07CiAgICAgIHZhciBsYXN0VXBkYXRlICAgID0gZWxtWyJsYXN0LnVwZGF0ZSJdOwogICAgICB2YXIgYXZhaWxhYmlsaXR5ICA9IGVsbVsicHJvdmlkZXIuYXZhaWxhYmlsaXR5Il07CiAgICAgIHZhciB0eXBlICAgICAgICAgID0gZWxtWyJ0eXBlIl0udG9VcHBlckNhc2UoKTsKICAgICAgdmFyIGNvbXBhdGliaWxpdHkgPSBlbG1bImNvbXBhdGliaWxpdHkiXTsKCiAgICAgIC8vIENyZWF0ZSBUUgogICAgICB2YXIgbmV3VFIgPSBuZXcgT2JqZWN0KCk7CiAgICAgIG5ld1RSWyJfZWxlbWVudCJdICAgICAgID0gIlRSIjsKICAgICAgbmV3VFJbImNsYXNzIl0gICAgICAgICAgPSAiIjsKICAgICAgbmV3VFJbImlkIl0gICAgICAgICAgICAgPSBmaWxlSUQ7CiAgICAgIG5ld1RSWyJvbmNsaWNrIl0gICAgICAgID0gJ2phdmFzY3JpcHQ6IGZpbGVEZXRhaWwoIicgKyBmaWxlSUQgKyAnIiwiJyArIGZpbGVUeXBlICsgJyIpOyc7CiAgICAgIHRhYmxlLmFwcGVuZENoaWxkKGNyZWF0ZUVsZW1lbnQobmV3VFIpKTsKCiAgICAgIHZhciB0ciA9IHRhYmxlLmxhc3RDaGlsZDsKCiAgICAgIC8vIENyZWF0ZSBmaWxlIG5hbWUgVEQKICAgICAgdmFyIG5ld1REID0gbmV3IE9iamVjdCgpOwogICAgICBuZXdURFsiX2VsZW1lbnQiXSA9ICJQIjsKICAgICAgbmV3VERbIl90ZXh0Il0gICAgPSBuYW1lOwogICAgICBjcmVhdGVOZXdURChuZXdURCwgdHIpOwoKICAgICAgLy8gQ3JlYXRlIGxhc3QgdXBkYXRlIFRECiAgICAgIHZhciBuZXdURCA9IG5ldyBPYmplY3QoKTsKICAgICAgbmV3VERbIl9lbGVtZW50Il0gPSAiUCI7CiAgICAgIG5ld1REWyJfdGV4dCJdICAgID0gbGFzdFVwZGF0ZTsKICAgICAgY3JlYXRlTmV3VEQobmV3VEQsIHRyKTsKCiAgICAgIC8vIENyZWF0ZSBhdmFpbGFiaWxpdHkgVEQKICAgICAgdmFyIG5ld1REID0gbmV3IE9iamVjdCgpOwogICAgICBuZXdURFsiX2VsZW1lbnQiXSA9ICJQIjsKICAgICAgbmV3VERbIl90ZXh0Il0gICAgPSBhdmFpbGFiaWxpdHk7CiAgICAgIGNyZWF0ZU5ld1REKG5ld1RELine truncated
|
||||||
|
webUI["html/img/BC-QR.jpg"] = "/9j/4AAQSkZJRgABAQAASABIAAD/4QBYRXhpZgAATU0AKgAAAAgAAgESAAMAAAABAAEAAIdpAAQAAAABAAAAJgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAuqADAAQAAAABAAAAugAAAAD/7QA4UGhvdG9zaG9wIDMuMAA4QklNBAQAAAAAAAA4QklNBCUAAAAAABDUHYzZjwCyBOmACZjs+EJ+/8AAEQgAugC6AwERAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/bAEMAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAf/bAEMBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAf/dAAQAGP/aAAwDAQACEQMRAD8A/v4oAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgD/0P7+KAP5/wD/AILmf8FzP+HL/wDwy8B+y9/w0mP2k/8AhdmB/wALsPwe/wCEL/4U8fhLn5j8Jfih/b//AAkH/C0fTRP7F/sL/mIjUQtkAfgH/wARzn/WLz/zdb/8k2gA/wCI5z/rF5/5ut/+SbQAf8Rzn/WLz/zdb/8AJNoAP+I5z/rF5/5ut/8Akm0Af08/8EWP+CrP/D4H9lrx9+0uPgMf2ef+EI+Pvin4GjwSPiofi1/aX/CN/Dn4W+Pj4n/4SH/hXXww+xm8PxOGkf2J/Yl19k/sU3/9sv8A2h9isAD3/wD4KiftzH/gmz+wt8cv21B8Lv8Ahc4+C5+Ghb4a/wDCbD4cf8JJ/wALG+MPgD4TjHjFfB/jwaSdJPjv/hISD4X1L7f/AGadNLWH24ahaAHwH/wQz/4Lmf8AD6D/AIahB/Ze/wCGbB+zZ/wpPI/4XYfjD/wmn/C4T8WsfMPhL8L/AOwP+Ef/AOFXemt/21/bv/MOGnFb0A/oAoAKAP5Af+Cov/B1h/w7Z/bq+OX7FZ/YOHxpPwWHwyP/AAsr/hqA/Dk+Iz8Rvg94B+LH/Inf8M7ePf7J/sr/AITv/hHcjxPqAvTp/wDaObEX32GyAPgH/iOc/wCsXn/m63/5JtAH9PP/AARY/wCCrP8Aw+B/Za8fftLj4DH9nn/hCPj74p+Bo8Ej4qH4tf2l/wAI38Ofhb4+Pif/AISH/hXXww+xm8PxOGkf2J/Yl19k/sU3/wDbL/2h9isAD3//AIKiftzH/gmz+wt8cv21B8Lv+Fzj4Ln4aFvhr/wmw+HH/CSf8LG+MPgD4TjHjFfB/jwaSdJPjv8A4SEg+F9S+3/2adNLWH24ahaAH8g//Ec5/wBYvP8Azdb/APJNoA/r7/4bm/41af8ADy3/AIVf/wA2B/8ADc3/AApb/hNTt/5N1/4X/wD8Kv8A+Fj/APCH4zj/AIpH/hOP+EEx/wAx7/hE8/8AEjoA/kE/4jnP+sXn/m63/wCSbQAf8Rzn/WLz/wA3W/8AyTaAD/iOc/6xef8Am63/AOSbQAf8Rzn/AFi8/wDN1v8A8k2gD7+/4Jdf8HWH/Dyb9ur4G/sVj9g4fBY/GkfE0/8ACyv+GoD8Rj4cPw5+D3j74sf8id/wzt4C/tb+1f8AhBP+EdyfE+niyOof2jm+Fj9hvQD+v6gAoA//0f7+KAP4A/8Ag+c/5xef93rf++lUAfv3/wAE9v8Agnx/wS0/4da/sP8Ax0+Of7D/AOwE279gL9mr4s/GL4x/Fn9mn9nb5f8AjHbwZ4w+IHxI+JPxB8YeC1AAA1fxN4x8Y+JtXJ/4/wDXNdv2zeXtAC/8csn/AFgD/wDOd1AB/wAcsn/WAP8A853UAfjF/wAHcn7Ef7GP7Nv/AATc+Cnjn9nX9kb9l74A+NtU/bd+HHhPVPGPwX+APwp+FfinU/C158CP2ldX1DwxfeIPA3g/Q9Vu9Bu9V0PQ9XutGurttOvNR0TTb5rU3lhYlQD6h/4Mqv8AlFj8e/8As/74p/8ArO37K1AH8of/AAWp/wCHzv8Aw0H/AMFGf+Fwf8PPP+HfH/DX3xy/sf8A4WX/AMNXf8Mcf8Ks/wCGltU/4Ub/AGf/AMJR/wAWS/4QH+0v+EB/4VV9n/4kP2//AIRH/hEv9M/sOgD9Pf8Agzc/aw/Za/Zg/wCHjQ/aX/aU+AX7Pf8AwnH/AAx+fBP/AAvL4yfDn4Snxd/wjK/tP/8ACQ/8IuPH/iXQRr39g/2/4dXWxo7Xv9jHXNG+3G1/tCydgD8Zf23P+Cmn/BSHx7/wUi/a68D/ALKf/BQT9t3xl4K8Zftu/H7wn+zd4N/Z6/at+PHiLwt4t8LeI/jx4o0f4PeGPgf4f+G/ju80rXdC13Sbvw5pXw20jwJZ3enalYXmhWHhSyezewRwDd/46m/+s/n/AJ0RoA/u3/4I1f8ABPj4W/HH/gm5+zf8Uf8Agp3+xB8PvjB+3J4n/wCFv/8AC7/iL+3p+zX4d+IP7VviI6N8d/ihoHw3X4reLv2hfBmq/GLVRpXwe0vwHofgX/hML+6+wfDjT/CGn6AI/CtjoaUAf5V/wV/Z++Pf7SXirUPAv7OfwO+L/wAf/G2l+HrzxXqng74L/DXxl8U/FWneFrO/0vSL/wAU3/h/wNouu6raaFZ6tr+jaTdaxd2f9n2eoa1pli12t5f2SsAf6eX/AAaN/s+fH39m3/gm98avA37RXwP+LvwA8bar+298RvFel+DfjT8NvGXwr8Vap4Vu/gP+zVo9j4nsfD/jnRNC1W70G91XQ9a0q11m2szp13qOi6jYpeC8sL1VAP248WftA/8ABN/9qbxX4k/Yk8c/HL9iH9ovxtrXiDV/CfjL9kTxZ8SvgP8AF7xXrPir4Tahc+KfEPhjxP8AALWNb8QarqOv/DfVvA194m1jRtV8Itf+C9Q8I3euX1lp15oD3lgAY/8Aw6e/4JZ/9I1P2Av/ABDr9nb/AOdvQBzP/BTTwn4X8B/8EjP+CgngTwN4a0LwX4I8Gf8ABOX9q7wl4O8HeE9H07w14V8K+FfDf7M/jzRfDvhjwx4f0a0tdK0Lw9oel2tnpGi6NpNnZ2FhYWVnY2NnFZRolAH+eX/waN/s+fAL9pL/AIKRfGrwN+0V8D/hF8fvBOlfsQ/EXxXpfg340/Dbwb8VPCul+KrT48fs1aPY+J7Hw/450TXdJtNes9K1zWtKtdZtrMajZ6drWo2KXhs7+9VgD+5b4o/Cf/g2++B/jnXPhb8avht/wRE+EPxN8LjTR4n+HfxR8IfsIeAPHfhsa1pGm+IdF/t7wj4q03SvEGk/2poGq6PrWlC/sLQXuj6lp2o2O+yvrN2AOC/45ZP+sAf/AJzuoA+nPgr+xF/wQ5/aT8K6h46/Zz/ZH/4JSfH/AME6X4gvfCepeMfgv8A/2Qfin4V0zxVZ6fpOr33he+8Q+BvCGvaVZ69aaVruj6vd6PdXYv7Sw1vTL6SzFnfWTMAfwm/8EqvCfhPwB/wd6eIfA3gbw34f8F+CfBf7b3/BUbwn4O8HeEtGsPDvhXwp4W8OfDj9r3SfD/hjwzoGj21ppWgaFoOlWdnpGkaNpNnZ2Gn6daWdhY2UVmqKoB/qLUAFAH//0v7+KAP4A/8Ag+c/5xef93rf++lUAfv5/wA6sn/eAP8A+B3UAf5A9ABQB/p+f8Hqv/KLH4Cf9n/fCz/1nb9qmgA/4Mqv+UWPx7/7P++Kf/rO37K1AH5Bf8F9P+Dj4ftEfDz/AIKDf8Elj+xt/wAIePD3x/1f4F/8L/P7Q518Xn/DL/7U+g6z/wAJSfhUPghof2b/AITo/CT7J/Y3/CxpP+EX/t8v/bXiAabsvwD8gP8Aghn/AMEM/wDh9B/w1CT+1D/wzYf2bP8AhSfP/Ckx8Yf+E0/4XCPizj5R8Wfhf/YH/CPj4Xeut/21/bv/ADDjpxa/APA/2TvgYP2X/wDgvh+zT+zSPFX/AAnA/Z4/4K+fBz4Gf8Jt/Yn/AAjf/CXn4S/tneH/AAAPFH/COnU9e/sA6/8A2AdX/sca5rv9jm8Ww/trUDF9tlAP9nSgD+QH/gqL/wAHWH/Dtn9ur45fsVn9g4fGk/BYfDI/8LK/4agPw5PiM/Eb4PeAfix/yJ3/AAzt49/sn+yv+E7/AOEdyPE+oC9On/2jmxF99hsgD+Ij/gix/wAFWf8Ahz9+1L4+/aXHwG/4aG/4Tf4BeKfgaPBI+Kg+Ev8AZv8AwknxG+Fvj4+J/wDhIf8AhXXxP+2fYx8MRpH9if2Ja/a/7aN//bKf2f8AYr8A/p5/4jnP+sXn/m63/wCSbQAf8MNf8O8v+OtP/haH/C4P+Fgf8Zy/8MA/8IV/wr/+x/Line truncated
|
||||||
|
webUI["html/img/x_black.png"] = "iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAIAAADTED8xAAAAAXNSR0IArs4c6QAAAAlwSFlzAAAWJQAAFiUBSVIk8AAAA6ppVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IlhNUCBDb3JlIDUuNC4wIj4KICAgPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIgogICAgICAgICAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgICAgICAgICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iPgogICAgICAgICA8eG1wOk1vZGlmeURhdGU+MjAxOC0xMS0xNVQxNzoxMTozNzwveG1wOk1vZGlmeURhdGU+CiAgICAgICAgIDx4bXA6Q3JlYXRvclRvb2w+UGl4ZWxtYXRvciAzLjM8L3htcDpDcmVhdG9yVG9vbD4KICAgICAgICAgPHRpZmY6T3JpZW50YXRpb24+MTwvdGlmZjpPcmllbnRhdGlvbj4KICAgICAgICAgPHRpZmY6Q29tcHJlc3Npb24+NTwvdGlmZjpDb21wcmVzc2lvbj4KICAgICAgICAgPHRpZmY6UmVzb2x1dGlvblVuaXQ+MjwvdGlmZjpSZXNvbHV0aW9uVW5pdD4KICAgICAgICAgPHRpZmY6WVJlc29sdXRpb24+MTQ0PC90aWZmOllSZXNvbHV0aW9uPgogICAgICAgICA8dGlmZjpYUmVzb2x1dGlvbj4xNDQ8L3RpZmY6WFJlc29sdXRpb24+CiAgICAgICAgIDxleGlmOlBpeGVsWERpbWVuc2lvbj4yNTY8L2V4aWY6UGl4ZWxYRGltZW5zaW9uPgogICAgICAgICA8ZXhpZjpDb2xvclNwYWNlPjE8L2V4aWY6Q29sb3JTcGFjZT4KICAgICAgICAgPGV4aWY6UGl4ZWxZRGltZW5zaW9uPjI1NjwvZXhpZjpQaXhlbFlEaW1lbnNpb24+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgoZIUJ+AAAXy0lEQVR4Ae2dCbhO1RrH/+Z5noUolDFjSBkuTsbUEXUoVG5JXZVLw71pvLqSBokGTzqkSAMhHZwyJFTITB2ZT6LIPA/3Xb6cew7f8U17WGvv/3o8j+/b315rve9vvevsd++93ncBLCRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiSQjkCWdJ8d/9i1J668yvFeo+3wh2/x1cxoKzter3RZ9H7A8V6j7fD1F3DkcLSVY6qXPabaMVauXR/39I+xDeeqHzqAljWRut25HmPpadjbaN0xlgacq7t9M0YPc667jD1lzfjV2W9DHsP6Vc52GUNv+QvipXdiqO9g1VvvNMb6T51Ev+44sN9BOhm6cnUCHDuG+xNw7GgGiXT+0qItbr9LZwGVbKXK4PkRuguZJt+wwVi2JO2b8x+yOd9lhh73/I69f6CNIRdrEb1Jc3w6AYcOZtBCqy+jP0T1a7SSKFNh5iXh8X6Z/urID25PAFFy1TI1YFWqOaJvzJ3kyo3KV2HKhzE3ZE8DXe7Ag4/b07TVre7eiYS2bt37pimjwQQQWb6Zg/geKFAwTSytP1xRFds2Y91K7YQU52f8DOTOo51gFwt09gz6dMH61Rf/4vARV+8B0nT9cy8evAMCxZTy3GvK1datvPgWChXRTajg8owcigXJwX9y9qgeVwDRecdW5MyJRs2cVT/a3uSv7BVVMHVStPVtqCfOzz+esKFdG5qUNyoP98YZLf7eaTMBhPOSBWjWBmXL24DchiblFd6WjTpcxJVuJUtj/HQznJ99e5EQh31/2jAk0TSphwsUkPzUKTzQA/K+yZTy3AiUKKWFsOL8FC6qhSQhhRjYBzu2hTzLsRN0mgCitNxcPtbXMeVj7ahIMQwdHWsjsdfv0gM3do69GSdaSByFmVOc6CjsPnRygQJCb1iDCpVQo07YKrh6ojy93bgBP611TQjl/Bjy5GftCtzXDadPu8YqWMeuLoYLJpA6li8/5vyIipUz+12v4/Iir3l1yBs9V8rYKWh7sys9R9bpkUNo2wAbf4qslv1na+YCBRQ+fEgtkZBVIkaUosXx31HuSBrf3QzrFzr/elBD6xe59HOBAqa061ccP6YeChlRqtZQXtDP6xwVVpyfcdORJ6+jnUbX2SfjMfyZ6KraXUtLFyigdJYsmDjLmDmwZzda1MCeP+wesP+3b4rz88tPyvmRq7qWRUsXKEDq7Fk81At7XfKtIx2tYiXxn5GRVor+/FsSzHB+ThxHvwRtrV/46+oCBUxD/myIX3FL9+gNxcmaV9dU4Q0pG2zvU14+yJMfI5yfZwYg6XPbgcTQgd4TQBTblKJe8dRrFIOODlaVxdIfvWd7hMPI91GrnoNaRdtV0lTIBNC7aHwPkAYuVy7M/B7Vaqcd0PrDZx+ohX32lZtvx+iJ9jVvWcup2xBXF7LMUe9iwgQQglWrI2mpGWtdRNq7OmPWNFvGXZyfeWshb6A1L6dP4daW+G6h5mKKeBrfBKeHJ3cCTz+S/oDWn9XKHHuWJcvKC/2tX8bm5WeNsH6RVPt7gDRLV4Fjtc0IHMtXQEULfDk1TXZrPnS+DQ8PtqYpW1tZ+BUG3Qt5iGdCMcQFCqAsUhTJK1GmnAlggTs7WJlHqHhJ5fzIW2fNi7wPaV0Hu3ZqLmaaeIa4QAF5zQockxwqBQulgY71gzg/+ls/zqJ/L4OsXwbFHBcoYEESOJYjBxqbEDgmeYTkz7Yld8M3dcMjT8U6hRyo/+ZwjHvTgX4s7MIoFyigd/bsmLIA9ZtYSMHGpnq0xdxZMbVvivOzfAluaYaThixhPD8kBk4AEb18RSSvQAHrHIzzOKz//9ftKqHiwQPRtzzmY3S4NfrqztQ8sA9x9VQ8k2nFNBcowFdw79higFmItDJLxXefMz1Kw+jUFQOejrKuk9X694SEuhtYzJwAAloCx+Q6YETgmOQAFuPYuili8yhWAhO+QJ58EVd0uMKEdzDqRYf7tKo7M12ggPYSODZ7OSpVsYqFje2kbkWLmhEvinxnMjp2tVEqS5resBodGuGoOQleM2pt1GPQjKIrezIlcOyyy/HUSxeIH+KrOD/6W//RI+h7u7nWL0NgrAsUMB954SLJpZvHhTAmHX6+pr5KfLR9S1iymOL8PHE/5s0OSyNdTzLZBQowNShwTHaCaFkrrHSwb3+ETt10tZnzck2dqFL7G15MdoEC6GXNiTyCMCJwrHwl/HtoaIPpeKsB1r/1F5MyOGUO3fwrQEC31h1UkJQB5Sy6tMTi+ZlKWqy4WvMjAZY6l5MncFNTrFyqs4xhymb+FSCgaPIXePf1MHV29bQseOVd5M08lcOQN3S3fsH3whPesH5RxfCb4PS2vGgu4jqhROn0x3T8LBGeefNjblIQ2Tp0waDnghzX6lDyDAx+SCuJYhHGKy5QgIEkKpTAMf2jxWUnhPjmF4aMGOH8/JaKNnUczf4Si3WHUdcrLlBA1ZT1ZgSOZcmKl99Fnoxbuejv/Jw5rcKdncx9FIYFx3iKh1ygAAlTAsckslEmQNpD9PbxBjg/rz6PjxJjNDjdqnvLBQrQlXhcCRzTf6MNcYQ6X4+li2GE87NkPrq20i23c+zTyVsuUICH7D5ixI5j4gi9Mha5c6uUcpo/95QM2LJ3iWaZzWO3fmnBcy5QgIoEjkncTOPmljCysRFZKX1zggE7o/W9DSuX2cjBvaa96AIFaGbLpgLHGlznHluv9DzmNTMeLUTF27sTQHAYFDgW1eA5UWnVUvXS98QJJ/pyow+PukABlBI4JuvPZGkNS3QEZMPC2+Nc2/wmOpkjrOXpCSAsZN+KcpejZt0IsfD0cwQG3H2pZUuegORpFygwQnnzqcCxK6p6YrwcVGLSWAy4x8H+3OnKBxNAwEpU7vRFyJHTHcYm9pqyDu0a4sgRE2WPSGavu0ABGAYFjkU0ejadLEF23dtiZ6pNzWvVrD8mgCBftgQNmqDilVrR11SYJ/tbmdVUUyX/EssfLlBAWdlW8auVur9zdd1cZnyMe7WPxrSOkp8mgFBr1R7vS+CYz7QO31x2bEGbuti/L/wapp/pGxcoMFCbU1CoCOo1Nn3YbJFfdia/syM2b7SlcV0b9eJiuEuzHvIY1q649Ck+/XXYYHWn5LPiS2egytVIWmZA4JiTtjhvFnq0M2VbFwvB+MwFCpCTxb2SRqVNJws5mt3U778h4cawEhaZrWcQ6X05AYTDquWoVgtVqgdB4rdDEpdzTzzWr/ab3gF9/XcPkDbOA/8OSd7PMnIoFiT7FoMv7wHSRrvxDfhkLrL69TIoHCRve5cWOHUqDYnfPvh47GWod2yDxM000T5wzCar3LcXCXGQLine truncated
|
||||||
|
webUI["html/js/base_ts.js"] = "dmFyIFNFUlZFUiA9IG5ldyBPYmplY3QoKTsKdmFyIEJVTEtfRURJVCA9IGZhbHNlOwp2YXIgQ09MVU1OX1RPX1NPUlQ7CnZhciBTRUFSQ0hfTUFQUElORyA9IG5ldyBPYmplY3QoKTsKdmFyIFVORE8gPSBuZXcgT2JqZWN0KCk7CnZhciBTRVJWRVJfQ09OTkVDVElPTiA9IGZhbHNlOwp2YXIgV1NfQVZBSUxBQkxFID0gZmFsc2U7Ci8vIE1lbsO8CnZhciBtZW51SXRlbXMgPSBuZXcgQXJyYXkoKTsKbWVudUl0ZW1zLnB1c2gobmV3IE1haW5NZW51SXRlbSgicGxheWxpc3QiLCAie3subWFpbk1lbnUuaXRlbS5wbGF5bGlzdH19IiwgIm0zdS5wbmciLCAie3subWFpbk1lbnUuaGVhZGxpbmUucGxheWxpc3R9fSIpKTsKLy9tZW51SXRlbXMucHVzaChuZXcgTWFpbk1lbnVJdGVtKCJwbXNJRCIsICJ7ey5tYWluTWVudS5pdGVtLnBtc0lEfX0iLCAibnVtYmVyLnBuZyIsICJ7ey5tYWluTWVudS5oZWFkbGluZS5wbXNJRH19IikpCm1lbnVJdGVtcy5wdXNoKG5ldyBNYWluTWVudUl0ZW0oImZpbHRlciIsICJ7ey5tYWluTWVudS5pdGVtLmZpbHRlcn19IiwgImZpbHRlci5wbmciLCAie3subWFpbk1lbnUuaGVhZGxpbmUuZmlsdGVyfX0iKSk7Cm1lbnVJdGVtcy5wdXNoKG5ldyBNYWluTWVudUl0ZW0oInhtbHR2IiwgInt7Lm1haW5NZW51Lml0ZW0ueG1sdHZ9fSIsICJ4bWx0di5wbmciLCAie3subWFpbk1lbnUuaGVhZGxpbmUueG1sdHZ9fSIpKTsKbWVudUl0ZW1zLnB1c2gobmV3IE1haW5NZW51SXRlbSgibWFwcGluZyIsICJ7ey5tYWluTWVudS5pdGVtLm1hcHBpbmd9fSIsICJtYXBwaW5nLnBuZyIsICJ7ey5tYWluTWVudS5oZWFkbGluZS5tYXBwaW5nfX0iKSk7Cm1lbnVJdGVtcy5wdXNoKG5ldyBNYWluTWVudUl0ZW0oInVzZXJzIiwgInt7Lm1haW5NZW51Lml0ZW0udXNlcnN9fSIsICJ1c2Vycy5wbmciLCAie3subWFpbk1lbnUuaGVhZGxpbmUudXNlcnN9fSIpKTsKbWVudUl0ZW1zLnB1c2gobmV3IE1haW5NZW51SXRlbSgic2V0dGluZ3MiLCAie3subWFpbk1lbnUuaXRlbS5zZXR0aW5nc319IiwgInNldHRpbmdzLnBuZyIsICJ7ey5tYWluTWVudS5oZWFkbGluZS5zZXR0aW5nc319IikpOwptZW51SXRlbXMucHVzaChuZXcgTWFpbk1lbnVJdGVtKCJsb2ciLCAie3subWFpbk1lbnUuaXRlbS5sb2d9fSIsICJsb2cucG5nIiwgInt7Lm1haW5NZW51LmhlYWRsaW5lLmxvZ319IikpOwptZW51SXRlbXMucHVzaChuZXcgTWFpbk1lbnVJdGVtKCJsb2dvdXQiLCAie3subWFpbk1lbnUuaXRlbS5sb2dvdXR9fSIsICJsb2dvdXQucG5nIiwgInt7Lm1haW5NZW51LmhlYWRsaW5lLmxvZ291dH19IikpOwovLyBLYXRlZ29yaWVuIGbDvHIgZGllIEVpbnN0ZWxsdW5nZW4KdmFyIHNldHRpbmdzQ2F0ZWdvcnkgPSBuZXcgQXJyYXkoKTsKc2V0dGluZ3NDYXRlZ29yeS5wdXNoKG5ldyBTZXR0aW5nc0NhdGVnb3J5SXRlbSgie3suc2V0dGluZ3MuY2F0ZWdvcnkuZ2VuZXJhbH19IiwgInh0ZXZlQXV0b1VwZGF0ZSx0dW5lcixlcGdTb3VyY2UsYXBpIikpOwpzZXR0aW5nc0NhdGVnb3J5LnB1c2gobmV3IFNldHRpbmdzQ2F0ZWdvcnlJdGVtKCJ7ey5zZXR0aW5ncy5jYXRlZ29yeS5maWxlc319IiwgInVwZGF0ZSxmaWxlcy51cGRhdGUsdGVtcC5wYXRoLGNhY2hlLmltYWdlcyx4ZXBnLnJlcGxhY2UubWlzc2luZy5pbWFnZXMiKSk7CnNldHRpbmdzQ2F0ZWdvcnkucHVzaChuZXcgU2V0dGluZ3NDYXRlZ29yeUl0ZW0oInt7LnNldHRpbmdzLmNhdGVnb3J5LnN0cmVhbWluZ319IiwgImJ1ZmZlcixidWZmZXIuc2l6ZS5rYixidWZmZXIudGltZW91dCx1c2VyLmFnZW50IikpOwpzZXR0aW5nc0NhdGVnb3J5LnB1c2gobmV3IFNldHRpbmdzQ2F0ZWdvcnlJdGVtKCJ7ey5zZXR0aW5ncy5jYXRlZ29yeS5iYWNrdXB9fSIsICJiYWNrdXAucGF0aCxiYWNrdXAua2VlcCIpKTsKc2V0dGluZ3NDYXRlZ29yeS5wdXNoKG5ldyBTZXR0aW5nc0NhdGVnb3J5SXRlbSgie3suc2V0dGluZ3MuY2F0ZWdvcnkuYXV0aGVudGljYXRpb259fSIsICJhdXRoZW50aWNhdGlvbi53ZWIsYXV0aGVudGljYXRpb24ucG1zLGF1dGhlbnRpY2F0aW9uLm0zdSxhdXRoZW50aWNhdGlvbi54bWwsYXV0aGVudGljYXRpb24uYXBpIikpOwpmdW5jdGlvbiBzaG93UG9wVXBFbGVtZW50KGVsbSkgewogICAgdmFyIGFsbEVsZW1lbnRzID0gbmV3IEFycmF5KCJwb3B1cC1jdXN0b20iKTsKICAgIGZvciAodmFyIGkgPSAwOyBpIDwgYWxsRWxlbWVudHMubGVuZ3RoOyBpKyspIHsKICAgICAgICBzaG93RWxlbWVudChhbGxFbGVtZW50c1tpXSwgZmFsc2UpOwogICAgfQogICAgc2hvd0VsZW1lbnQoZWxtLCB0cnVlKTsKICAgIHNldFRpbWVvdXQoZnVuY3Rpb24gKCkgewogICAgICAgIHNob3dFbGVtZW50KCJwb3B1cCIsIHRydWUpOwogICAgfSwgMTApOwogICAgcmV0dXJuOwp9CmZ1bmN0aW9uIHNob3dFbGVtZW50KGVsbUlELCB0eXBlKSB7CiAgICB2YXIgY3NzQ2xhc3M7CiAgICBzd2l0Y2ggKHR5cGUpIHsKICAgICAgICBjYXNlIHRydWU6CiAgICAgICAgICAgIGNzc0NsYXNzID0gImJsb2NrIjsKICAgICAgICAgICAgYnJlYWs7CiAgICAgICAgY2FzZSBmYWxzZToKICAgICAgICAgICAgY3NzQ2xhc3MgPSAibm9uZSI7CiAgICAgICAgICAgIGJyZWFrOwogICAgfQogICAgZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoZWxtSUQpLmNsYXNzTmFtZSA9IGNzc0NsYXNzOwp9CmZ1bmN0aW9uIGNoYW5nZUJ1dHRvbkFjdGlvbihlbGVtZW50LCBidXR0b25JRCwgYXR0cmlidXRlKSB7CiAgICB2YXIgdmFsdWUgPSBlbGVtZW50Lm9wdGlvbnNbZWxlbWVudC5zZWxlY3RlZEluZGV4XS52YWx1ZTsKICAgIGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKGJ1dHRvbklEKS5zZXRBdHRyaWJ1dGUoYXR0cmlidXRlLCB2YWx1ZSk7Cn0KZnVuY3Rpb24gZ2V0TG9jYWxEYXRhKGRhdGFUeXBlLCBpZCkgewogICAgdmFyIGRhdGEgPSBuZXcgT2JqZWN0KCk7CiAgICBzd2l0Y2ggKGRhdGFUeXBlKSB7CiAgICAgICAgY2FzZSAibTN1IjoKICAgICAgICAgICAgZGF0YSA9IFNFUlZFUlsic2V0dGluZ3MiXVsiZmlsZXMiXVtkYXRhVHlwZV1baWRdOwogICAgICAgICAgICBicmVhazsKICAgICAgICBjYXNlICJoZGhyIjoKICAgICAgICAgICAgZGF0YSA9IFNFUlZFUlsic2V0dGluZ3MiXVsiZmlsZXMiXVtkYXRhVHlwZV1baWRdOwogICAgICAgICAgICBicmVhazsKICAgICAgICBjYXNlICJmaWx0ZXIiOgogICAgICAgIGNhc2UgImN1c3RvbS1maWx0ZXIiOgogICAgICAgIGNhc2UgImdyb3VwLXRpdGxlIjoKICAgICAgICAgICAgaWYgKGlkID09IC0xKSB7CiAgICAgICAgICAgICAgICBkYXRhWyJhY3RpdmUiXSA9IHRydWU7CiAgICAgICAgICAgICAgICBkYXRhWyJjYXNlU2Vuc2l0aXZlIl0gPSBmYWxzZTsKICAgICAgICAgICAgICAgIGRhdGFbImRlc2NyaXB0aW9uIl0gPSAiIjsKICAgICAgICAgICAgICAgIGRhdGFbImV4Y2x1ZGUiXSA9ICIiOwogICAgICAgICAgICAgICAgZGF0YVsiZmlsdGVyIl0gPSAiIjsKICAgICAgICAgICAgICAgIGRhdGFbImluY2x1ZGUiXSA9ICIiOwogICAgICAgICAgICAgICAgZGF0YVsibmFtZSJdID0gIiI7CiAgICAgICAgICAgICAgICBkYXRhWyJ0eXBlIl0gPSAiZ3JvdXAtdGl0bGUiOwogICAgICAgICAgICAgICAgU0VSVkVSWyJzZXR0aW5ncyJdWyJmaWx0ZXIiXVtpZF0gPSBkYXRhOwogICAgICAgICAgICB9CiAgICAgICAgICAgIGRhdGEgPSBTRVJWRVJbInNldHRpbmdzIl1bImZpbHRlciJdW2lkXTsKICAgICAgICAgICAgYnJlYWs7CiLine truncated
|
||||||
|
webUI["html/js/menu.js"] = "CmZ1bmN0aW9uIHNldE1lbnVJdGVtKCkgewoKICBtZW51ID0gbmV3IE9iamVjdCgpOwogIHN1Yk1lbnUgPSBuZXcgT2JqZWN0KCk7CgogIHZhciBtZW51X20zdSA9IG5ldyBPYmplY3QoKTsKICBtZW51X20zdVsiX21lbnVUeXBlIl0gICAgICAgPSAiaW5wdXRBcnJheSI7CiAgbWVudV9tM3VbIl9lbGVtZW50Il0gICAgICAgID0gIkxJIjsKICBtZW51X20zdVsiX2NvbmZpZ0tleSJdICAgICAgPSAiZmlsZXMubTN1IjsKICBtZW51X20zdVsiX3RleHQiXSAgICAgICAgICAgPSAiUGxheWxpc3QiOwogIG1lbnVfbTN1WyJfaWNvbiJdICAgICAgICAgICA9ICJpbWcvbTN1LnBuZyI7CiAgbWVudV9tM3VbIl9oZWFkbGluZSJdICAgICAgID0gIlBsYXlsaXN0czogTG9jYWwgb3IgcmVtb3RlIjsKICBtZW51X20zdVsiX3VzYWdlIl0gICAgICAgICAgPSAiPGI+SW5mbzwvYj48YnI+QXZhaWxhYmlsaXR5OiBGaWxlIGF2YWlsYWJpbGl0eSBpbiBwZXJjZW50PGJyPlN0cmVhbXM6ICAgICAgTnVtYmVyIG9mIHN0cmVhbXMgaW4gdGhlIGZpbGUuPGJyPmdyb3VwLXRpdGxlOiAgU3RyZWFtcyB0aGF0IGFyZSBhc3NpZ25lZCB0byBhIGdyb3VwLiBTaW1wbGlmaWVzIGZpbHRlcmluZyBzdHJlYW1zPGJyPnR2Zy1pZDogICAgICAgVGhpcyBJRCBpcyB1c2VkIGZvciBhdXRvbWF0aWMgbWFwcGluZywgbXVzdCBtYXRjaCB3aXRoIHRoZSBjaGFubmVsIElEIGluIHRoZSBYTUxUViBmaWxlLjxicj5VbmlxdWUgSUQ6ICAgIFN0cmVhbXMgd2l0aCBhIHVuaXF1ZSBJRCB0byBpZGVudGlmeSB0aGVtLiBBbGxvd3MgY2hhbm5lbCBuYW1lIGNoYW5nZXMgaW4gdGhlIE0zVSB3aXRob3V0IGxvc2luZyB0aGUgWE1MVFYgbWFwcGluZyAoUFBWIC8gbGl2ZSBldmVudHMpLjxicj48YnI+PGI+VXNhZ2UgTTNVOjwvYj48YnI+UmVtb3RlIHBsYXlsaXN0OiBodHRwOi8veW91ci5pcHR2LnByb3ZpZGVyLmNvbS9maWxlLm0zdTxicj5Mb2NhbCAgcGxheWxpc3Q6IC9wYXRoL3RvL2ZpbGUubTN1PGJyPjxicj48Yj5Vc2FnZSBIREhvbWVSdW46PC9iPjxicj5JUDogMTkyLjE2OC4xLjEwOjUwMDQ8YnI+IgogIG1lbnVfbTN1WyJuYW1lIl0gICAgICAgICAgICA9ICJmaWxlIjsKICBtZW51X20zdVsiaWQiXSAgICAgICAgICAgICAgPSAiZmlsZSI7CiAgbWVudV9tM3VbInZhbHVlIl0gICAgICAgICAgID0gbWVudV9tM3VbIm5hbWUiXTsKICBtZW51X20zdVsicGxhY2Vob2xkZXIiXSAgICAgPSAiUGxheWxpc3Q6IGxvY2FsIG9yIHJlbW90ZSI7CiAgbWVudV9tM3VbIm9uY2xpY2siXSAgICAgICAgID0gImphdmFzY3JpcHQ6IHRvZ2dsZU1lbnUodGhpcyk7IjsKICBtZW51X20zdVsiY2xhc3MiXSAgICAgICAgICAgPSAibWVudS1ub3RBY3RpdmUiOwoKCiAgdmFyIG1lbnVfZmlsdGVyID0gbmV3IE9iamVjdCgpOwogIG1lbnVfZmlsdGVyWyJfbWVudVR5cGUiXSAgICA9ICJpbnB1dEFycmF5IjsKICBtZW51X2ZpbHRlclsiX2VsZW1lbnQiXSAgICAgPSAiTEkiOwogIG1lbnVfZmlsdGVyWyJfY29uZmlnS2V5Il0gICA9ICJmaWx0ZXIiOwogIG1lbnVfZmlsdGVyWyJfdGV4dCJdICAgICAgICA9ICJGaWx0ZXIiOwogIG1lbnVfZmlsdGVyWyJfaWNvbiJdICAgICAgICA9ICJpbWcvZmlsdGVyLnBuZyI7CiAgbWVudV9maWx0ZXJbIl9oZWFkbGluZSJdICAgID0gIkZpbHRlciBieSBNM1UgcGFyYW1ldGVycywgZS5nLiBncm91cC10aXRsZSI7CiAgbWVudV9maWx0ZXJbIl91c2FnZSJdICAgICAgID0gIjxiPlVzYWdlOjwvYj48YnI+U3BvcnQgLSBBbGwgc3BvcnRzIGNoYW5uZWxzPGJyPlNwb3J0IHtIRH0gLSBBbGwgSEQgc3BvcnRzIGNoYW5uZWxzPGJyPlNwb3J0IHtIRH0gIXtFUyxERX0gLSBBbGwgSEQgc3BvcnRzIGNoYW5uZWxzLCBidXQgbm8gU3BhbmlzaCBhbmQgR2VybWFuPGJyPjxicj5UbyBmaWx0ZXIgdGhlIHN0cmVhbXMgb2YgYSBIREhvbWVSdW4sIHRoZSBwbGF5bGlzdCBuYW1lIGNhbiBiZSBlbnRlcmVkOjxicj5NeSB0dW5lciB7SER9IgogIC8vbWVudV9maWx0ZXJbIl91c2FnZSJdICAgICAgID0gIjxiPlVzYWdlOjwvYj48YnI+QWxsIHNwb3J0cyBjaGFubmVsczogU3BvcnQ8YnI+QWxsIEhEIHNwb3J0cyBjaGFubmVsczogU3BvcnQge0hEfTxicj5BbGwgSEQgc3BvcnRzIGNoYW5uZWxzLCBidXQgbm8gU3BhbmlzaCBhbmQgR2VybWFuOiBTcG9ydCB7SER9ICF7RVMsREV9IgogIG1lbnVfZmlsdGVyWyJuYW1lIl0gICAgICAgICA9ICJmaWx0ZXIiOwogIG1lbnVfZmlsdGVyWyJpZCJdICAgICAgICAgICA9ICJNM1UiOwogIG1lbnVfZmlsdGVyWyJ2YWx1ZSJdICAgICAgICA9IG1lbnVfZmlsdGVyWyJuYW1lIl07CiAgbWVudV9maWx0ZXJbInBsYWNlaG9sZGVyIl0gID0gIkZpbHRlciBzdHJlYW1zOiBTcG9ydCI7CiAgbWVudV9maWx0ZXJbIm9uY2xpY2siXSAgICAgID0gImphdmFzY3JpcHQ6IHRvZ2dsZU1lbnUodGhpcyk7IjsKICBtZW51X2ZpbHRlclsiY2xhc3MiXSAgICAgICAgPSAibWVudS1ub3RBY3RpdmUiOwoKICB2YXIgbWVudV9pZCA9IG5ldyBPYmplY3QoKTsKICBtZW51X2lkWyJfbWVudVR5cGUiXSAgICAgICAgPSAiaW5wdXRBcnJheSI7CiAgbWVudV9pZFsiX2VsZW1lbnQiXSAgICAgICAgID0gIkxJIjsKICBtZW51X2lkWyJfY29uZmlnS2V5Il0gICAgICAgPSAiaWQiOwogIG1lbnVfaWRbIl90ZXh0Il0gICAgICAgICAgICA9ICJQTVMgSUQiOwogIG1lbnVfaWRbIl9pY29uIl0gICAgICAgICAgICA9ICJpbWcvbnVtYmVyLnBuZyI7CiAgbWVudV9pZFsiX2hlYWRsaW5lIl0gICAgICAgID0gIlNldHVwIFBNUyBndWlkZSBudW1iZXIiOwogIG1lbnVfaWRbIl91c2FnZSJdICAgICAgICAgICA9ICdTb21lIHBsYXlsaXN0cyBoYXZlIHVuaXF1ZSBjaGFubmVsIElEcy48YnI+RW50ZXIgdGhlIGtleXdvcmQgb2YgdGhlIElELiBUaGUgY2hhbm5lbCBhc3NpZ25tZW50IGluIFBNUyB3aWxsIGNoYW5nZSBhcyBhIHJlc3VsdC48YnI+PGJyPmUuZy4gY2hhbm5lbElEPGJyPiNFWFRJTkY6MCB0eXBlPSJzdHJlYW0iIDxiPmNoYW5uZWxJZDwvYj49IjgxIiwgTXkgU3RyZWFtaW5nIENoYW5uZWwgSEQ8YnI+PGJyPk9ubHkgZW50ZXIgaGVyZSBpZiB5b3Uga25vdyB3aGF0IHlvdSBhcmUgZG9pbmchJwogIG1lbnVfaWRbIm5hbWUiXSAgICAgICAgICAgICA9ICJpZCI7CiAgbWVudV9pZFsiaWQiXSAgICAgICAgICAgICAgID0gImlkIjsKICBtZW51X2lkWyJ2YWx1ZSJdICAgICAgICAgICAgPSBtZW51X2lkWyJuYW1lIl07CiAgbWVudV9pZFsicGxhY2Vob2xkZXIiXSAgICAgID0gIlVuaXF1ZSBJRCBmcm9tIHRoZSBNM1UgZmlsZSI7CiAgbWVudV9pZFsib25jbGljayJdICAgICAgICAgID0gImphdmFzY3JpcHQ6IHRvZ2dsZU1lbnUodGhpcyk7IjsKICBtZW51X2lkWyJjbGFzcyJdICAgICAgICAgICAgPSAibWVudS1ub3RBY3RpdmUiOwoKCiAgdmFyIG1lbnVfeG1sdHYgPSBuZXcgT2JqZWN0KCk7CiAgbWVudV94bWx0dlsiX21lbnVUeXBlIl0gICAgID0gImlucHV0QXJyYXkiOwogIG1lbnVfeG1sdHZbIl9lbGVtZW50Il0gICAgICA9ICJMSSI7CiAgbWVudV94bWx0dlsiX2NvbmZpZ0tleSJdICAgID0gImZpbGVzLnhtbHR2IjsKICBtZW51X3htbHR2WyJfdGV4dCJdICAgICAgICAgPSAiWE1MVFYiOwogIG1lbnVfeG1sdHZbIl9pY29uIl0gICAgICAgICA9ICJpbWcveG1sdHYucG5nIjsKICBtZW51X3htbHR2WyJfaGVhZGxpbmUiXSAgICAgPSAiWE1MVFYgZmlsZXM6IExvY2FsIG9yIHJlbW90ZSI7CiAgbWVudV94bLine truncated
|
||||||
|
webUI["html/video/stream-limit.ts"] = "R0AREABC8CUAAcEAAP8B/wAB/IAUSBIBBkZGbXBlZwlTZXJ2aWNlMDF3fEPK//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////9HQAAQAACwDQABwQAAAAHwACqxBLL//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////0dQABAAArASAAHBAADhAPAAG+EA8AAVvU1W////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////R0EAMAdQAAB7DH4AAAAB4AAAgMAKMQAJEKERAAfYYQAAAAEJ8AAAAAFnZAAorNlAeAIn5cBEAAADAAQAAAMAyDxgxlgAAAABaOvjyyLAAAABBgX//6rcRem95tlIt5Ys2CDZI+7veDI2NCAtIGNvcmUgMTUyIHIyODU0IGU5YTU5MDMgLSBILjI2NC9NUEVHLTQgQVZDIGNvZGVjIC0gQ29weWxlZnQgMjAwMy0yMDE3IC0gaHR0cDovL3dHAQARd3cudmlkZW9sYW4ub3JnL3gyNjQuaHRtbCAtIG9wdGlvbnM6IGNhYmFjPTEgcmVmPTMgZGVibG9jaz0xOjA6MCBhbmFseXNlPTB4MzoweDExMyBtZT1oZXggc3VibWU9NyBwc3k9MSBwc3lfcmQ9MS4wMDowLjAwIG1peGVkX3JlZj0xIG1lX3JhbmdlPTE2IGNocm9tYV9tZT0xIHRyZWxsaXM9MSA4eDhkY3Q9MSBjcW09MCBkZUcBABJhZHpvbmU9MjEsMTEgZmFzdF9wc2tpcD0xIGNocm9tYV9xcF9vZmZzZXQ9LTIgdGhyZWFkcz02IGxvb2thaGVhZF90aHJlYWRzPTEgc2xpY2VkX3RocmVhZHM9MCBucj0wIGRlY2ltYXRlPTEgaW50ZXJsYWNlZD0wIGJsdXJheV9jb21wYXQ9MCBjb25zdHJhaW5lZF9pbnRyYT0wIGJmcmFtZXM9MyBiX3B5cmFtaWQ9MiBiX2FkRwEAE2FwdD0xIGJfYmlhcz0wIGRpcmVjdD0xIHdlaWdodGI9MSBvcGVuX2dvcD0wIHdlaWdodHA9MiBrZXlpbnQ9MjUwIGtleWludF9taW49MjUgc2NlbmVjdXQ9NDAgaW50cmFfcmVmcmVzaD0wIHJjX2xvb2thaGVhZD00MCByYz1jcmYgbWJ0cmVlPTEgY3JmPTIzLjAgcWNvbXA9MC42MCBxcG1pbj0wIHFwbWF4PTY5IHFwc3RlcD1HAQAUNCBpcF9yYXRpbz0xLjQwIGFxPTE6MS4wMACAAAABZYiEADv//vdOvwKbVMIqA5JXCvbKpCZZuVJrAfKmAAADAAADAAADAAADAAADAq7aiiFafTJvTQAAAwAAAwAAF1AAAAVUAAADAiYAAAMBNwAAAwDUAAADAMkAAAMA4gAAAwD+AAADATIAAAMCGgAABAQAAAYoAAAOwAAAAwAAAwAAAwAAAwAAAwAAAwAAAwAAAwAAAwAAAwAAA0cBABUAAAMAAAMAAAMAAAMAAAMAAAMAAAMAAAMAAAMAAAMAAAMAA2F55UwH3zADgCn7Xsg5urkZA62Mq62dWKwprJak9U0cjT1Yh9J7WRISuxmagZEf3SG5hJ5glLK+m1DQRdCFD2mN6YZGjkCTKwYyDxKlNHkaW8fuatJR0sAtCoVfOjRr2/NS+bw85zMQI7VFu7+JhD/Go1Q5RgMv5bIq0gpxJKRnF31ojI62CwCRiJ1dHa6nIwcU00zURwEAFhyu6/UdOQ6gXR/Fn3SkmUwkQsjmQF0XEAAAAwAAQkN8C3guxx7g23xd3I1uQvL9Dh+C+fpNfOmVma2q7DiUF3VD8o51I57iYniPq7fe7f2YnPwRwynt2WAsZSjTMgHnGbk5zsyy+rXIMy1p3notBIJkpSBHSuogn9FDL99PItYMLSYCXzbB1ngJo9jnaTgI0b904RmkSjuZtpKyt0ofkYfxNRrztwDGURi3JRPkpuouCWX8KWcxn8NHAQAXWrAsVbXQ0f+vby6QN9oHh0jRsBw7+79AAAsCUlIAAAMAAIGTVQt7SJOBNtpTiAsB1/AzhYzJ8lMVgRENWBT0VBkiPo9+OyjWfg6dlrISORs+GKnEy5aUKA1M9uXDtedogUi7S7uVd54Gj1Qb5NTw4Jix5w1Ejl/de0xvxERyv4b3rmZdsU72FOcfnbK1K8lG4wnxVzYPGiElBGSrNsWNXskCT++8dTbAQv4ejltKQDN2z2xpw5g1CkcBABjFGRM3p9Rp9BGLQ/gaCYSE37IZAAADAFsvgUK7rSLf56T0ZOVulX2oYo74ASEKbonCVfRcGAke05L38QhPtmJ8xRmebFR5/801CbQAWvnAOc2AWgY1coPTdBTZIfsBwU94Gu+qjaoR4dfBX9u8RZXmrbLBCqJ8wTgvf9kJu7/27gcTx8SsJfq40cjhNaRNiof2e8CF4imWObVdvBDpPRxSiR1tsORfA0r+gqjURLyvIwDsE+O9NkeiRwEAGZMG5kW8doFnKjj4QW/OIpFfzF+53Eb4LrnRmrkHqYA/RnqVvBWNpUSFdATxWKzy0MBYRpY4IoUCpCeWwgQ/9Ny0Yi1iQ++wmh1ay/SnYR5B8k+FpDYbWDODuSuCMmrTDKXjicAStFseuB4peB1Ufu7fitj0L0uZzCgGY5zkzbBp3bfXJQEGCnSwEtW+TY50gbhenVCesUfJKjvG6PshP5ScKPdVkZltrS4wx1CntFR+srvN3MjP6p1HAQAaM8+T4dTA6o28g/ki3+J9b5WD8oY+fVgTsb1jfV2GWWd2Hh8JK00uloyX94k2QHGzhbuiS1YKiEdCLz9Pt3RuxuTNm9XSlLwQvyRvfY6sKD8KIinvSq3XP//Kx5Ni82QOXvrn5eEmFlTGuT36Yax5OR4BI3UhdWSGGa0YoqeD7zcvWv64AAANbhz0PNupTjTGu6vxaQq6PcgarV8F7NT0zHSQCZ8KuEFdideJmx4581/BCMYXuJt5cEcBABufpZ3Gzdy5JLw0J2eg1hz70uf/nD82aqNxy/HUvEd6+w5woyIYb7nzl1eo/+ACzPidfiSKydCNxKh9e7tKjNNTJYRX+1f5A398n+2wKJPX7dRmPk4C0j/auaYtAMF+Ft5g6ZqxQuA071fEmj4uiYMR7F+JbZ7CjA98DObmNy/ZWoJQvMrvR3ep5XAmNFkqs1Ma20JWsiikVsr/jblTK0PBVnLul36knafsXUW38/19lMQo0HUcMvrmRwEAHI2OaNsvRTodm5RGcpDEKfXIqXsLfH53r0F3K+sfRvbk3u2ilZd/c5zH95TBbceTvFEt5mD6oPEhxHX3gId6ZBsNNUKUnWDxoX7MaYE38/ELMyIVzRxDCIEkf+q7aY6tcZEMrPBa352hxE6SJ+2y/zlDc8X4jSYerSPRgLBiUm/TSHdm/sHP7d/kl2fmIUNKBbeaghigfwmfDnaZ3wPkRi3oXIp3YypbLYpeup62X805+oYMAgynQd1HAQAdHLgV5nC45givqz8G4AAB0pcgXQpZhwRtJ0wE/aFEPMXugXV9NeZnpzJeXaijTBtaFonT4e+hZZXjLHhxTugKb085RNFOoT0GAB6b2twqc++PKcb796IKMKtNpGaUWjoFvBDGh4HrRguoZWCbLy2RFxUX8SmNUb7qfDXwIi4nY4i89/TcrSc0q64NPWA3SPmQ5D5/ZFbRzBtFuMVVmbEF0cxv9RoqLyW/VGkp+bBAmoMhAw82eI9VskcBAB7bidwIeUDbJvHb2Z8Ep/avJccAh157ZUytHDDW9DhmdbdBzT1TpsJFsFWe8FOQfaNpJVfj2nGqShY4+DhxRYxOpzXw2VF7hQpp6PqFUtyi4ib3VHETkSuHz+2uom/bMRbhw+hQ05xdXOHcFVpih+k58QKrhN+txmOmV3ULdeiVLUplf35jQJTUIZltXjTCZhljRc0sdqOZHDZjy58Sc6hmq8D6lztRR5jN6ifzzKdZbHOpMvK28A4ZRwEAHwyCdGDG4LksEyABPYPnzVuaVJHpUiKgtb3006VleBPo2EZenL5/vyGrjLMas0C6R0iyXZjr0xqvCfaFfxVO/+DQUMKaj0XLNNIrLqXxwzxzy2ENSUjNNySzfvDkpjuJVywa0uwsg60r6EURKgU15gAtbm3Kx4tjb3pRMM8SZHnsN075ztW98+aTrdbRryY9XR74p76dkImiqWIVRQAAAwAAerbiggDgwfd5iHYwqlkEXOqApTuXEhFHAQAQ1PUugOrA4HsFQsLzVf2RjOkhWvlC/kwNqqDXkwlUM6egLoIdgXAyBFgSXuM59iSh6DSi4LwFa/FhyGMrxPV0UF8QD9dGgSeXBUvExQHAGOHz98Mbwm3lBdIzxkzeGLDhi0wZ5SurQPpWomKoDW7t0a+GpQkmCEgVO7iBc/JrOFIt8BBRW1E3EOMv6Wpc5BLine truncated
|
||||||
|
webUI["html/lang/de.json"] = "ewogICJtYWluTWVudSI6IHsKICAgICJpdGVtIjp7CiAgICAgICJwbGF5bGlzdCI6ICJXaWVkZXJnYWJlbGlzdGUiLAogICAgICAicG1zSUQiOiAiUE1TIElEIiwKICAgICAgImZpbHRlciI6ICJGaWx0ZXIiLAogICAgICAieG1sdHYiOiAiWE1MVFYiLAogICAgICAibWFwcGluZyI6ICJNYXBwaW5nIiwKICAgICAgInVzZXJzIjogIkJlbnV0emVyIiwKICAgICAgInNldHRpbmdzIjogIkVpbnN0ZWxsdW5nZW4iLAogICAgICAibG9nIjogIkxvZyIsCiAgICAgICJsb2dvdXQiOiAiQWJtZWxkZW4iCiAgICB9LAogICAgImhlYWRsaW5lIjogewogICAgICAicGxheWxpc3QiOiAiTG9rYWxlIG9kZXIgZW50ZmVybnRlIFdpZWRlcmdhYmVsaXN0ZW4iLAogICAgICAiZmlsdGVyIjogIkZpbHRlcm4gdm9uIEthbsOkbGVuIiwKICAgICAgInhtbHR2IjogIkxva2FsZSBvZGVyIGVudGZlcm50ZSBYTUxUViBEYXRlaWVuIiwKICAgICAgIm1hcHBpbmciOiAiS2Fuw6RsZSBkZXIgV2llZGVyZ2FiZWxpc3RlbiBFUEcgS2Fuw6RsZW4genVvcmRuZW4iLAogICAgICAidXNlcnMiOiAiQmVudXR6ZXJ2ZXJ3YWx0dW5nIiwKICAgICAgInNldHRpbmdzIjogIkVpbnN0ZWxsdW5nZW4iLAogICAgICAibG9nIjogIkxvZyIsCiAgICAgICJsb2dvdXQiOiAiQWJtZWxkZW4iCiAgICB9CiAgfSwKICAiY29uZmlybSI6ewogICAgInJlc3RvcmUiOiAiQWxsZSBEYXRlbiB3ZXJkZW4gbWl0IGRlbmVuIGF1cyBkZW0gQmFja3VwIGVyc2V0enQuIFdpZWRlcmhlc3RlbGx1bmcgZHVyY2hmw7xocmVuPyIKICB9LAogICJhbGVydCI6IHsKICAgICJmaWxlTG9hZGluZ0Vycm9yIjogIkRhdGVpIGtvbm50ZSBuaWNodCBnZWxhZGVuIHdlcmRlbiIsCiAgICAiaW52YWxpZENoYW5uZWxOdW1iZXIiOiAiVW5nw7xsdGlnZSBLYW5hbG51bW1lciIKICB9LAogICJidXR0b24iOnsKICAgICJiYWNrIjogIlp1csO8Y2siLAogICAgImJhY2t1cCI6ICJCYWNrdXAiLAogICAgImJ1bGtFZGl0IjogIk1hc3NlbmJlYXJiZWl0dW5nIiwKICAgICJjYW5jZWwiOiAiQWJicmVjaGVuIiwKICAgICJkZWxldGUiOiAiTMO2c2NoZW4iLAogICAgImRvbmUiOiAiRmVydGlnIiwKICAgICJsb2dpbiI6ICJBbm1lbGRlbiIsCiAgICAibmV3IjogIk5ldSIsCiAgICAibmV4dCI6ICJXZWl0ZXIiLAogICAgInJlc3RvcmUiOiAiV2llZGVyaGVyc3RlbGxlbiIsCiAgICAic2F2ZSI6ICJTcGVjaWhlcm4iLAogICAgInNlYXJjaCI6ICJTdWNoZW4iLAogICAgInVwZGF0ZSI6ICJVcGRhdGUiLAogICAgImNyYWV0ZUFjY291bnQiOiAiQWNjb3VudCBlcnN0ZWxsZW4iLAogICAgInJlc2V0bG9ncyI6ICJMb2dzIGzDtnNjaGVuIiwKICAgICJ1cGxvYWRMb2dvIjogIkxvZ28gaG9jaGxhZGVuIgogIH0sCiAgImZpbHRlciI6IHsKICAgICJ0YWJsZSI6IHsKICAgICAgIm5hbWUiOiAiRmlsdGVyIE5hbWUiLAogICAgICAidHlwZSI6ICJGaWx0ZXIgVHlwIiwKICAgICAgImZpbHRlciI6ICJGaWx0ZXIiCiAgICB9LAogICAgImN1c3RvbSI6ICJCZW51dHplcmRlZmluaWVydCIsCiAgICAiZ3JvdXAiOiAiR3J1cHBlIiwKICAgICJuYW1lIjogewogICAgICAidGl0bGUiOiAiRmlsdGVyIE5hbWUiLAogICAgICAicGxhY2Vob2xkZXIiOiAiRmlsdGVyIE5hbWUiLAogICAgICAiZGVzY3JpcHRpb24iOiAiIgogICAgfSwKICAgICJkZXNjcmlwdGlvbiI6IHsKICAgICAgInRpdGxlIjogIkJlc2NocmVpYnVuZyIsCiAgICAgICJwbGFjZWhvbGRlciI6ICJCZXNjaHJlaWJ1bmciLAogICAgICAiZGVzY3JpcHRpb24iOiAiIgogICAgfSwKICAgICJ0eXBlIjogewogICAgICAidGl0bGUiOiAiVHlwIiwKICAgICAgImdyb3VwVGl0bGUiOiAiR3JvdXBlbnRpdGVsIiwKICAgICAgImN1c3RvbUZpbHRlciI6ICJCZW51dHplcmRlZmluaWVydGVyIEZpbHRlciIKICAgIH0sCiAgICAiY2FzZVNlbnNpdGl2ZSI6IHsKICAgICAgInRpdGxlIjogIkdyb8OfLSAvIEtsZWluc2NocmVpYnVuZyBiZWFjaHRlbiIsCiAgICAgICJwbGFjZWhvbGRlciI6ICIiLAogICAgICAiZGVzY3JpcHRpb24iOiAiIgogICAgfSwKICAgICJmaWx0ZXJSdWxlIjogewogICAgICAidGl0bGUiOiAiRmlsdGVycmVnZWwiLAogICAgICAicGxhY2Vob2xkZXIiOiAiU3BvcnQge0hEfSAhe0VTLElUfSIsCiAgICAgICJkZXNjcmlwdGlvbiI6ICIiCiAgICB9LAogICAgImZpbHRlckdyb3VwIjogewogICAgICAidGl0bGUiOiAiR3JvdXBlbnRpdGVsIiwKICAgICAgInBsYWNlaG9sZGVyIjogIiIsCiAgICAgICJkZXNjcmlwdGlvbiI6ICJHcnVwcGUgYXVzIGRlciBNM1UgYXVzd8OkaGxlbi4gKFrDpGhsZXIpPGJyPsOkbmRlcnVuZyBkZXMgR3J1cHBlbnRpdGVscyBpbiBkZXIgTTNVIG1hY2h0IGRlbiBGaWx0ZXIgdW5nw7xsdGlnLiIKICAgIH0sCiAgICAiaW5jbHVkZSI6IHsKICAgICAgInRpdGxlIjogIk11c3MgYmVpbmhhbHRlbiIsCiAgICAgICJwbGFjZWhvbGRlciI6ICJGSEQsVUhEIiwKICAgICAgImRlc2NyaXB0aW9uIjogIkthbmFsbmFtZSBtdXNzIGRpZXNlIFfDtnJ0ZXIgZW50aGFsdGVuLjxicj4oS29tbWFnZXRyZW5udCkgS29tbWEgYmVkZXV0ZXQgb2RlciIKICAgIH0sCiAgICAiZXhjbHVkZSI6IHsKICAgICAgInRpdGxlIjogIkRhcmYgbmljaHQgYmVpbmhhbHRlbiIsCiAgICAgICJwbGFjZWhvbGRlciI6ICJFUyxJVCIsCiAgICAgICJkZXNjcmlwdGlvbiI6ICJLYW5hbG5hbWUgZGFyZiBkaWVzZSBXw7ZydGVyIG5pY2h0IGVudGhhbHRlbi48YnI+KEtvbW1hZ2V0cmVubnQpS29tbWEgYmVkZXV0ZXQgb2RlciIKICAgIH0KCiAgfSwKICAicGxheWxpc3QiOiB7CiAgICAidGFibGUiOiB7CiAgICAgICJwbGF5bGlzdCI6ICJXaWVkZXJnYWJlbGlzdGUiLAogICAgICAidHVuZXIiOiAiVHVuZXIiLAogICAgICAibGFzdFVwZGF0ZSI6ICJMZXR6dGUgQWt0dWFsaXNpZXJ1bmciLAogICAgICAiYXZhaWxhYmlsaXR5IjogIlZlcmbDvGdiYXJrZWl0IiwKICAgICAgInR5cGUiOiAiVHlwIiwKICAgICAgInN0cmVhbXMiOiAiU3RyZWFtcyIsCiAgICAgICJncm91cFRpdGxlIjogIkdyb3VwZW50aXRlbCIsCiAgICAgICJ0dmdJRCI6ICJ0dmctaWQiLAogICAgICAidW5pcXVlSUQiOiAiRWluZGV1dGlnZSBJRCIKICAgIH0sCiAgICAicGxheWxpc3RUeXBlIjogewogICAgICAidGl0bGUiOiAiV2llZGVyZ2FiZWxpc3RlbiBUeXAiLAogICAgICAicGxhY2Vob2xkZXIiOiAiIiwKICAgICAgImRlc2NyaXB0aW9uIjogIiIKICAgIH0sCiAgICAidHlwZSI6IHsKICAgICAgInRpdGxlIjogIlR5cCIsCiAgICAgICJwbGFjZWhvbGRlciI6ICIiLAogICAgICAiZGVzY3JpcHRpb24iOiAiIgogICAgfSwKICAgICJuYW1lIjogewogICAgICAidGl0bGUiOiAiTmFtZSIsCiAgICAgICJwbGFjZWhvbGRlciI6ICJOYW1lIGRlciBXaWVkZXJnYWJlbGlzdGUiLAogICAgICAiZGVzY3JpcHRpb24iOiAiIgogICAgfSwKICAgICJkZXNjcmlwdGlvbiI6IHsKICAgICAgInRpdGxlIjogIkJlc2NocmVpYnVuZyIsCiAgICAgICJwbGFjZWhvbGRlciI6ICJCZXNjaHJlaWJ1bmciLAogICAgICAiZGVzY3JpcHRpb24iOiAiIgogICAgfSwKICAgICJmaWxlTTNVIjogewogICAgICAidGl0bGUiOiAiTTNVIERhdGVpIiwKICAgICAgInBsYWNlaG9sZGVyIjogIkRhdGVpcGZhZCBvZGVyIFVSTCBkZXIgTTNVIiwKICAgICAgImRlc2NyaXB0aW9uIjogIiIKICAgIH0sCiAgICAiZmlsZUhESFIiOiB7CiAgICAgICJ0aXRsZSI6ICJIREhvbWVSdW4Line truncated
|
||||||
|
webUI["html/create-first-user.html"] = "PCFkb2N0eXBlIGh0bWw+CjxodG1sPgogIDxoZWFkPgogICAgPG1ldGEgY2hhcnNldD0idXRmLTgiPgogICAgPG1ldGEgbmFtZT0idmlld3BvcnQiIGNvbnRlbnQ9IndpZHRoPWRldmljZS13aWR0aCwgaW5pdGlhbC1zY2FsZT0xLjAiIC8+IAogICAgPHRpdGxlPnhUZVZlPC90aXRsZT4KICAgIDxsaW5rIHJlbD0ic3R5bGVzaGVldCIgaHJlZj0iY3NzL3NjcmVlbi5jc3MiIHR5cGU9InRleHQvY3NzIj4KICAgIDxsaW5rIHJlbD0ic3R5bGVzaGVldCIgaHJlZj0iY3NzL2Jhc2UuY3NzIiB0eXBlPSJ0ZXh0L2NzcyI+CiAgICA8c2NyaXB0IGxhbmd1YWdlPSJqYXZhc2NyaXB0IiB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiIHNyYz0ianMvbmV0d29ya190cy5qcyI+PC9zY3JpcHQ+CiAgICA8c2NyaXB0IGxhbmd1YWdlPSJqYXZhc2NyaXB0IiB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiIHNyYz0ianMvYXV0aGVudGljYXRpb25fdHMuanMiPjwvc2NyaXB0PgogIDwvaGVhZD4KCiAgICA8Ym9keT4KICAgICAgICAgIAogICAgICA8ZGl2IGlkPSJoZWFkZXIiIGNsYXNzPSJpbWdDZW50ZXIiPjwvZGl2PgogICAgICA8ZGl2IGlkPSJib3giPgogICAgICAgIDxkaXYgaWQ9ImhlYWRsaW5lIj4KICAgICAgICAgIDxoMSBpZD0iaGVhZC10ZXh0IiBjbGFzcz0iY2VudGVyIj57ey5hY2NvdW50LmhlYWRsaW5lfX08L2gxPgogICAgICAgIDwvZGl2PgogICAgICAgIDxwIGlkPSJlcnIiIGNsYXNzPSJlcnJvck1zZyBjZW50ZXIiPjwvcD4gICAKICAgICAgICA8ZGl2IGlkPSJjb250ZW50Ij4KICAgICAgICAgICAgPGZvcm0gaWQ9ImF1dGhlbnRpY2F0aW9uIiBhY3Rpb249Ii93ZWIvIiBtZXRob2Q9InBvc3QiPgogICAgICAgICAgICAgIDxoNT57ey5hY2NvdW50LnVzZXJuYW1lLnRpdGxlfX06PC9oNT4KICAgICAgICAgICAgICA8aW5wdXQgaWQ9InVzZXJuYW1lIiB0eXBlPSJ0ZXh0IiBuYW1lPSJ1c2VybmFtZSIgcGxhY2Vob2xkZXI9IlVzZXJuYW1lIiB2YWx1ZT0iIj4KICAgICAgICAgICAgICA8aDU+e3suYWNjb3VudC5wYXNzd29yZC50aXRsZX19OjwvaDU+CiAgICAgICAgICAgICAgPGlucHV0IGlkPSJwYXNzd29yZCIgdHlwZT0icGFzc3dvcmQiIG5hbWU9InBhc3N3b3JkIiBwbGFjZWhvbGRlcj0iUGFzc3dvcmQiIHZhbHVlPSIiPgogICAgICAgICAgICAgIDxoNT57ey5hY2NvdW50LmNvbmZpcm0udGl0bGV9fTo8L2g1PgogICAgICAgICAgICAgIDxpbnB1dCBpZD0iY29uZmlybSIgIHR5cGU9InBhc3N3b3JkIiBuYW1lPSJjb25maXJtIiAgcGxhY2Vob2xkZXI9IkNvbmZpcm0iIHZhbHVlPSIiPgogICAgICAgICAgICA8L2Zvcm0+CiAgICAgICAgPC9kaXY+CiAgICAgICAgCiAgICAgICAgPGRpdiBpZD0iYm94LWZvb3RlciI+CiAgICAgICAgICA8aW5wdXQgaWQ9InN1Ym1pdCIgY2xhc3M9IiIgdHlwZT0iYnV0dG9uIiB2YWx1ZT0ie3suYnV0dG9uLmNyYWV0ZUFjY291bnR9fSIgb25jbGljaz0iamF2YXNjcmlwdDogbG9naW4oKTsiPgogICAgICAgIDwvZGl2PgogICAgICAKICAgICAgICAKICAgICAgPC9kaXY+CiAgICA8L2JvZHk+CjwvaHRtbD4="
|
||||||
|
webUI["html/img/users.png"] = "iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAAXNSR0IArs4c6QAAAAlwSFlzAAAsSwAALEsBpT2WqQAABCRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IlhNUCBDb3JlIDUuNC4wIj4KICAgPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgICAgICAgICAgIHhtbG5zOmV4aWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vZXhpZi8xLjAvIgogICAgICAgICAgICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgICAgICAgICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyI+CiAgICAgICAgIDx0aWZmOlJlc29sdXRpb25Vbml0PjI8L3RpZmY6UmVzb2x1dGlvblVuaXQ+CiAgICAgICAgIDx0aWZmOkNvbXByZXNzaW9uPjU8L3RpZmY6Q29tcHJlc3Npb24+CiAgICAgICAgIDx0aWZmOlhSZXNvbHV0aW9uPjI4ODwvdGlmZjpYUmVzb2x1dGlvbj4KICAgICAgICAgPHRpZmY6T3JpZW50YXRpb24+MTwvdGlmZjpPcmllbnRhdGlvbj4KICAgICAgICAgPHRpZmY6WVJlc29sdXRpb24+Mjg4PC90aWZmOllSZXNvbHV0aW9uPgogICAgICAgICA8ZXhpZjpQaXhlbFhEaW1lbnNpb24+NTA8L2V4aWY6UGl4ZWxYRGltZW5zaW9uPgogICAgICAgICA8ZXhpZjpDb2xvclNwYWNlPjE8L2V4aWY6Q29sb3JTcGFjZT4KICAgICAgICAgPGV4aWY6UGl4ZWxZRGltZW5zaW9uPjUwPC9leGlmOlBpeGVsWURpbWVuc2lvbj4KICAgICAgICAgPGRjOnN1YmplY3Q+CiAgICAgICAgICAgIDxyZGY6QmFnLz4KICAgICAgICAgPC9kYzpzdWJqZWN0PgogICAgICAgICA8eG1wOk1vZGlmeURhdGU+MjAxOC0wOC0zMFQxNzowODozODwveG1wOk1vZGlmeURhdGU+CiAgICAgICAgIDx4bXA6Q3JlYXRvclRvb2w+UGl4ZWxtYXRvciAzLjM8L3htcDpDcmVhdG9yVG9vbD4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+CjU01MMAAANJSURBVGgF1ZnLaxNRFMYz2rpoFbW+SCsihaJQRMT6rPhAXAq6c+HChSguRFd1oRtd+A/oWhQ3oiiWFB91UbqRqlXXgoIIQhFbQTTQao2/GzLDZEjIPefO7SQHPjJz55zzfd/MZCa5N8h5ilKptJvWp8FekAed4Bv4Cj6Dh2A4CIJZPpszMHEV2MQ0SZdB0DROENMHjoEzQBoFCpZnbgYR58E/qfpE/kv2F2dmBvJ94G9ClHb3SiZGUBuASa3qGnVzjG3RmFmkKYrVHGB7e2zfdbOdBhc1TVyNnNSQNqg5wVURf/FdjRxpIEpzuIOio9JCtRHO2hrINkgJLfPFt6vaCII2WYrSpPVJi1RGuBrmTbxeSibIX5j3CUauAfOo9BXzNH4nMJ5TXREI1gHzqPQVRtc2zCyxJdAaGbclcMj7zi/jOdt6rZFhCH7akijznknqVEY4U78heSAhUuTekNSojFQIChIiYe57TtZrSY2LkecQzUjIBLljglz3VJ4qg+AXSDNGabbWXZ2wA6Qjabqg136hhHK6y60V8o2EGyl8/qHHpKZPGkYeQzyvIa9RM86XvFhjvOGQsxGIp2B50ZDJLuG2XZqnLO5rM3viGh9o0OZJon1bRLxxdHLcns1jJiYOAu2U0D2P0uStMXJdcVU+UdMlZ/NYgaA28ERg5ge5/R4l6VsjrANMWJiZJWeXnqm60vnxW90ul6u8ByaS4zX2i+S+qjGuGkrdSEWFzT1vlhlSC19Gui0UtnNriSfi6vX1ZWRjPcLEeGrzYqkb4SyvQGxvQnC93cF6BzIfx8gpYBsfSdycuei4AAR1ggugCCRh1lbugIF4vwXbhtisjfSDc+ARSOOf4lv6mNWv1VIj1ouQNF9K851gDzArtWbV1uYxS5o4zB+sp+AuKPC+cVv5RXwPuATMmUpreY1Wopgh+ybYKj4dFHWB+8DMwTZTjCHmsJUhEgfAF9DMcQtx9V8dHMyDqWZ2ENM2FL8yVV92kkY56GM5Lc6Z1vY0jbp5EJQnuqPLg4kdLWTCnIxVIJoDi4wweNYcbbGIfuLEjRxqMRNGbvTvsmyE22oZg70taKQn1BxekXw40GKfK0O9oZHsJ8ZCRbLPaI3xP7YzeQoHxWckAAAAAElFTkSuQmCC"
|
||||||
|
webUI["html/img/x_white.png"] = "iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAIAAADTED8xAAAAAXNSR0IArs4c6QAAAAlwSFlzAAAWJQAAFiUBSVIk8AAAA6ppVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IlhNUCBDb3JlIDUuNC4wIj4KICAgPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIgogICAgICAgICAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgICAgICAgICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iPgogICAgICAgICA8eG1wOk1vZGlmeURhdGU+MjAxOC0xMS0xNVQxNzoxMToyNTwveG1wOk1vZGlmeURhdGU+CiAgICAgICAgIDx4bXA6Q3JlYXRvclRvb2w+UGl4ZWxtYXRvciAzLjM8L3htcDpDcmVhdG9yVG9vbD4KICAgICAgICAgPHRpZmY6T3JpZW50YXRpb24+MTwvdGlmZjpPcmllbnRhdGlvbj4KICAgICAgICAgPHRpZmY6Q29tcHJlc3Npb24+NTwvdGlmZjpDb21wcmVzc2lvbj4KICAgICAgICAgPHRpZmY6UmVzb2x1dGlvblVuaXQ+MjwvdGlmZjpSZXNvbHV0aW9uVW5pdD4KICAgICAgICAgPHRpZmY6WVJlc29sdXRpb24+MTQ0PC90aWZmOllSZXNvbHV0aW9uPgogICAgICAgICA8dGlmZjpYUmVzb2x1dGlvbj4xNDQ8L3RpZmY6WFJlc29sdXRpb24+CiAgICAgICAgIDxleGlmOlBpeGVsWERpbWVuc2lvbj4yNTY8L2V4aWY6UGl4ZWxYRGltZW5zaW9uPgogICAgICAgICA8ZXhpZjpDb2xvclNwYWNlPjE8L2V4aWY6Q29sb3JTcGFjZT4KICAgICAgICAgPGV4aWY6UGl4ZWxZRGltZW5zaW9uPjI1NjwvZXhpZjpQaXhlbFlEaW1lbnNpb24+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgrJkQrHAAAWO0lEQVR4Ae1da5icRZV+537pyfRcEdbltl5AFEkgywaDgI8sAWRZszyyYBACbAgQQgARuSmykfVBXJEIAgqEIGC4emGjQUVWMJJduUSeIIquoiJgpjPTPcncb/t+3ZlmMtPd0z3p/r5zvjr1I+n+uqerznvOW3Wq6pyqsrGxMVgxBFxFoNxVwU1uQ8BDwAhgduA0AkYAp9VvwhsBzAacRsAI4LT6TXgjgNmA0wgYAZxWvwlvBDAbcBoBI4DT6jfhjQBmA04jYARwWv0mvBHAbMBpBIwATqvfhDcCmA04jYARwGn1m/BGALMBpxEwAjitfhPeCGA24DQCRgCn1W/CGwHMBpxGwAjgtPpNeCOA2YDTCBgBnFa/CW8EMBtwGgEjgNPqN+GNAGYDTiNgBHBa/Sa8EcBswGkEjABOq9+ENwKYDTiNgBHAafWb8EYAswGnETACOK1+E94IYDbgNAJGAKfVb8IbAcwGnEbACOC0+k14I4DZgNMIGAGcVr8JbwQwG3AaASOA0+o34Y0AZgNOI2AEcFr9JrwRwGzAaQSMAE6r34Q3ApgNOI2AEcBp9ZvwRgCzAacRqAxQ+nv68JvhAOsvrOr51Ti+prA/CfDbr4/ilp4A6y+s6isbECkr7E+K9e0gCfDcEFbpUVJjOTa3Y08lQ+bSBP6rv1hGUtrf2bcSlzWUtoocvx6kPq9vxPurcrRN1kfdozgnIatJ2VrzzT411l9VhvubEA2o+yeAQRKgFvhWE+qCEz6bAWV7vr4fq/uyfSjl+RujWNEtpTHTtmPlLMwLtBMMkgBE54BK3Ng4LUqCvnBJN/4yKqg9U5tC56dLdgvTbT62FpdF0u+CeREwASj00nos5FigpMRHQQsTW+7tw2NKXP89KrAmisCH/7KxsbHA1dk5hoM68NpI4A3JtwFrmnB6Xb5f9u17dH7e26Gj+y8vw+MtOLraN2yyVhT8CMCmtZTh3iYQFC3lom7Q2qSVc/U4P5dHRFg/NSiCAGzHkdW4Iri1sEJNmU42rU1UofPzPSXOD3dUrp0lBTwRLlAKDO6JHbEVzwxKgWbadtzbjEUyZi9vJp2fTnmD0lQMW8rxQjv2ktLxihkBiBT35O5rAvebtJQVCfxVhs1xOFJh/dTsHU2CrJ/tkWVu+1bgtqgW+8fWUZwvYMX9vn58V4nzsyyChcLCSQS5QGnDX5zAmt70O+kv1jbjX4NzhBQ5P7OrsLENwuwfEgmwfQxzYvidkji5tnL8qh3tAQ2lC7vwHQ3df0M5nm3DfhXiurOA9JYTh4YyL0SCUSIqSmwUywJyhO7v12H91OPNjRKtnw2TSAA2a24VrhOzUjYtDx/qw8O+d8N0fi4UthSbDajT63GGvH3DVGslukCplnGDekEnfjSQDVVZz3erwEvtaPNx1NLi/OxX6Tk/HNVlFqEjAMEiYow4aJfnNWZU5JYRLPexP/6WEuenht5ss1zrpyrlEoCN26Mcq/Wsiq7tw7d9Ga+4+eAn2TISPs+HNzRiTpA5V9M3UzQB2PyP1ODCoCNmp0dx/Bvnc0Oq9LGF5yW8LQj55aO1WF4vvZly5wBp5NirHhrDi0PpB6JfLKrzAvtKV9b249Su0v180X55rwov5IFhjsKLAgIQwV8NY24MfaXvXIuire+24MTS7PfQ+WHAs/zuv7IMT7bi8EBTvfJUpXQXKCWGrsQxLyy5NFxl5IV866fKrmnQYf1sqg4CsKFMHPsXqWvJKZam/31jBEwYKHp5oB+Pis9IptQfrgGPOdFSdLhAKTR1JY6taynmOUJbks4Pd52FF+6HbGrzlu+0FD0t1ZY4xjNUEsVzhOj8yLf+sjIvzVeR9ZOlmgjA5jJxTMvw+pcR8AiJopQH+/GIBufn0giOLc3svygwZvwRTS5QSgBdiWPrW7Fg11K/tTg/86rxVCs0LPzsRARlIwDbzo1F7ywxJQ1fEkf3rjlCDDWV7/w0lXtKUWf9NCcldrQTabGPnsSxP4/g0l1whB7qx8ManJ+vR8FsPo1FnwuURvnMBO5Wkjj2o9aZnALSwZWfGDrEH5d0Tj1u1xOylbaf1AvFBGDi2MEx/FZD4tjeldhceEjwyXEw00B4ObAK/9MGJTs0GbBU6QKl5FCUOPbHYXxqWwb0czyi8yPf+uvLsJbHG+cQQ/xHiglAbA+pwn8oSRy7vRdP5n3kEZ2foNIsC7LYVVHveGPVRTcBCP0nI/hHDWvPPIL17AR68lsRuqBbget/ah3OVt35J4mrngCMt71HSeLYH4ZxeR6OENOLHxTv+r+jUtMJTjnGKMWT4IlSrRvACZ0THwh9zWCBJ1u8/exsJTbmBTwzwVJyqS7Dhlbv4IIQFPUjQEoHWhLHUo5QjsXbCxLSrZ+Af2FWSKyfsoSEAJTki404SEOf9H/DuDLL1tgj/XhAvPNzQi0u1pOkmuofc/wbEhcoJeHLycSx3vwmmjlAKfVHvAnhp1MSplQ4P29ntLO/p7+UXBelrsDP33+PkhvHRrkiFMekvl6+81ORvMfEz7OPfDCe8LhAKbC4La8iceyVYVw9YUXoUQ3Oz9UNOCr79N0HYy1FFaFygVIAMR+XN44xCk14oSP0s1YcVgUVzs+RNXiiBToD3nIZQthGAMrarOTGMTpCZ8XBM0V5ypXwdU+egM27S8Jn/bSWEI4AKb5/djtWTvAxcnUCgX7GHSWuCwkvj7XgBA3b7TOAMbQEoAfEG8d+nnf4zQywc+RPLooou8y8IL2ElgBE4dURzI4hIf4khYIU5vOX51Z7m76hm/q+hWII5wBp4Zg4pjdRIy1FgC94YSFvKgmx9RPbMBOA4vH2rjPFn88aoInnrvrWKN4ZypnvBLHD7AKlxGQEMhPHuO5upSAEzqrHnWoTHfOXNPwEIBbPDeEDWzEoPkQif7WV+psHVOEXbXBh7Ay5C5QyFEWJY6W27Hx+vy6Z6OiC9RMNJwhAOS+J4JiQrmTnY9MFfefLjThQeaJj/vI64QKl4OC1igfFpO+55q+5En3zY3V4sJQXfJSo2TP+WVdGAAK0e/LGMeZkWcmGwD6V4BFXThWHCEC9Hs8bxxzxbQu3Yt5MzlX/Jsc6CLcIQKu4vhGzNSSOFW7Au/oXK2dhnnvIODQHSBvIr0dwSAfkJ46lG+zDiwU1+EGLdzeza8W5EYAK3r8CX3HM081t1rtXeEfLOGj9hMVFAlDsJXU4Sf+hTrnNOs9PmZfzzSbs5qghuEoAGsc3otgz7IEu+XDg8shMTq7O55dVfMfFOUBaMU8P4UNbMeJwiMT8avx3q3fniLPF1ZEvqfAPVuEqPRd6Ft1GW3itS7PT1k9InR4BKD8Tx47cig1OJo492oKFzoeHOD0CkACcBTDdW8uNY0UcBJZFzPo9OF0nACHYu8K5/X9uBf5nYxHZpPinjACe8k6uBfM/HCkN5VjbDOd9nx3aNgLsAGJVI97txmrIzY3Yz9Z/x3s7I8AOJCIMBWsGT74Pdzm9HmfYDuAEHRsB3gLj4Eo1N4691ehCXnGIu8Vc/50Rc30ZdGc0wD2x4zrx+MCkx2F4W1OGZ9owxw03L3+F2QiwE1b0gNYwMCaMLvINjWb9O+k69cYILine truncated
|
||||||
|
webUI["html/index.html"] = "PCFkb2N0eXBlIGh0bWw+CjxodG1sPgogIDxoZWFkPgogICAgPG1ldGEgY2hhcnNldD0idXRmLTgiPgogICAgPCEtLS0KICAgIDxtZXRhIG5hbWU9InZpZXdwb3J0IiBjb250ZW50PSJ3aWR0aD1kZXZpY2Utd2lkdGgsIGluaXRpYWwtc2NhbGU9MS4wIiAvPiAKICAgIC0tPgogICAgPHRpdGxlPnhUZVZlPC90aXRsZT4KICAgIDxsaW5rIHJlbD0ic3R5bGVzaGVldCIgaHJlZj0iY3NzL3NjcmVlbi5jc3MiIHR5cGU9InRleHQvY3NzIj4KICAgIDxsaW5rIHJlbD0ic3R5bGVzaGVldCIgaHJlZj0iY3NzL2Jhc2UuY3NzIiB0eXBlPSJ0ZXh0L2NzcyI+CgogICAgPHNjcmlwdCBsYW5ndWFnZT0iamF2YXNjcmlwdCIgdHlwZT0idGV4dC9qYXZhc2NyaXB0IiBzcmM9ImpzL25ldHdvcmtfdHMuanMiPjwvc2NyaXB0PgogICAgPHNjcmlwdCBsYW5ndWFnZT0iamF2YXNjcmlwdCIgdHlwZT0idGV4dC9qYXZhc2NyaXB0IiBzcmM9ImpzL21lbnVfdHMuanMiPjwvc2NyaXB0PgogICAgPHNjcmlwdCBsYW5ndWFnZT0iamF2YXNjcmlwdCIgdHlwZT0idGV4dC9qYXZhc2NyaXB0IiBzcmM9ImpzL3NldHRpbmdzX3RzLmpzIj48L3NjcmlwdD4KICAgIDxzY3JpcHQgbGFuZ3VhZ2U9ImphdmFzY3JpcHQiIHR5cGU9InRleHQvamF2YXNjcmlwdCIgc3JjPSJqcy9sb2dzX3RzLmpzIj48L3NjcmlwdD4KICAgIDxzY3JpcHQgbGFuZ3VhZ2U9ImphdmFzY3JpcHQiIHR5cGU9InRleHQvamF2YXNjcmlwdCIgc3JjPSJqcy9iYXNlX3RzLmpzIj48L3NjcmlwdD4KCiAgPC9oZWFkPgoKICAgIDxib2R5IG9ubG9hZD0iamF2YXNjcmlwdDogUGFnZVJlYWR5KCk7Ij4KICAgICAgPGRpdiBpZD0ibG9hZGluZyIgY2xhc3M9Im5vbmUiPgogICAgICAgIDxkaXYgY2xhc3M9ImxvYWRlciI+CiAgICAgICAgICAKICAgICAgICA8L2Rpdj4KICAgICAgPC9kaXY+CgogICAgICA8ZGl2IGlkPSJwb3B1cCIgY2xhc3M9Im5vbmUiPgoKICAgICAgICA8ZGl2IGlkPSJwb3B1cC1jdXN0b20iPjwvZGl2PgoKICAgICAgPC9kaXY+CgogICAgICA8ZGl2IGlkPSJsYXlvdXQiPgoKICAgICAgICA8IS0tCiAgICAgICAgPGRpdiBpZD0ibm90aWZpY2F0aW9uIj4KICAgICAgICAgIDxkaXYgY2xhc3M9ImVsZW1lbnQiPgogICAgICAgICAgICA8aDU+WEVQRzwvaDU+CiAgICAgICAgICAgIDxwcmU+MTEuMDUuMjAxOSAtIDIwOjIxPC9wcmU+CiAgICAgICAgICAgIDxocj4KICAgICAgICAgICAgPHA+SGFsbG8gZGFzIGlzdCBlaW4gVGVzdC4gVW5kIG5vY2ggbWVociBUZXh0LjwvcD4KICAgICAgICAgIDwvZGl2PgogICAgICAgIDwvZGl2PgogICAgICAtLT4KCiAgICAgICAgPGRpdiBpZD0ibWVudS13cmFwcGVyIiBjbGFzcz0ibGF5b3V0LWxlZnQiPgogICAgICAgICAgPGRpdiBpZD0gImJyYW5jaCI+PC9kaXY+CiAgICAgICAgICA8ZGl2IGlkPSJsb2dvIj48L2Rpdj4KICAgICAgICAgIDxuYXYgaWQ9Im1haW4tbWVudSI+PC9uYXY+CiAgICAgICAgPC9kaXY+CgogICAgICAgIDxkaXYgY2xhc3M9ImxheW91dC1yaWdodCI+CiAgICAgICAgICA8dGFibGUgaWQ9ImNsaWVudEluZm8iIGNsYXNzPSIiPgogICAgICAgICAgICA8dHI+CiAgICAgICAgICAgICAgPHRkIGNsYXNzPSJ0ZEtleSI+eFRlVmU6PC90ZD4KICAgICAgICAgICAgICA8dGQgaWQ9InZlcnNpb24iIGNsYXNzPSJ0ZFZhbCI+Jm5ic3A7PC90ZD4KICAgICAgICAgICAgICA8dGQgY2xhc3M9InRkS2V5Ij5PUzo8L3RkPgogICAgICAgICAgICAgIDx0ZCBpZD0ib3MiIGNsYXNzPSJ0ZFZhbCI+Jm5ic3A7PC90ZD4KICAgICAgICAgICAgICA8dGQgY2xhc3M9InRkS2V5IHBob25lIj5EVlIgSVA6PC90ZD4KICAgICAgICAgICAgICA8dGQgaWQ9IkRWUiIgY2xhc3M9InRkVmFsIHBob25lIj4mbmJzcDs8L3RkPgogICAgICAgICAgICA8L3RyPgogICAgICAgICAgICA8dHI+CiAgICAgICAgICAgICAgPHRkIGNsYXNzPSJ0ZEtleSI+VVVJRDo8L3RkPgogICAgICAgICAgICAgIDx0ZCBpZD0idXVpZCIgY2xhc3M9InRkVmFsIj4mbmJzcDs8L3RkPgogICAgICAgICAgICAgIDx0ZCBjbGFzcz0idGRLZXkiPkFyY2g6PC90ZD4KICAgICAgICAgICAgICA8dGQgaWQ9ImFyY2giIGNsYXNzPSJ0ZFZhbCI+Jm5ic3A7PC90ZD4KICAgICAgICAgICAgICA8dGQgY2xhc3M9InRkS2V5IHBob25lIj5NM1UgVVJMOjwvdGQ+CiAgICAgICAgICAgICAgPHRkIGlkPSJtM3UtdXJsIiBjbGFzcz0idGRWYWwgcGhvbmUiPiZuYnNwOzwvdGQ+CiAgICAgICAgICAgICAgCiAgICAgICAgICAgIDwvdHI+CiAgICAgICAgICAgIDx0cj4KICAgICAgICAgICAgICA8dGQgY2xhc3M9InRkS2V5Ij5BdmFpbGFibGUgU3RyZWFtczo8L3RkPgogICAgICAgICAgICAgIDx0ZCBpZD0ic3RyZWFtcyIgY2xhc3M9InRkVmFsIj4mbmJzcDs8L3RkPgogICAgICAgICAgICAgIDx0ZCBjbGFzcz0idGRLZXkiPkVQRyBTb3VyY2U6PC90ZD4KICAgICAgICAgICAgICA8dGQgaWQ9ImVwZ1NvdXJjZSIgY2xhc3M9InRkVmFsIj4mbmJzcDs8L3RkPgogICAgICAgICAgICAgIDx0ZCBjbGFzcz0idGRLZXkgcGhvbmUiPlhFUEcgVVJMOjwvdGQ+CiAgICAgICAgICAgICAgPHRkIGlkPSJ4ZXBnLXVybCIgY2xhc3M9InRkVmFsIHBob25lIj4mbmJzcDs8L3RkPgogICAgICAgICAgICA8L3RyPgogICAgICAgICAgICA8dHI+CiAgICAgICAgICAgICAgPHRkIGNsYXNzPSJ0ZEtleSI+WEVQRyBDaGFubmVsczo8L3RkPgogICAgICAgICAgICAgIDx0ZCBpZD0ieGVwZyIgY2xhc3M9InRkVmFsIj4mbmJzcDs8L3RkPgogICAgICAgICAgICAgIDx0ZCBjbGFzcz0idGRLZXkiPkVycm9yczo8L3RkPgogICAgICAgICAgICAgIDx0ZCBpZD0iZXJyb3JzIiBjbGFzcz0idGRWYWwiPiZuYnNwOzwvdGQ+CiAgICAgICAgICAgICAgPHRkIGNsYXNzPSJ0ZEtleSI+V2FybmluZ3M6PC90ZD4KICAgICAgICAgICAgICA8dGQgaWQ9Indhcm5pbmdzIiBjbGFzcz0idGRWYWwiPiZuYnNwOzwvdGQ+CiAgICAgICAgICAgIDwvdHI+CiAgICAgICAgICA8L3RhYmxlPgogICAgICAgICAKICAgICAgICAgIDxkaXYgaWQ9Im15U3RyZWFtc0JveCIgY2xhc3M9Im5vdFZpc2libGUiPgogICAgICAgICAgICA8ZGl2IGlkPSJhbGxTdHJlYW1zIj4KICAgICAgICAgICAgICA8dGFibGUgaWQ9ImFjdGl2ZVN0cmVhbXMiPjwvdGFibGU+CiAgICAgICAgICAgICAgPHRhYmxlIGlkPSJpbmFjdGl2ZVN0cmVhbXMiPjwvdGFibGU+CiAgICAgICAgICAgIDwvZGl2PgoKICAgICAgICAgICAgPCEtLTxkaXYgaWQ9Im9wZW5TdHJlYW1zIiBvbmNsaWNrPSJqYXZhc2NyaXB0OiBzaG93U3RyZWFtcygpOyI+PC9kaXY+LS0+CiAgICAgICAgICA8L2Rpdj4KICAgICAgICAgIAogICAgICAgICAgPGRpdiBpZD0iY29udGVudCIgY2xhc3M9IiI+PC9kaXY+CiAgICAgICAgICAgIAogICAgICAgIDwvZGl2PgoKICAgICAgPC9kaXY+CiAgICAgIAogICAgPC9ib2R5Pgo8L2h0bWw+"
|
||||||
|
webUI["html/js/settings_ts.js"] = "dmFyIF9fZXh0ZW5kcyA9ICh0aGlzICYmIHRoaXMuX19leHRlbmRzKSB8fCAoZnVuY3Rpb24gKCkgewogICAgdmFyIGV4dGVuZFN0YXRpY3MgPSBmdW5jdGlvbiAoZCwgYikgewogICAgICAgIGV4dGVuZFN0YXRpY3MgPSBPYmplY3Quc2V0UHJvdG90eXBlT2YgfHwKICAgICAgICAgICAgKHsgX19wcm90b19fOiBbXSB9IGluc3RhbmNlb2YgQXJyYXkgJiYgZnVuY3Rpb24gKGQsIGIpIHsgZC5fX3Byb3RvX18gPSBiOyB9KSB8fAogICAgICAgICAgICBmdW5jdGlvbiAoZCwgYikgeyBmb3IgKHZhciBwIGluIGIpIGlmIChiLmhhc093blByb3BlcnR5KHApKSBkW3BdID0gYltwXTsgfTsKICAgICAgICByZXR1cm4gZXh0ZW5kU3RhdGljcyhkLCBiKTsKICAgIH07CiAgICByZXR1cm4gZnVuY3Rpb24gKGQsIGIpIHsKICAgICAgICBleHRlbmRTdGF0aWNzKGQsIGIpOwogICAgICAgIGZ1bmN0aW9uIF9fKCkgeyB0aGlzLmNvbnN0cnVjdG9yID0gZDsgfQogICAgICAgIGQucHJvdG90eXBlID0gYiA9PT0gbnVsbCA/IE9iamVjdC5jcmVhdGUoYikgOiAoX18ucHJvdG90eXBlID0gYi5wcm90b3R5cGUsIG5ldyBfXygpKTsKICAgIH07Cn0pKCk7CnZhciBTZXR0aW5nc0NhdGVnb3J5ID0gLyoqIEBjbGFzcyAqLyAoZnVuY3Rpb24gKCkgewogICAgZnVuY3Rpb24gU2V0dGluZ3NDYXRlZ29yeSgpIHsKICAgICAgICB0aGlzLkRvY3VtZW50SUQgPSAiY29udGVudF9zZXR0aW5ncyI7CiAgICB9CiAgICBTZXR0aW5nc0NhdGVnb3J5LnByb3RvdHlwZS5jcmVhdGVDYXRlZ29yeUhlYWRsaW5lID0gZnVuY3Rpb24gKHZhbHVlKSB7CiAgICAgICAgdmFyIGVsZW1lbnQgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCJINCIpOwogICAgICAgIGVsZW1lbnQuaW5uZXJIVE1MID0gdmFsdWU7CiAgICAgICAgcmV0dXJuIGVsZW1lbnQ7CiAgICB9OwogICAgU2V0dGluZ3NDYXRlZ29yeS5wcm90b3R5cGUuY3JlYXRlSFIgPSBmdW5jdGlvbiAoKSB7CiAgICAgICAgdmFyIGVsZW1lbnQgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCJIUiIpOwogICAgICAgIHJldHVybiBlbGVtZW50OwogICAgfTsKICAgIFNldHRpbmdzQ2F0ZWdvcnkucHJvdG90eXBlLmNyZWF0ZVNldHRpbmdzID0gZnVuY3Rpb24gKHNldHRpbmdzS2V5KSB7CiAgICAgICAgdmFyIHNldHRpbmcgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCJUUiIpOwogICAgICAgIHZhciBjb250ZW50ID0gbmV3IFBvcHVwQ29udGVudCgpOwogICAgICAgIHZhciBkYXRhID0gU0VSVkVSWyJzZXR0aW5ncyJdW3NldHRpbmdzS2V5XTsKICAgICAgICBzd2l0Y2ggKHNldHRpbmdzS2V5KSB7CiAgICAgICAgICAgIC8vIFRleHRlaW5nYWJlbgogICAgICAgICAgICBjYXNlICJ1cGRhdGUiOgogICAgICAgICAgICAgICAgdmFyIHRkTGVmdCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoIlREIik7CiAgICAgICAgICAgICAgICB0ZExlZnQuaW5uZXJIVE1MID0gInt7LnNldHRpbmdzLnVwZGF0ZS50aXRsZX19IiArICI6IjsKICAgICAgICAgICAgICAgIHZhciB0ZFJpZ2h0ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgiVEQiKTsKICAgICAgICAgICAgICAgIHZhciBpbnB1dCA9IGNvbnRlbnQuY3JlYXRlSW5wdXQoInRleHQiLCAidXBkYXRlIiwgZGF0YS50b1N0cmluZygpKTsKICAgICAgICAgICAgICAgIGlucHV0LnNldEF0dHJpYnV0ZSgicGxhY2Vob2xkZXIiLCAie3suc2V0dGluZ3MudXBkYXRlLnBsYWNlaG9sZGVyfX0iKTsKICAgICAgICAgICAgICAgIGlucHV0LnNldEF0dHJpYnV0ZSgib25jaGFuZ2UiLCAiamF2YXNjcmlwdDogdGhpcy5jbGFzc05hbWUgPSAnY2hhbmdlZCciKTsKICAgICAgICAgICAgICAgIHRkUmlnaHQuYXBwZW5kQ2hpbGQoaW5wdXQpOwogICAgICAgICAgICAgICAgc2V0dGluZy5hcHBlbmRDaGlsZCh0ZExlZnQpOwogICAgICAgICAgICAgICAgc2V0dGluZy5hcHBlbmRDaGlsZCh0ZFJpZ2h0KTsKICAgICAgICAgICAgICAgIGJyZWFrOwogICAgICAgICAgICBjYXNlICJiYWNrdXAucGF0aCI6CiAgICAgICAgICAgICAgICB2YXIgdGRMZWZ0ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgiVEQiKTsKICAgICAgICAgICAgICAgIHRkTGVmdC5pbm5lckhUTUwgPSAie3suc2V0dGluZ3MuYmFja3VwUGF0aC50aXRsZX19IiArICI6IjsKICAgICAgICAgICAgICAgIHZhciB0ZFJpZ2h0ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgiVEQiKTsKICAgICAgICAgICAgICAgIHZhciBpbnB1dCA9IGNvbnRlbnQuY3JlYXRlSW5wdXQoInRleHQiLCAiYmFja3VwLnBhdGgiLCBkYXRhKTsKICAgICAgICAgICAgICAgIGlucHV0LnNldEF0dHJpYnV0ZSgicGxhY2Vob2xkZXIiLCAie3suc2V0dGluZ3MuYmFja3VwUGF0aC5wbGFjZWhvbGRlcn19Iik7CiAgICAgICAgICAgICAgICBpbnB1dC5zZXRBdHRyaWJ1dGUoIm9uY2hhbmdlIiwgImphdmFzY3JpcHQ6IHRoaXMuY2xhc3NOYW1lID0gJ2NoYW5nZWQnIik7CiAgICAgICAgICAgICAgICB0ZFJpZ2h0LmFwcGVuZENoaWxkKGlucHV0KTsKICAgICAgICAgICAgICAgIHNldHRpbmcuYXBwZW5kQ2hpbGQodGRMZWZ0KTsKICAgICAgICAgICAgICAgIHNldHRpbmcuYXBwZW5kQ2hpbGQodGRSaWdodCk7CiAgICAgICAgICAgICAgICBicmVhazsKICAgICAgICAgICAgY2FzZSAidGVtcC5wYXRoIjoKICAgICAgICAgICAgICAgIHZhciB0ZExlZnQgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCJURCIpOwogICAgICAgICAgICAgICAgdGRMZWZ0LmlubmVySFRNTCA9ICJ7ey5zZXR0aW5ncy50ZW1wUGF0aC50aXRsZX19IiArICI6IjsKICAgICAgICAgICAgICAgIHZhciB0ZFJpZ2h0ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgiVEQiKTsKICAgICAgICAgICAgICAgIHZhciBpbnB1dCA9IGNvbnRlbnQuY3JlYXRlSW5wdXQoInRleHQiLCAidGVtcC5wYXRoIiwgZGF0YSk7CiAgICAgICAgICAgICAgICBpbnB1dC5zZXRBdHRyaWJ1dGUoInBsYWNlaG9sZGVyIiwgInt7LnNldHRpbmdzLnRtcFBhdGgucGxhY2Vob2xkZXJ9fSIpOwogICAgICAgICAgICAgICAgaW5wdXQuc2V0QXR0cmlidXRlKCJvbmNoYW5nZSIsICJqYXZhc2NyaXB0OiB0aGlzLmNsYXNzTmFtZSA9ICdjaGFuZ2VkJyIpOwogICAgICAgICAgICAgICAgdGRSaWdodC5hcHBlbmRDaGlsZChpbnB1dCk7CiAgICAgICAgICAgICAgICBzZXR0aW5nLmFwcGVuZENoaWxkKHRkTGVmdCk7CiAgICAgICAgICAgICAgICBzZXR0aW5nLmFwcGVuZENoaWxkKHRkUmlnaHQpOwogICAgICAgICAgICAgICAgYnJlYWs7CiAgICAgICAgICAgIGNhc2UgInVzZXIuYWdlbnQiOgogICAgICAgICAgICAgICAgdmFyIHRkTGVmdCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoIlREIik7CiAgICAgICAgICAgICAgICB0ZExlZnQuaW5uZXJIVE1MID0gInt7LnNldHRpbmdzLnVzZXJBZ2VudC50aXRsZX19IiArICI6IjsKICAgICAgICAgICAgICAgIHZhciB0ZFJpZ2h0ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgiVEQiKTsKICAgICAgICAgICAgICAgIHZhciBpbnB1dCA9IGNvbnRlbnQuY3JlYXRlSW5wdXQoInRleHQiLCAidXNlci5hZ2VudCIsIGRhdGEpOwogICAgICAgICAgICAgICAgaW5wdXQuc2V0QXR0cmlidXRlKCJwbGFjZWhvbGRlciIsICJ7ey5zZXR0aW5ncy51c2VyQWdlbnQucGxhY2Vob2xkZXJ9fSIpOwogICAgICAgICAgICAgICAgaW5wdXQuc2V0QXR0cmlidXRlKCJvbmNoYW5nZSIsICJqYXZhc2NyaXB0OiB0aGlzLmNsYXNzTmFtZSA9ICdjaGFuZ2Line truncated
|
||||||
|
webUI["html/js/base.js"] = "dmFyIGNvbmZpZyAgICAgICAgPSBuZXcgT2JqZWN0KCk7CnZhciBtZW51ICAgICAgICAgID0gbmV3IE9iamVjdCgpOwp2YXIgc3ViTWVudSAgICAgICA9IG5ldyBPYmplY3QoKTsKdmFyIGFjdGl2ZVN0cmVhbXMgPSBuZXcgT2JqZWN0KCk7CnZhciB4RVBHICAgICAgICAgID0gbmV3IE9iamVjdCgpOwp2YXIgdXNlcnMgICAgICAgICA9IG5ldyBPYmplY3QoKTsKdmFyIGxvZyAgICAgICAgICAgPSBuZXcgT2JqZWN0KCk7CnZhciB1bmRvICAgICAgICAgID0gbmV3IE9iamVjdCgpOwp2YXIgd2ViU29ja2V0cyAgICA9IHRydWU7CnZhciBjbG9zZUxvZywgdmVyc2lvbiwgYWN0aXZlTWVudTsKdmFyIGNvbHVtblRvU29ydCAgPSAwCgoKaWYgKHdpbmRvdy5XZWJTb2NrZXQgPT09IHVuZGVmaW5lZCkgewogIGFsZXJ0KCJZb3VyIGJyb3dzZXIgZG9lcyBub3Qgc3VwcG9ydCBXZWJTb2NrZXRzIik7CiAgd2ViU29ja2V0cyA9IGZhbHNlOwp9IAoKZnVuY3Rpb24gcGFnZVJlYWR5KCkgewogIHZhciBkYXRhID0gbmV3IE9iamVjdCgpOwogIGRhdGFbImNtZCJdID0gImdldFNlcnZlckNvbmZpZyI7CiAgeFRlVmUoZGF0YSk7CiAgLy9zaG93TG9hZGluZ1NjcmVlbihmYWxzZSk7CgogIHZhciByZXNpemVIYW5kbGUgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgib3BlblN0cmVhbXMiKTsKICB2YXIgYm94ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoIm15U3RyZWFtc0JveCIpOwogIHJlc2l6ZUhhbmRsZS5hZGRFdmVudExpc3RlbmVyKCJtb3VzZWRvd24iLCBpbml0aWFsaXNlUmVzaXplLCBmYWxzZSk7CgogIGZ1bmN0aW9uIGluaXRpYWxpc2VSZXNpemUoZSkgewogICAgd2luZG93LmFkZEV2ZW50TGlzdGVuZXIoIm1vdXNlbW92ZSIsIHN0YXJ0UmVzaXppbmcsIGZhbHNlKTsKICAgIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCJtb3VzZXVwIiwgc3RvcFJlc2l6aW5nLCBmYWxzZSk7CiAgfQoKICBmdW5jdGlvbiBzdGFydFJlc2l6aW5nKGUpIHsKICAgIGJveC5zdHlsZS5oZWlnaHQgPSAoZS5jbGllbnRZIC0gYm94Lm9mZnNldFRvcCkgKyAicHgiOwogICAgICAKICAgIHZhciBlbG0gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgiYWxsU3RyZWFtcyIpOwogICAgaWYgKGUuY2xpZW50WSA+IDEyMCkgewogICAgICBlbG0uY2xhc3NOYW1lID0gInZpc2libGUiOyAKICAgIH0gZWxzZSB7CiAgICAgIGVsbS5jbGFzc05hbWUgPSAibm90VmlzaWJsZSI7IAogICAgfQoKICAgIGNhbGN1bGF0ZVdyYXBwZXJIZWlnaHQoKTsKCiAgfQogIGZ1bmN0aW9uIHN0b3BSZXNpemluZyhlKSB7CiAgICAgIHdpbmRvdy5yZW1vdmVFdmVudExpc3RlbmVyKCdtb3VzZW1vdmUnLCBzdGFydFJlc2l6aW5nLCBmYWxzZSk7CiAgICAgIHdpbmRvdy5yZW1vdmVFdmVudExpc3RlbmVyKCdtb3VzZXVwJywgc3RvcFJlc2l6aW5nLCBmYWxzZSk7CiAgICAgIGNhbGN1bGF0ZVdyYXBwZXJIZWlnaHQoKTsKICB9CgogIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCJyZXNpemUiLCBmdW5jdGlvbigpewogICAgY2FsY3VsYXRlV3JhcHBlckhlaWdodCgpOwogIH0sIHRydWUpOwp9CgoKZnVuY3Rpb24gZ2V0T2JqS2V5cyhvYmopIHsKICB2YXIga2V5cyA9IG5ldyBBcnJheSgpOwoKICBmb3IgKHZhciBpIGluIG9iaikgewogICAgaWYgKG9iai5oYXNPd25Qcm9wZXJ0eShpKSkgewogICAgICBrZXlzLnB1c2goaSk7CiAgICB9CiAgfQoKICByZXR1cm4ga2V5czsKfQoKCmZ1bmN0aW9uIGNyZWF0ZUVsZW1lbnQoaXRlbSkgewogIC8vY29uc29sZS5sb2coaXRlbSk7CiAgdmFyIGVsZW1lbnQgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KGl0ZW1bIl9lbGVtZW50Il0pOwogIGlmIChpdGVtLmhhc093blByb3BlcnR5KCJfdGV4dCIpKSB7CiAgICAvL2VsZW1lbnQuaW5uZXJIVE1MID0gIjxwPiIgKyBpdGVtWyJfdGV4dCJdICsgIjwvcD4iOwogICAgZWxlbWVudC5pbm5lckhUTUwgPSBpdGVtWyJfdGV4dCJdOwogIH0KCiAgdmFyIGtleXMgPSBnZXRPYmpLZXlzKGl0ZW0pOwogIGZvciAodmFyIGkgPSAwOyBpIDwga2V5cy5sZW5ndGg7IGkrKykgewogICAgaWYgKGtleXNbaV0uY2hhckF0KDApICE9ICJfIikgewogICAgICAvL2NvbnNvbGUubG9nKGtleXNbaV0sIGl0ZW1ba2V5c1tpXV0pOwogICAgICBlbGVtZW50LnNldEF0dHJpYnV0ZShrZXlzW2ldLCBpdGVtW2tleXNbaV1dKTsKICAgIH0KICB9CgogIC8vY29uc29sZS5sb2coZWxlbWVudCk7CiAgcmV0dXJuIGVsZW1lbnQ7Cn0KCmZ1bmN0aW9uIG1vZGlmeU9wdGlvbihpZCwgb3B0aW9ucywgdmFsdWVzKSB7CiAgdmFyIHNlbGVjdCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKGlkKTsKICBzZWxlY3QuaW5uZXJIVE1MID0gIiI7CgogIGZvciAodmFyIGkgPSAwOyBpIDwgb3B0aW9ucy5sZW5ndGg7IGkrKykgewogIAogICAgdmFyIGVsZW1lbnQgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCJPUFRJT04iKQogIAogICAgZWxlbWVudC52YWx1ZSA9IHZhbHVlc1tpXTsKICAgIGVsZW1lbnQuaW5uZXJIVE1MID0gb3B0aW9uc1tpXTsKCiAgICBkb2N1bWVudC5nZXRFbGVtZW50QnlJZChpZCkuYXBwZW5kQ2hpbGQoZWxlbWVudCk7CiAgCiAgfQoKfQoKCmZ1bmN0aW9uIHN0YXJ0V2ViU29ja2V0KCkgewogIGlmICh3ZWJTb2NrZXRzID09IGZhbHNlKSB7CiAgICByZXR1cm47CiAgfQoKICAvL3dzLnNlbmQoJ3siY21kIjogImdldFNlcnZlckNvbmZpZzEifScpOwoKfQoKZnVuY3Rpb24gY2hlY2tFcnIob2JqKSB7CiAgLy9hbGVydChvYmpbImVyciJdKQogIC8vc2NyZWVuTG9nKG9ialsiZXJyIl0sICJlcnJvciIpCiAgY29uc29sZS5sb2cob2JqKTsKICB2YXIgbmV3T2JqID0gbmV3IE9iamVjdCgpOwogIHZhciBuZXdFcnIgPSBuZXcgT2JqZWN0KCk7CiAgbmV3RXJyWyJrZXkiXSAgID0gIkVycm9yIjsKICBuZXdFcnJbInZhbHVlIl0gPSBvYmpbImVyciJdOwogIG5ld0VyclsidHlwZSJdICA9ICJlcnJvciI7CgogIG5ld09ialswXSA9IG5ld0VycgogIHNob3dMb2cobmV3T2JqKTsKICByZXR1cm4KfQoKZnVuY3Rpb24gc2NyZWVuTG9nKG1zZywgbXNnVHlwZSwgc2hvdykgewogIHJldHVybgogIGNsZWFyVGltZW91dChjbG9zZUxvZykKICB2YXIgZGl2ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoInNjcmVlbkxvZyIpOwogIHZhciBuZXdNc2cgPSBuZXcgT2JqZWN0KCk7CiAgCiAgbmV3TXNnWyJfZWxlbWVudCJdID0gIlAiOwogIAogIHN3aXRjaChtc2dUeXBlKSB7CiAgICBjYXNlICJlcnJvciI6ICAgbmV3TXNnWyJjbGFzcyJdID0gImVycm9yTXNnIjsgYnJlYWs7CiAgICBjYXNlICJ3YXJuaW5nIjogbmV3TXNnWyJjbGFzcyJdID0gIndhcm5pbmdNc2ciOyBicmVhazsKICAgIC8vZGVmYXVsdDogICAgICBuZXdNc2dbImNsYXNzIl0gPSAiaW5mb01zZyIKICB9CgogIG5ld01zZ1siX3RleHQiXSA9IG1zZzsKCiAgZGl2LmFwcGVuZENoaWxkKGNyZWF0ZUVsZW1lbnQobmV3TXNnKSk7CgogIGRpdi5zY3JvbGxUb3AgPSBkaXYuc2Nyb2xsSGVpZ2h0OwoKICBpZiAoc2hvdyA9PSBmYWxzZSkgewogICAgcmV0dXJuOwogIH0KCiAgZGl2LmNsYXNzTmFtZSA9ICIiCiAgY2xvc2VMb2cgPSBzZXRUaW1lb3V0KGNsb3NlU2NyZWVuTG9nLCAxMDAwMCk7Cn0KCgpmdW5jdGlvbiBjbG9zZVNjcmVlbkxvZygpIHsKICB2YXIgZGl2ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoInNjcmVlbkxvZyIpOwogIGRpdi5jbGFzc05hbWUgPSAic2NyZWVuTG9nSGlkZGVuIgp9CgpmdW5jdGlvbiBzaLine truncated
|
||||||
|
webUI["html/js/classes_ts.js"] = "dmFyIF9fZXh0ZW5kcyA9ICh0aGlzICYmIHRoaXMuX19leHRlbmRzKSB8fCAoZnVuY3Rpb24gKCkgewogICAgdmFyIGV4dGVuZFN0YXRpY3MgPSBmdW5jdGlvbiAoZCwgYikgewogICAgICAgIGV4dGVuZFN0YXRpY3MgPSBPYmplY3Quc2V0UHJvdG90eXBlT2YgfHwKICAgICAgICAgICAgKHsgX19wcm90b19fOiBbXSB9IGluc3RhbmNlb2YgQXJyYXkgJiYgZnVuY3Rpb24gKGQsIGIpIHsgZC5fX3Byb3RvX18gPSBiOyB9KSB8fAogICAgICAgICAgICBmdW5jdGlvbiAoZCwgYikgeyBmb3IgKHZhciBwIGluIGIpIGlmIChiLmhhc093blByb3BlcnR5KHApKSBkW3BdID0gYltwXTsgfTsKICAgICAgICByZXR1cm4gZXh0ZW5kU3RhdGljcyhkLCBiKTsKICAgIH07CiAgICByZXR1cm4gZnVuY3Rpb24gKGQsIGIpIHsKICAgICAgICBleHRlbmRTdGF0aWNzKGQsIGIpOwogICAgICAgIGZ1bmN0aW9uIF9fKCkgeyB0aGlzLmNvbnN0cnVjdG9yID0gZDsgfQogICAgICAgIGQucHJvdG90eXBlID0gYiA9PT0gbnVsbCA/IE9iamVjdC5jcmVhdGUoYikgOiAoX18ucHJvdG90eXBlID0gYi5wcm90b3R5cGUsIG5ldyBfXygpKTsKICAgIH07Cn0pKCk7CnZhciBNYWluTWVudSA9IC8qKiBAY2xhc3MgKi8gKGZ1bmN0aW9uICgpIHsKICAgIGZ1bmN0aW9uIE1haW5NZW51KCkgewogICAgICAgIHRoaXMuRG9jdW1lbnRJRCA9ICJtYWluLW1lbnUiOwogICAgICAgIHRoaXMuSFRNTFRhZyA9ICJMSSI7CiAgICB9CiAgICBNYWluTWVudS5wcm90b3R5cGUuY3JlYXRlID0gZnVuY3Rpb24gKCkgewogICAgICAgIGNvbnNvbGUubG9nKHRoaXMuRG9jdW1lbnRJRCk7CiAgICB9OwogICAgcmV0dXJuIE1haW5NZW51Owp9KCkpOwp2YXIgTWFpbk1lbnVJdGVtID0gLyoqIEBjbGFzcyAqLyAoZnVuY3Rpb24gKF9zdXBlcikgewogICAgX19leHRlbmRzKE1haW5NZW51SXRlbSwgX3N1cGVyKTsKICAgIGZ1bmN0aW9uIE1haW5NZW51SXRlbSgpIHsKICAgICAgICByZXR1cm4gX3N1cGVyICE9PSBudWxsICYmIF9zdXBlci5hcHBseSh0aGlzLCBhcmd1bWVudHMpIHx8IHRoaXM7CiAgICB9CiAgICBNYWluTWVudUl0ZW0ucHJvdG90eXBlLmNyZWF0ZTIgPSBmdW5jdGlvbiAoKSB7CiAgICAgICAgdmFyIGVsZW1lbnQgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KHRoaXMuSFRNTFRhZyk7CiAgICAgICAgZWxlbWVudC5pbm5lclRleHQgPSB0aGlzLlZhbHVlOwogICAgICAgIGNvbnNvbGUubG9nKGVsZW1lbnQpOwogICAgfTsKICAgIHJldHVybiBNYWluTWVudUl0ZW07Cn0oTWFpbk1lbnUpKTsKZnVuY3Rpb24gcGFnZVJlYWR5KCkgewogICAgdmFyIGl0ZW0gPSBuZXcgTWFpbk1lbnVJdGVtKCk7CiAgICBpdGVtLlZhbHVlID0gIlRlc3QiOwogICAgaXRlbS5jcmVhdGUyKCk7Cn0K"
|
||||||
|
webUI["html/js/mapping-editor.js"] = "dmFyIG1hcHBpbmdFcnJvciAgPSBmYWxzZTsKdmFyIGJ1bGsgICAgICAgICAgPSBmYWxzZTsKdmFyIGJ1bGtFZGl0QWxsICAgPSBmYWxzZTsgCnZhciBzZWxlY3RPYmogICAgID0gbmV3IE9iamVjdCgpOwp2YXIgc2VhcmNoT2JqICAgICA9IG5ldyBPYmplY3QoKTsKCnZhciBidWxrSURzICAgICAgID0gbmV3IEFycmF5KCk7CnZhciBidWxrQ2hhbmdlT2JqID0gbmV3IE9iamVjdCgpOwoKZnVuY3Rpb24gY2hlY2tVbmRvKGtleSwgZWxtKSB7CiAgdmFyIHRtcCA9IG5ldyBPYmplY3QoKTsKICB0bXAgPSBlbG0KICBjb25zb2xlLmxvZygiLS0iKTsKICBpZiAodW5kby5oYXNPd25Qcm9wZXJ0eSgiZXBnTWFwcGluZyIpKSB7CiAgICB4RVBHWyJlcGdNYXBwaW5nIl0gPSBKU09OLnBhcnNlKEpTT04uc3RyaW5naWZ5KHVuZG9bImVwZ01hcHBpbmciXSkpOzsKICB9IGVsc2UgewogICAgdW5kb1siZXBnTWFwcGluZyJdID0gSlNPTi5wYXJzZShKU09OLnN0cmluZ2lmeShlbG0pKTsKICB9Cn0KCi8vdmFyIHBsZXhDYXRlZ29yaWVzID0gbmV3IEFycmF5KCItIiwgIkFjdGlvbiBzcG9ydHMiLCAiQWN0aW9uIiwgIkFkdWx0cyBvbmx5IiwgIkFkdmVudHVyZSIsICJBZXJvYmljcyIsICJBbmltYWxzIiwgIkFuaW1hdGVkIiwgIkFuaW1lIiwgIkFudGhvbG9neSIsICJBcmNoZXJ5IiwgIkFydCIsICJBcnRzL2NyYWZ0cyIsICJBdWN0aW9uIiwgIkF1dG8gcmFjaW5nIiwgIkF1dG8iLCAiQXZpYXRpb24iLCAiQXdhcmRzIiwgIkJhbGxldCIsICJCYXNlYmFsbCIsICJCYXNrZXRiYWxsIiwgIkJpY3ljbGUgcmFjaW5nIiwgIkJpY3ljbGUiLCAiQmlsbGlhcmRzIiwgIkJpb2dyYXBoeSIsICJCb2F0IHJhY2luZyIsICJCb2F0IiwgIkJvd2xpbmciLCAiQm94aW5nIiwgIkJ1cy4vZmluYW5jaWFsIiwgIkNoaWxkcmVuIiwgIkNvbGxlY3RpYmxlcyIsICJDb21lZHkgZHJhbWEiLCAiQ29tZWR5IiwgIkNvbW11bml0eSIsICJDb21wdXRlcnMiLCAiQ29uc3VtZXIiLCAiQ29va2luZyIsICJDcmltZSBkcmFtYSIsICJDcmltZSIsICJEYW5jZSIsICJEYXJrIGNvbWVkeSIsICJEZWJhdGUiLCAiRGl2aW5nIiwgIkRvY3VkcmFtYSIsICJEb2N1bWVudGFyeSIsICJEcmFtYSIsICJFZHVjYXRpb25hbCIsICJFbnRlcnRhaW5tZW50IiwgIkVudmlyb25tZW50IiwgIkVxdWVzdHJpYW4iLCAiRXJvdGljIiwgIkV2ZW50IiwgIkZhbnRhc3kiLCAiRmFzaGlvbiIsICJGZWF0dXJlIEZpbG0iLCAiRmlzaGluZyIsICJGb290YmFsbCIsICJHYW1lIHNob3ciLCAiR2FtaW5nIiwgIkdheS9sZXNiaWFuIiwgIkdvbGYiLCAiSGFuZGJhbGwiLCAiSGVhbHRoIiwgIkhpc3RvcmljYWwgZHJhbWEiLCAiSGlzdG9yeSIsICJIb2NrZXkiLCAiSG9saWRheSIsICJIb21lIGltcHJvdmVtZW50IiwgIkhvcnJvciIsICJIb3JzZSIsICJIb3VzZS9nYXJkZW4iLCAiSG93LXRvIiwgIkludGVydmlldyIsICJJbnRsIHNvY2NlciIsICJMYXciLCAiTWFydGlhbCBhcnRzIiwgIk1lZGljYWwiLCAiTWlsaXRhcnkiLCAiTWluaXNlcmllcyIsICJNaXhlZCBtYXJ0aWFsIGFydHMiLCAiTW90b3JjeWNsZSByYWNpbmciLCAiTW90b3JjeWNsZSIsICJNb3RvcnNwb3J0cyIsICJNb3VudGFpbiBiaWtpbmciLCAiTXVzaWMiLCAiTXVzaWNhbCBjb21lZHkiLCAiTXVzaWNhbCIsICJNeXN0ZXJ5IiwgIk5hdHVyZSIsICJOZXdzIiwgIk5ld3NtYWdhemluZSIsICJPbHltcGljcyIsICJPcGVyYSIsICJPdXRkb29ycyIsICJQYXJhZGUiLCAiUGFyYW5vcm1hbCIsICJQYXJlbnRpbmciLCAiUGVyZm9ybWluZyBhcnRzIiwgIlBsYXlvZmYgc3BvcnRzIiwgIlBva2VyIiwgIlBvbGl0aWNzIiwgIlBybyB3cmVzdGxpbmciLCAiUHVibGljIGFmZmFpcnMiLCAiUmVhbGl0eSIsICJSZWxpZ2lvdXMiLCAiUm9kZW8iLCAiUm9sbGVyIGRlcmJ5IiwgIlJvbWFuY2UiLCAiUm9tYW50aWMgY29tZWR5IiwgIlJ1Z2J5IiwgIlJ1bm5pbmciLCAiU2FpbGluZyIsICJTY2llbmNlIGZpY3Rpb24iLCAiU2NpZW5jZSIsICJTZWxmIGltcHJvdmVtZW50IiwgIlNlcmllcyIsICJTaG9vdGluZyIsICJTaG9wcGluZyIsICJTaG9ydCBGaWxtIiwgIlNpdGNvbSIsICJTa2lpbmciLCAiU25vb2tlciIsICJTb2FwIiwgIlNvY2NlciIsICJTcGVjaWFsIiwgIlNwb3J0cyIsICJzcG9ydHMiLCAiU3BvcnRzIGV2ZW50IiwgIlNwb3J0cyBub24tZXZlbnQiLCAiU3BvcnRzIHRhbGsiLCAiU3RhbmR1cCIsICJTdXJmaW5nIiwgIlN1c3BlbnNlIiwgIlRWIE1vdmllIiwgIlRhbGsiLCAiVGVjaG5vbG9neSIsICJUZW5uaXMiLCAiVGhlYXRlciIsICJUaHJpbGxlciIsICJUcmFjay9maWVsZCIsICJUcmF2ZWwiLCAiVHJpYXRobG9uIiwgIlZhcmlldHkiLCAiVm9sbGV5YmFsbCIsICJXYXIiLCAiV2F0ZXJzcG9ydHMiLCAiV2VhdGhlciIsICJXZXN0ZXJuIiwgIldyZXN0bGluZyIsICJZYWNodCByYWNpbmciLCAibW92aWUiLCAic2VyaWVzIiwgInNwb3J0cyIsICJ0dnNob3ciKTsKdmFyIHBsZXhDYXRlZ29yaWVzVmFsdWVzID0gbmV3IEFycmF5KCItIiwgIktpZHMiLCAiTmV3cyIsICJNb3ZpZSIsICJTZXJpZXMiLCAiU3BvcnRzIikKdmFyIHBsZXhDYXRlZ29yaWVzT3B0aW9uID0gbmV3IEFycmF5KCItIiwgIktpZHMgKEVtYnkgb25seSkiLCAiTmV3cyIsICJNb3ZpZSIsICJTZXJpZXMiLCAiU3BvcnRzIikKCgpmdW5jdGlvbiBvcGVuTWFwcGluZ0VkaXRvcihlbG0pIHsKICB2YXIgY29sdW1uVG9Tb3J0ICA9IDEKCiAgY2hlY2tVbmRvKCJlcGdNYXBwaW5nIiwgeEVQR1siZXBnTWFwcGluZyJdKQoKICB2YXIgbmV3RGl2ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoInNldHRpbmdzIik7CiAgCiAgdmFyIG5ld0VudHJ5ID0gbmV3IE9iamVjdCgpOwogIG5ld0VudHJ5WyJfZWxlbWVudCJdICA9ICJIUiI7CiAgbmV3RGl2LmFwcGVuZENoaWxkKGNyZWF0ZUVsZW1lbnQobmV3RW50cnkpKTsKCiAgdmFyIG5ld0VudHJ5ID0gbmV3IE9iamVjdCgpOwogIG5ld0VudHJ5WyJfZWxlbWVudCJdICAgID0gIklOUFVUIjsKICBuZXdFbnRyeVsidHlwZSJdICAgICAgICA9ICJidXR0b24iOwogIG5ld0VudHJ5WyJjbGFzcyJdICAgICAgID0gImJ1dHRvbiI7CiAgbmV3RW50cnlbInZhbHVlIl0gICAgICAgPSAiU2F2ZSI7CiAgbmV3RW50cnlbIm9uY2xpY2siXSAgICAgPSAic2F2ZVhFUEcoKSI7CiAgbmV3RGl2LmFwcGVuZENoaWxkKGNyZWF0ZUVsZW1lbnQobmV3RW50cnkpKTsKCiAgdmFyIG5ld0VudHJ5ID0gbmV3IE9iamVjdCgpOwogIG5ld0VudHJ5WyJfZWxlbWVudCJdICAgID0gIklOUFVUIjsKICBuZXdFbnRyeVsidHlwZSJdICAgICAgICA9ICJidXR0b24iOwogIG5ld0VudHJ5WyJjbGFzcyJdICAgICAgID0gImJ1dHRvbiI7CiAgbmV3RW50cnlbInZhbHVlIl0gICAgICAgPSAiQnVsayBFZGl0IjsKICBuZXdFbnRyeVsib25jbGljayJdICAgICA9ICJidWxrRWRpdCgpIjsKICBuZXdEaXYuYXBwZW5kQ2hpbGQoY3JlYXRlRWxlbWVudChuZXdFbnRyeSkpOwoKICB2YXIgbmV3RW50cnkgPSBuZXcgT2JqZWN0KCk7CiAgbmV3RW50cnlbIl9lbGVtZW50Il0gICAgPSAiSU5QVVQiOwogIG5ld0VudHJ5WyJ0eXBlIl0gICAgICAgID0gImJ1dHRvbiI7CiAgbmV3RW50cnlbImNsYXNzIl0gICAgICAgPSAiYnV0dG9uIjsKICBuZXdFbnRyeVsidmFsdWUiXSAgICAgICA9ICJTaG93IFhFUEciOwogIG5ld0VudHJ5WyJvbmNsaWNrIl0gICAgID0gInNob3dYRVBHKCkiOwogIG5ld0Rpdi5hcHBlbmRDaGlLine truncated
|
||||||
|
webUI["html/js/network_ts.js"] = "dmFyIFNlcnZlciA9IC8qKiBAY2xhc3MgKi8gKGZ1bmN0aW9uICgpIHsKICAgIGZ1bmN0aW9uIFNlcnZlcihjbWQpIHsKICAgICAgICB0aGlzLmNtZCA9IGNtZDsKICAgIH0KICAgIFNlcnZlci5wcm90b3R5cGUucmVxdWVzdCA9IGZ1bmN0aW9uIChkYXRhKSB7CiAgICAgICAgaWYgKFNFUlZFUl9DT05ORUNUSU9OID09IHRydWUpIHsKICAgICAgICAgICAgcmV0dXJuOwogICAgICAgIH0KICAgICAgICBTRVJWRVJfQ09OTkVDVElPTiA9IHRydWU7CiAgICAgICAgY29uc29sZS5sb2coZGF0YSk7CiAgICAgICAgaWYgKHRoaXMuY21kICE9ICJ1cGRhdGVMb2ciKSB7CiAgICAgICAgICAgIHNob3dFbGVtZW50KCJsb2FkaW5nIiwgdHJ1ZSk7CiAgICAgICAgICAgIFVORE8gPSBuZXcgT2JqZWN0KCk7CiAgICAgICAgfQogICAgICAgIHN3aXRjaCAod2luZG93LmxvY2F0aW9uLnByb3RvY29sKSB7CiAgICAgICAgICAgIGNhc2UgImh0dHA6IjoKICAgICAgICAgICAgICAgIHRoaXMucHJvdG9jb2wgPSAid3M6Ly8iOwogICAgICAgICAgICAgICAgYnJlYWs7CiAgICAgICAgICAgIGNhc2UgImh0dHBzOi8vIjoKICAgICAgICAgICAgICAgIHRoaXMucHJvdG9jb2wgPSAid3NzOi8vIjsKICAgICAgICAgICAgICAgIGJyZWFrOwogICAgICAgIH0KICAgICAgICB2YXIgdXJsID0gdGhpcy5wcm90b2NvbCArIHdpbmRvdy5sb2NhdGlvbi5ob3N0bmFtZSArICI6IiArIHdpbmRvdy5sb2NhdGlvbi5wb3J0ICsgIi9kYXRhLyIgKyAiP1Rva2VuPSIgKyBnZXRDb29raWUoIlRva2VuIik7CiAgICAgICAgZGF0YVsiY21kIl0gPSB0aGlzLmNtZDsKICAgICAgICB2YXIgd3MgPSBuZXcgV2ViU29ja2V0KHVybCk7CiAgICAgICAgd3Mub25vcGVuID0gZnVuY3Rpb24gKCkgewogICAgICAgICAgICBXU19BVkFJTEFCTEUgPSB0cnVlOwogICAgICAgICAgICBjb25zb2xlLmxvZygiUkVRVUVTVCAoSlMpOiIpOwogICAgICAgICAgICBjb25zb2xlLmxvZyhkYXRhKTsKICAgICAgICAgICAgY29uc29sZS5sb2coIlJFUVVFU1Q6IChKU09OKSIpOwogICAgICAgICAgICBjb25zb2xlLmxvZyhKU09OLnN0cmluZ2lmeShkYXRhKSk7CiAgICAgICAgICAgIHRoaXMuc2VuZChKU09OLnN0cmluZ2lmeShkYXRhKSk7CiAgICAgICAgfTsKICAgICAgICB3cy5vbmVycm9yID0gZnVuY3Rpb24gKGUpIHsKICAgICAgICAgICAgY29uc29sZS5sb2coIk5vIHdlYnNvY2tldCBjb25uZWN0aW9uIHRvIHhUZVZlIGNvdWxkIGJlIGVzdGFibGlzaGVkLiBDaGVjayB5b3VyIG5ldHdvcmsgY29uZmlndXJhdGlvbi4iKTsKICAgICAgICAgICAgU0VSVkVSX0NPTk5FQ1RJT04gPSBmYWxzZTsKICAgICAgICAgICAgaWYgKFdTX0FWQUlMQUJMRSA9PSBmYWxzZSkgewogICAgICAgICAgICAgICAgYWxlcnQoIk5vIHdlYnNvY2tldCBjb25uZWN0aW9uIHRvIHhUZVZlIGNvdWxkIGJlIGVzdGFibGlzaGVkLiBDaGVjayB5b3VyIG5ldHdvcmsgY29uZmlndXJhdGlvbi4iKTsKICAgICAgICAgICAgfQogICAgICAgIH07CiAgICAgICAgd3Mub25tZXNzYWdlID0gZnVuY3Rpb24gKGUpIHsKICAgICAgICAgICAgU0VSVkVSX0NPTk5FQ1RJT04gPSBmYWxzZTsKICAgICAgICAgICAgc2hvd0VsZW1lbnQoImxvYWRpbmciLCBmYWxzZSk7CiAgICAgICAgICAgIGNvbnNvbGUubG9nKCJSRVNQT05TRToiKTsKICAgICAgICAgICAgdmFyIHJlc3BvbnNlID0gSlNPTi5wYXJzZShlLmRhdGEpOwogICAgICAgICAgICBjb25zb2xlLmxvZyhyZXNwb25zZSk7CiAgICAgICAgICAgIGlmIChyZXNwb25zZS5oYXNPd25Qcm9wZXJ0eSgidG9rZW4iKSkgewogICAgICAgICAgICAgICAgZG9jdW1lbnQuY29va2llID0gIlRva2VuPSIgKyByZXNwb25zZVsidG9rZW4iXTsKICAgICAgICAgICAgfQogICAgICAgICAgICBpZiAocmVzcG9uc2VbInN0YXR1cyJdID09IGZhbHNlKSB7CiAgICAgICAgICAgICAgICBhbGVydChyZXNwb25zZVsiZXJyIl0pOwogICAgICAgICAgICAgICAgaWYgKHJlc3BvbnNlLmhhc093blByb3BlcnR5KCJyZWxvYWQiKSkgewogICAgICAgICAgICAgICAgICAgIGxvY2F0aW9uLnJlbG9hZCgpOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgcmV0dXJuOwogICAgICAgICAgICB9CiAgICAgICAgICAgIGlmIChyZXNwb25zZS5oYXNPd25Qcm9wZXJ0eSgibG9nb1VSTCIpKSB7CiAgICAgICAgICAgICAgICB2YXIgZGl2ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoImNoYW5uZWwtaWNvbiIpOwogICAgICAgICAgICAgICAgZGl2LnZhbHVlID0gcmVzcG9uc2VbImxvZ29VUkwiXTsKICAgICAgICAgICAgICAgIGRpdi5jbGFzc05hbWUgPSAiY2hhbmdlZCI7CiAgICAgICAgICAgICAgICByZXR1cm47CiAgICAgICAgICAgIH0KICAgICAgICAgICAgc3dpdGNoIChkYXRhWyJjbWQiXSkgewogICAgICAgICAgICAgICAgY2FzZSAidXBkYXRlTG9nIjoKICAgICAgICAgICAgICAgICAgICBTRVJWRVJbImxvZyJdID0gcmVzcG9uc2VbImxvZyJdOwogICAgICAgICAgICAgICAgICAgIGlmIChkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgiY29udGVudF9sb2ciKSkgewogICAgICAgICAgICAgICAgICAgICAgICBzaG93TG9ncyhmYWxzZSk7CiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgIHJldHVybjsKICAgICAgICAgICAgICAgICAgICBicmVhazsKICAgICAgICAgICAgICAgIGRlZmF1bHQ6CiAgICAgICAgICAgICAgICAgICAgU0VSVkVSID0gbmV3IE9iamVjdCgpOwogICAgICAgICAgICAgICAgICAgIFNFUlZFUiA9IHJlc3BvbnNlOwogICAgICAgICAgICAgICAgICAgIGJyZWFrOwogICAgICAgICAgICB9CiAgICAgICAgICAgIGlmIChyZXNwb25zZS5oYXNPd25Qcm9wZXJ0eSgib3Blbk1lbnUiKSkgewogICAgICAgICAgICAgICAgdmFyIG1lbnUgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZChyZXNwb25zZVsib3Blbk1lbnUiXSk7CiAgICAgICAgICAgICAgICBtZW51LmNsaWNrKCk7CiAgICAgICAgICAgICAgICBzaG93RWxlbWVudCgicG9wdXAiLCBmYWxzZSk7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgaWYgKHJlc3BvbnNlLmhhc093blByb3BlcnR5KCJvcGVuTGluayIpKSB7CiAgICAgICAgICAgICAgICB3aW5kb3cubG9jYXRpb24gPSByZXNwb25zZVsib3BlbkxpbmsiXTsKICAgICAgICAgICAgfQogICAgICAgICAgICBpZiAocmVzcG9uc2UuaGFzT3duUHJvcGVydHkoImFsZXJ0IikpIHsKICAgICAgICAgICAgICAgIGFsZXJ0KHJlc3BvbnNlWyJhbGVydCJdKTsKICAgICAgICAgICAgfQogICAgICAgICAgICBpZiAocmVzcG9uc2UuaGFzT3duUHJvcGVydHkoInJlbG9hZCIpKSB7CiAgICAgICAgICAgICAgICBsb2NhdGlvbi5yZWxvYWQoKTsKICAgICAgICAgICAgfQogICAgICAgICAgICBpZiAocmVzcG9uc2UuaGFzT3duUHJvcGVydHkoIndpemFyZCIpKSB7CiAgICAgICAgICAgICAgICBjcmVhdGVMYXlvdXQoKTsKICAgICAgICAgICAgICAgIGNvbmZpZ3VyYXRpb25XaXphcmRbcmVzcG9uc2VbIndpemFyZCJdXS5jcmVhdGVXaXphcmQoKTsKICAgICAgICAgICAgICAgIHJldHVybjsKICAgICAgICAgICAgfQogICAgICAgICAgICBjcmVhdGVMYXlvdXQoKTsKICAgICAgICB9OwogICAgfTsKICAgIHJldHVybiBTZXJ2ZXI7Cn0oKSk7CmZ1bmN0aW9uIGdldENvb2tpZShuYW1lKSB7CiAgICB2YXIgdmFsdWUgPSAiOyAiICsgZG9jdW1lbnQuY29va2llOwogICAgdmFyIHBhcnRzID0gdmFLine truncated
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,967 @@
|
|||||||
|
package src
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"encoding/xml"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"path"
|
||||||
|
"runtime"
|
||||||
|
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Provider XMLTV Datei überprüfen
|
||||||
|
func checkXMLCompatibility(id string, body []byte) (err error) {
|
||||||
|
|
||||||
|
var xmltv XMLTV
|
||||||
|
var compatibility = make(map[string]int)
|
||||||
|
|
||||||
|
err = xml.Unmarshal(body, &xmltv)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
compatibility["xmltv.channels"] = len(xmltv.Channel)
|
||||||
|
compatibility["xmltv.programs"] = len(xmltv.Program)
|
||||||
|
|
||||||
|
setProviderCompatibility(id, "xmltv", compatibility)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// XEPG Daten erstellen
|
||||||
|
func buildXEPG(background bool) {
|
||||||
|
|
||||||
|
if System.ScanInProgress == 1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
System.ScanInProgress = 1
|
||||||
|
|
||||||
|
if Settings.EpgSource == "XEPG" {
|
||||||
|
|
||||||
|
switch background {
|
||||||
|
|
||||||
|
case true:
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
|
||||||
|
createXEPGMapping()
|
||||||
|
createXEPGDatabase()
|
||||||
|
mapping()
|
||||||
|
cleanupXEPG()
|
||||||
|
createXMLTVFile()
|
||||||
|
createM3UFile()
|
||||||
|
go cachingImages()
|
||||||
|
|
||||||
|
showInfo("XEPG:" + fmt.Sprintf("Ready to use"))
|
||||||
|
|
||||||
|
System.ScanInProgress = 0
|
||||||
|
|
||||||
|
// Cache löschen
|
||||||
|
/*
|
||||||
|
Data.Cache.XMLTV = make(map[string]XMLTV)
|
||||||
|
Data.Cache.XMLTV = nil
|
||||||
|
*/
|
||||||
|
runtime.GC()
|
||||||
|
|
||||||
|
}()
|
||||||
|
|
||||||
|
case false:
|
||||||
|
|
||||||
|
createXEPGMapping()
|
||||||
|
createXEPGDatabase()
|
||||||
|
mapping()
|
||||||
|
cleanupXEPG()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
|
||||||
|
createXMLTVFile()
|
||||||
|
createM3UFile()
|
||||||
|
go cachingImages()
|
||||||
|
showInfo("XEPG:" + fmt.Sprintf("Ready to use"))
|
||||||
|
|
||||||
|
System.ScanInProgress = 0
|
||||||
|
|
||||||
|
// Cache löschen
|
||||||
|
//Data.Cache.XMLTV = make(map[string]XMLTV)
|
||||||
|
//Data.Cache.XMLTV = nil
|
||||||
|
runtime.GC()
|
||||||
|
|
||||||
|
}()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
getLineup()
|
||||||
|
System.ScanInProgress = 0
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// XEPG Daten aktualisieren
|
||||||
|
func updateXEPG(background bool) {
|
||||||
|
|
||||||
|
if System.ScanInProgress == 1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
System.ScanInProgress = 1
|
||||||
|
|
||||||
|
if Settings.EpgSource == "XEPG" {
|
||||||
|
|
||||||
|
switch background {
|
||||||
|
|
||||||
|
case false:
|
||||||
|
|
||||||
|
createXEPGDatabase()
|
||||||
|
mapping()
|
||||||
|
cleanupXEPG()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
|
||||||
|
createXMLTVFile()
|
||||||
|
createM3UFile()
|
||||||
|
showInfo("XEPG:" + fmt.Sprintf("Ready to use"))
|
||||||
|
|
||||||
|
System.ScanInProgress = 0
|
||||||
|
|
||||||
|
}()
|
||||||
|
|
||||||
|
case true:
|
||||||
|
System.ScanInProgress = 0
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
System.ScanInProgress = 0
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cache löschen
|
||||||
|
//Data.Cache.XMLTV = nil //make(map[string]XMLTV)
|
||||||
|
//Data.Cache.XMLTV = make(map[string]XMLTV)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mapping Menü für die XMLTV Dateien erstellen
|
||||||
|
func createXEPGMapping() {
|
||||||
|
|
||||||
|
Data.XMLTV.Files = getLocalProviderFiles("xmltv")
|
||||||
|
Data.XMLTV.Mapping = make(map[string]interface{})
|
||||||
|
|
||||||
|
var tmpMap = make(map[string]interface{})
|
||||||
|
|
||||||
|
var friendlyDisplayName = func(channel Channel) (displayName string) {
|
||||||
|
var dn = channel.DisplayName
|
||||||
|
displayName = dn[0].Value
|
||||||
|
|
||||||
|
switch len(dn) {
|
||||||
|
case 1:
|
||||||
|
displayName = dn[0].Value
|
||||||
|
default:
|
||||||
|
displayName = fmt.Sprintf("%s (%s)", dn[1].Value, dn[0].Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(Data.XMLTV.Files) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := len(Data.XMLTV.Files) - 1; i >= 0; i-- {
|
||||||
|
|
||||||
|
var file = Data.XMLTV.Files[i]
|
||||||
|
|
||||||
|
var err error
|
||||||
|
var fileID = strings.TrimSuffix(getFilenameFromPath(file), path.Ext(getFilenameFromPath(file)))
|
||||||
|
showInfo("XEPG:" + "Parse XMLTV file: " + getProviderParameter(fileID, "xmltv", "name"))
|
||||||
|
|
||||||
|
//xmltv, err = getLocalXMLTV(file)
|
||||||
|
var xmltv XMLTV
|
||||||
|
|
||||||
|
err = getLocalXMLTV(file, &xmltv)
|
||||||
|
if err != nil {
|
||||||
|
Data.XMLTV.Files = append(Data.XMLTV.Files, Data.XMLTV.Files[i+1:]...)
|
||||||
|
var errMsg = err.Error()
|
||||||
|
err = errors.New(getProviderParameter(fileID, "xmltv", "name") + ": " + errMsg)
|
||||||
|
ShowError(err, 000)
|
||||||
|
}
|
||||||
|
|
||||||
|
// XML Parsen (Provider Datei)
|
||||||
|
if err == nil {
|
||||||
|
|
||||||
|
// Daten aus der XML Datei in eine temporäre Map schreiben
|
||||||
|
var xmltvMap = make(map[string]interface{})
|
||||||
|
|
||||||
|
for _, c := range xmltv.Channel {
|
||||||
|
var channel = make(map[string]interface{})
|
||||||
|
|
||||||
|
channel["id"] = c.ID
|
||||||
|
channel["display-name"] = friendlyDisplayName(*c)
|
||||||
|
channel["icon"] = c.Icon.Src
|
||||||
|
|
||||||
|
xmltvMap[c.ID] = channel
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpMap[getFilenameFromPath(file)] = xmltvMap
|
||||||
|
Data.XMLTV.Mapping[getFilenameFromPath(file)] = xmltvMap
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auswahl für den Dummy erstellen
|
||||||
|
var dummy = make(map[string]interface{})
|
||||||
|
var times = []string{"30", "60", "90", "120"}
|
||||||
|
|
||||||
|
for _, i := range times {
|
||||||
|
|
||||||
|
var dummyChannel = make(map[string]string)
|
||||||
|
dummyChannel["display-name"] = i + " Minutes"
|
||||||
|
dummyChannel["id"] = i + "_Minutes"
|
||||||
|
dummyChannel["icon"] = ""
|
||||||
|
|
||||||
|
dummy[dummyChannel["id"]] = dummyChannel
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Data.XMLTV.Mapping = tmpMap
|
||||||
|
Data.XMLTV.Mapping["xTeVe Dummy"] = dummy
|
||||||
|
|
||||||
|
tmpMap = make(map[string]interface{})
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// XEPG Datenbank erstellen / aktualisieren
|
||||||
|
func createXEPGDatabase() (err error) {
|
||||||
|
|
||||||
|
var allChannelNumbers []float64
|
||||||
|
Data.Cache.Streams.Active = []string{}
|
||||||
|
|
||||||
|
Data.XEPG.Channels, err = loadJSONFileToMap(System.File.XEPG)
|
||||||
|
if err != nil {
|
||||||
|
ShowError(err, 1004)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var createNewID = func() (xepg string) {
|
||||||
|
|
||||||
|
var firstID = 0 //len(Data.XEPG.Channels)
|
||||||
|
|
||||||
|
newXEPGID:
|
||||||
|
|
||||||
|
if _, ok := Data.XEPG.Channels["x-ID."+strconv.FormatInt(int64(firstID), 10)]; ok {
|
||||||
|
firstID++
|
||||||
|
goto newXEPGID
|
||||||
|
}
|
||||||
|
|
||||||
|
xepg = "x-ID." + strconv.FormatInt(int64(firstID), 10)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var getFreeChannelNumber = func() (xChannelID string) {
|
||||||
|
|
||||||
|
var firstFreeNumber float64 = Settings.MappingFirstChannel
|
||||||
|
|
||||||
|
newNumber:
|
||||||
|
|
||||||
|
if indexOfFloat64(firstFreeNumber, allChannelNumbers) == -1 {
|
||||||
|
xChannelID = fmt.Sprintf("%g", firstFreeNumber)
|
||||||
|
allChannelNumbers = append(allChannelNumbers, firstFreeNumber)
|
||||||
|
} else {
|
||||||
|
firstFreeNumber++
|
||||||
|
goto newNumber
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
showInfo("XEPG:" + "Update database")
|
||||||
|
|
||||||
|
// Kanal mit fehlenden Kanalnummern löschen
|
||||||
|
for id, dxc := range Data.XEPG.Channels {
|
||||||
|
|
||||||
|
var xepgChannel XEPGChannelStruct
|
||||||
|
err = json.Unmarshal([]byte(mapToJSON(dxc)), &xepgChannel)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(xepgChannel.XChannelID) == 0 {
|
||||||
|
fmt.Println(mapToJSON(xepgChannel))
|
||||||
|
delete(Data.XEPG.Channels, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
if xChannelID, err := strconv.ParseFloat(xepgChannel.XChannelID, 64); err == nil {
|
||||||
|
allChannelNumbers = append(allChannelNumbers, xChannelID)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, dsa := range Data.Streams.Active {
|
||||||
|
|
||||||
|
var channelExists = false // Entscheidet ob ein Kanal neu zu Datenbank hinzugefügt werden soll.
|
||||||
|
var channelHasUUID = false // Überprüft, ob der Kanal (Stream) eindeutige ID's besitzt
|
||||||
|
var currentXEPGID string // Aktuelle Datenbank ID (XEPG). Wird verwendet, um den Kanal in der Datenbank mit dem Stream der M3u zu aktualisieren
|
||||||
|
var m3uChannel M3UChannelStructXEPG
|
||||||
|
|
||||||
|
err = json.Unmarshal([]byte(mapToJSON(dsa)), &m3uChannel)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Data.Cache.Streams.Active = append(Data.Cache.Streams.Active, m3uChannel.Name)
|
||||||
|
|
||||||
|
// XEPG Datenbank durchlaufen um nach dem Kanal zu suchen.
|
||||||
|
for xepg, dxc := range Data.XEPG.Channels {
|
||||||
|
|
||||||
|
var xepgChannel XEPGChannelStruct
|
||||||
|
err = json.Unmarshal([]byte(mapToJSON(dxc)), &xepgChannel)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vergleichen des Streams anhand einer UUID in der M3U mit dem Kanal in der Databank
|
||||||
|
if len(xepgChannel.UUIDValue) > 0 && len(m3uChannel.UUIDValue) > 0 {
|
||||||
|
|
||||||
|
if xepgChannel.UUIDValue == m3uChannel.UUIDValue && xepgChannel.UUIDKey == m3uChannel.UUIDKey {
|
||||||
|
|
||||||
|
channelExists = true
|
||||||
|
channelHasUUID = true
|
||||||
|
currentXEPGID = xepg
|
||||||
|
break
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Vergleichen des Streams mit dem Kanal in der Databank anhand des Kanalnamens
|
||||||
|
//fmt.Println(xepgChannel.Name, xepgChannel.UUIDKey, xepgChannel.UUIDValue)
|
||||||
|
if xepgChannel.Name == m3uChannel.Name {
|
||||||
|
channelExists = true
|
||||||
|
currentXEPGID = xepg
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//os.Exit(0)
|
||||||
|
|
||||||
|
switch channelExists {
|
||||||
|
case true:
|
||||||
|
// Bereits vorhandener Kanal
|
||||||
|
var xepgChannel XEPGChannelStruct
|
||||||
|
err = json.Unmarshal([]byte(mapToJSON(Data.XEPG.Channels[currentXEPGID])), &xepgChannel)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Streaming URL aktualisieren
|
||||||
|
xepgChannel.URL = m3uChannel.URL
|
||||||
|
|
||||||
|
// Name aktualisieren, anhand des Names wird überprüft ob der Kanal noch in einer Playlist verhanden. Funktion: cleanupXEPG
|
||||||
|
xepgChannel.Name = m3uChannel.Name
|
||||||
|
|
||||||
|
// Kanalname aktualisieren, nur mit Kanal ID's möglich
|
||||||
|
if channelHasUUID == true {
|
||||||
|
if xepgChannel.XUpdateChannelName == true {
|
||||||
|
xepgChannel.XName = m3uChannel.Name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kanallogo aktualisieren. Wird bei vorhandenem Logo in der XMLTV Datei wieder überschrieben
|
||||||
|
if xepgChannel.XUpdateChannelIcon == true {
|
||||||
|
xepgChannel.TvgLogo = m3uChannel.TvgLogo
|
||||||
|
}
|
||||||
|
|
||||||
|
Data.XEPG.Channels[currentXEPGID] = xepgChannel
|
||||||
|
|
||||||
|
case false:
|
||||||
|
// Neuer Kanal
|
||||||
|
var xepg = createNewID()
|
||||||
|
var xChannelID = getFreeChannelNumber()
|
||||||
|
|
||||||
|
var newChannel XEPGChannelStruct
|
||||||
|
newChannel.FileM3UID = m3uChannel.FileM3UID
|
||||||
|
newChannel.FileM3UName = m3uChannel.FileM3UName
|
||||||
|
newChannel.FileM3UPath = m3uChannel.FileM3UPath
|
||||||
|
newChannel.Values = m3uChannel.Values
|
||||||
|
newChannel.GroupTitle = m3uChannel.GroupTitle
|
||||||
|
newChannel.Name = m3uChannel.Name
|
||||||
|
newChannel.TvgID = m3uChannel.TvgID
|
||||||
|
newChannel.TvgLogo = m3uChannel.TvgLogo
|
||||||
|
newChannel.TvgName = m3uChannel.TvgName
|
||||||
|
newChannel.URL = m3uChannel.URL
|
||||||
|
newChannel.XmltvFile = ""
|
||||||
|
newChannel.XMapping = ""
|
||||||
|
|
||||||
|
if len(m3uChannel.UUIDKey) > 0 {
|
||||||
|
newChannel.UUIDKey = m3uChannel.UUIDKey
|
||||||
|
newChannel.UUIDValue = m3uChannel.UUIDValue
|
||||||
|
}
|
||||||
|
|
||||||
|
newChannel.XName = m3uChannel.Name
|
||||||
|
newChannel.XGroupTitle = m3uChannel.GroupTitle
|
||||||
|
newChannel.XEPG = xepg
|
||||||
|
newChannel.XChannelID = xChannelID
|
||||||
|
|
||||||
|
Data.XEPG.Channels[xepg] = newChannel
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
err = saveMapToJSONFile(System.File.XEPG, Data.XEPG.Channels)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kanäle automatisch zuordnen und das Mapping überprüfen
|
||||||
|
func mapping() (err error) {
|
||||||
|
showInfo("XEPG:" + "Map channels")
|
||||||
|
|
||||||
|
for xepg, dxc := range Data.XEPG.Channels {
|
||||||
|
|
||||||
|
var xepgChannel XEPGChannelStruct
|
||||||
|
err = json.Unmarshal([]byte(mapToJSON(dxc)), &xepgChannel)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Automatische Mapping für neue Kanäle. Wird nur ausgeführt, wenn der Kanal deaktiviert ist und keine XMLTV Datei und kein XMLTV Kanal zugeordnet ist.
|
||||||
|
if xepgChannel.XActive == false {
|
||||||
|
|
||||||
|
// Werte kann "-" sein, deswegen len < 1
|
||||||
|
if len(xepgChannel.XmltvFile) < 1 && len(xepgChannel.XmltvFile) < 1 {
|
||||||
|
|
||||||
|
var tvgID = xepgChannel.TvgID
|
||||||
|
|
||||||
|
// Default für neuen Kanal setzen
|
||||||
|
xepgChannel.XmltvFile = "-"
|
||||||
|
xepgChannel.XMapping = "-"
|
||||||
|
|
||||||
|
Data.XEPG.Channels[xepg] = xepgChannel
|
||||||
|
|
||||||
|
for file, xmltvChannels := range Data.XMLTV.Mapping {
|
||||||
|
|
||||||
|
if channel, ok := xmltvChannels.(map[string]interface{})[tvgID]; ok {
|
||||||
|
|
||||||
|
if channelID, ok := channel.(map[string]interface{})["id"].(string); ok {
|
||||||
|
|
||||||
|
xepgChannel.XmltvFile = file
|
||||||
|
xepgChannel.XMapping = channelID
|
||||||
|
xepgChannel.XActive = true
|
||||||
|
|
||||||
|
// Falls in der XMLTV Datei ein Logo existiert, wird dieses verwendet. Falls nicht, dann das Logo aus der M3U Datei
|
||||||
|
if icon, ok := channel.(map[string]interface{})["icon"].(string); ok {
|
||||||
|
if len(icon) > 0 {
|
||||||
|
xepgChannel.TvgLogo = icon
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Data.XEPG.Channels[xepg] = xepgChannel
|
||||||
|
break
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Überprüfen, ob die zugeordneten XMLTV Dateien und Kanäle noch existieren.
|
||||||
|
if xepgChannel.XActive == true {
|
||||||
|
|
||||||
|
var mapping = xepgChannel.XMapping
|
||||||
|
var file = xepgChannel.XmltvFile
|
||||||
|
|
||||||
|
if file != "xTeVe Dummy" {
|
||||||
|
|
||||||
|
if value, ok := Data.XMLTV.Mapping[file].(map[string]interface{}); ok {
|
||||||
|
|
||||||
|
if channel, ok := value[mapping].(map[string]interface{}); ok {
|
||||||
|
|
||||||
|
// Kanallogo aktualisieren
|
||||||
|
if logo, ok := channel["icon"].(string); ok {
|
||||||
|
|
||||||
|
if xepgChannel.XUpdateChannelIcon == true && len(logo) > 0 {
|
||||||
|
xepgChannel.TvgLogo = logo
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
ShowError(fmt.Errorf(fmt.Sprintf("Missing EPG data: %s", xepgChannel.Name)), 0)
|
||||||
|
showWarning(2302)
|
||||||
|
xepgChannel.XActive = false
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
var fileID = strings.TrimSuffix(getFilenameFromPath(file), path.Ext(getFilenameFromPath(file)))
|
||||||
|
|
||||||
|
ShowError(fmt.Errorf("Missing XMLTV file: %s", getProviderParameter(fileID, "xmltv", "name")), 0)
|
||||||
|
showWarning(2301)
|
||||||
|
xepgChannel.XActive = false
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(xepgChannel.XmltvFile) == 0 {
|
||||||
|
xepgChannel.XmltvFile = "-"
|
||||||
|
xepgChannel.XActive = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(xepgChannel.XMapping) == 0 {
|
||||||
|
xepgChannel.XMapping = "-"
|
||||||
|
xepgChannel.XActive = false
|
||||||
|
}
|
||||||
|
|
||||||
|
Data.XEPG.Channels[xepg] = xepgChannel
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
err = saveMapToJSONFile(System.File.XEPG, Data.XEPG.Channels)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// XMLTV Datei erstellen
|
||||||
|
func createXMLTVFile() (err error) {
|
||||||
|
|
||||||
|
Data.Cache.ImagesFiles = []string{}
|
||||||
|
Data.Cache.ImagesURLS = []string{}
|
||||||
|
Data.Cache.ImagesCache = []string{}
|
||||||
|
|
||||||
|
files, err := ioutil.ReadDir(System.Folder.ImagesCache)
|
||||||
|
if err == nil {
|
||||||
|
|
||||||
|
for _, file := range files {
|
||||||
|
|
||||||
|
if indexOfString(file.Name(), Data.Cache.ImagesCache) == -1 {
|
||||||
|
Data.Cache.ImagesCache = append(Data.Cache.ImagesCache, file.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(Data.XMLTV.Files) == 0 && len(Data.Streams.Active) == 0 {
|
||||||
|
Data.XEPG.Channels = make(map[string]interface{})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
showInfo("XEPG:" + fmt.Sprintf("Create XMLTV file (%s)", System.File.XML))
|
||||||
|
|
||||||
|
var xepgXML XMLTV
|
||||||
|
|
||||||
|
xepgXML.Generator = System.Name
|
||||||
|
xepgXML.Source = fmt.Sprintf("%s - %s", System.Name, System.Version)
|
||||||
|
|
||||||
|
var tmpProgram = &XMLTV{}
|
||||||
|
|
||||||
|
for _, dxc := range Data.XEPG.Channels {
|
||||||
|
|
||||||
|
var xepgChannel XEPGChannelStruct
|
||||||
|
err := json.Unmarshal([]byte(mapToJSON(dxc)), &xepgChannel)
|
||||||
|
if err == nil {
|
||||||
|
|
||||||
|
if xepgChannel.XActive == true {
|
||||||
|
|
||||||
|
// Kanäle
|
||||||
|
var channel Channel
|
||||||
|
channel.ID = xepgChannel.XChannelID
|
||||||
|
channel.Icon = Icon{Src: getCacheImageURL(xepgChannel.TvgLogo)}
|
||||||
|
channel.DisplayName = append(channel.DisplayName, DisplayName{Value: xepgChannel.XName})
|
||||||
|
|
||||||
|
xepgXML.Channel = append(xepgXML.Channel, &channel)
|
||||||
|
|
||||||
|
// Programme
|
||||||
|
|
||||||
|
*tmpProgram, err = getProgramData(xepgChannel)
|
||||||
|
if err == nil {
|
||||||
|
|
||||||
|
for _, program := range tmpProgram.Program {
|
||||||
|
xepgXML.Program = append(xepgXML.Program, program)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
var content, _ = xml.MarshalIndent(xepgXML, " ", " ")
|
||||||
|
var xmlOutput = []byte(xml.Header + string(content))
|
||||||
|
writeByteToFile(System.File.XML, xmlOutput)
|
||||||
|
|
||||||
|
xepgXML = XMLTV{}
|
||||||
|
|
||||||
|
//saveMapToJSONFile(System.File.Images, Data.Cache.ImageCache)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Programmdaten erstellen (createXMLTVFile)
|
||||||
|
func getProgramData(xepgChannel XEPGChannelStruct) (xepgXML XMLTV, err error) {
|
||||||
|
|
||||||
|
var xmltvFile = System.Folder.Data + xepgChannel.XmltvFile
|
||||||
|
var channelID = xepgChannel.XMapping
|
||||||
|
|
||||||
|
var xmltv XMLTV
|
||||||
|
|
||||||
|
if xmltvFile == System.Folder.Data+"xTeVe Dummy" {
|
||||||
|
xmltv = createDummyProgram(xepgChannel)
|
||||||
|
} else {
|
||||||
|
|
||||||
|
err = getLocalXMLTV(xmltvFile, &xmltv)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, xmltvProgram := range xmltv.Program {
|
||||||
|
|
||||||
|
if xmltvProgram.Channel == channelID {
|
||||||
|
//fmt.Println(&channelID)
|
||||||
|
var program = &Program{}
|
||||||
|
|
||||||
|
// Channel ID
|
||||||
|
program.Channel = xepgChannel.XChannelID
|
||||||
|
program.Start = xmltvProgram.Start
|
||||||
|
program.Stop = xmltvProgram.Stop
|
||||||
|
|
||||||
|
// Title
|
||||||
|
program.Title = xmltvProgram.Title
|
||||||
|
|
||||||
|
// Sub title (Untertitel)
|
||||||
|
program.SubTitle = xmltvProgram.SubTitle
|
||||||
|
|
||||||
|
// Description (Beschreibung)
|
||||||
|
program.Desc = xmltvProgram.Desc
|
||||||
|
|
||||||
|
// Category (Kategorie)
|
||||||
|
getCategory(program, xmltvProgram, xepgChannel)
|
||||||
|
|
||||||
|
// Country (Länder)
|
||||||
|
program.Country = xmltvProgram.Country
|
||||||
|
|
||||||
|
// Program icon (Poster / Cover)
|
||||||
|
getPoster(program, xmltvProgram, xepgChannel)
|
||||||
|
|
||||||
|
// Language (Sprache)
|
||||||
|
program.Language = xmltvProgram.Language
|
||||||
|
|
||||||
|
// Episodes numbers (Episodennummern)
|
||||||
|
getEpisodeNum(program, xmltvProgram, xepgChannel)
|
||||||
|
|
||||||
|
// Video (Videoparameter)
|
||||||
|
getVideo(program, xmltvProgram, xepgChannel)
|
||||||
|
|
||||||
|
// Date (Datum)
|
||||||
|
program.Date = xmltvProgram.Date
|
||||||
|
|
||||||
|
// Previously shown (Wiederholung)
|
||||||
|
program.PreviouslyShown = xmltvProgram.PreviouslyShown
|
||||||
|
|
||||||
|
// New (Neu)
|
||||||
|
program.New = xmltvProgram.New
|
||||||
|
|
||||||
|
// Live
|
||||||
|
program.Live = xmltvProgram.Live
|
||||||
|
|
||||||
|
xepgXML.Program = append(xepgXML.Program, program)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dummy Daten erstellen (createXMLTVFile)
|
||||||
|
func createDummyProgram(xepgChannel XEPGChannelStruct) (dummyXMLTV XMLTV) {
|
||||||
|
|
||||||
|
var currentTime = time.Now()
|
||||||
|
var dateArray = strings.Fields(currentTime.String())
|
||||||
|
var offset = " " + dateArray[2]
|
||||||
|
var currentDay = currentTime.Format("20060102")
|
||||||
|
var startTime, _ = time.Parse("20060102150405", currentDay+"000000")
|
||||||
|
|
||||||
|
showInfo("Create Dummy Guide:" + "Time offset" + offset + " - " + xepgChannel.XName)
|
||||||
|
|
||||||
|
var dl = strings.Split(xepgChannel.XMapping, "_")
|
||||||
|
dummyLength, err := strconv.Atoi(dl[0])
|
||||||
|
if err != nil {
|
||||||
|
ShowError(err, 000)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for d := 0; d < 4; d++ {
|
||||||
|
|
||||||
|
var epgStartTime = startTime.Add(time.Hour * time.Duration(d*24))
|
||||||
|
|
||||||
|
for t := dummyLength; t <= 1440; t = t + dummyLength {
|
||||||
|
|
||||||
|
var epgStopTime = epgStartTime.Add(time.Minute * time.Duration(dummyLength))
|
||||||
|
|
||||||
|
var epg Program
|
||||||
|
poster := Poster{}
|
||||||
|
|
||||||
|
epg.Channel = xepgChannel.XMapping
|
||||||
|
epg.Start = epgStartTime.Format("20060102150405") + offset
|
||||||
|
epg.Stop = epgStopTime.Format("20060102150405") + offset
|
||||||
|
epg.Title = append(epg.Title, &Title{Value: xepgChannel.XName + " (" + epgStartTime.Weekday().String()[0:2] + ". " + epgStartTime.Format("15:04") + " - " + epgStopTime.Format("15:04") + ")", Lang: "en"})
|
||||||
|
epg.Desc = append(epg.Desc, &Desc{Value: "xTeVe: (" + strconv.Itoa(dummyLength) + " Minutes) " + epgStartTime.Weekday().String() + " " + epgStartTime.Format("15:04") + " - " + epgStopTime.Format("15:04"), Lang: "en"})
|
||||||
|
|
||||||
|
if Settings.XepgReplaceMissingImages == true {
|
||||||
|
poster.Src = getCacheImageURL(xepgChannel.TvgLogo)
|
||||||
|
epg.Poster = append(epg.Poster, poster)
|
||||||
|
}
|
||||||
|
|
||||||
|
epg.EpisodeNum = append(epg.EpisodeNum, &EpisodeNum{Value: epgStartTime.Format("2006-01-02 15:04:05"), System: "original-air-date"})
|
||||||
|
|
||||||
|
epg.New = &New{Value: ""}
|
||||||
|
|
||||||
|
dummyXMLTV.Program = append(dummyXMLTV.Program, &epg)
|
||||||
|
epgStartTime = epgStopTime
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kategorien erweitern (createXMLTVFile)
|
||||||
|
func getCategory(program *Program, xmltvProgram *Program, xepgChannel XEPGChannelStruct) {
|
||||||
|
|
||||||
|
for _, i := range xmltvProgram.Category {
|
||||||
|
|
||||||
|
category := &Category{}
|
||||||
|
category.Value = i.Value
|
||||||
|
category.Lang = i.Lang
|
||||||
|
program.Category = append(program.Category, category)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(xepgChannel.XCategory) > 0 {
|
||||||
|
|
||||||
|
category := &Category{}
|
||||||
|
category.Value = xepgChannel.XCategory
|
||||||
|
category.Lang = "en"
|
||||||
|
program.Category = append(program.Category, category)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Programm Poster Cover aus der XMLTV Datei laden
|
||||||
|
func getPoster(program *Program, xmltvProgram *Program, xepgChannel XEPGChannelStruct) {
|
||||||
|
|
||||||
|
for _, poster := range xmltvProgram.Poster {
|
||||||
|
poster.Src = getCacheImageURL(poster.Src)
|
||||||
|
program.Poster = append(program.Poster, poster)
|
||||||
|
}
|
||||||
|
|
||||||
|
if Settings.XepgReplaceMissingImages == true {
|
||||||
|
|
||||||
|
if len(xmltvProgram.Poster) == 0 {
|
||||||
|
var poster Poster
|
||||||
|
poster.Src = getCacheImageURL(xepgChannel.TvgLogo)
|
||||||
|
program.Poster = append(program.Poster, poster)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Episodensystem übernehmen, falls keins vorhanden ist und eine Kategorie im Mapping eingestellt wurden, wird eine Episode erstellt
|
||||||
|
func getEpisodeNum(program *Program, xmltvProgram *Program, xepgChannel XEPGChannelStruct) {
|
||||||
|
|
||||||
|
program.EpisodeNum = xmltvProgram.EpisodeNum
|
||||||
|
|
||||||
|
if len(xepgChannel.XCategory) > 0 {
|
||||||
|
|
||||||
|
if len(xmltvProgram.EpisodeNum) == 0 {
|
||||||
|
program.EpisodeNum = append(program.EpisodeNum, &EpisodeNum{Value: time.Now().Format("2006-01-02"), System: "original-air-date"})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Videoparameter erstellen (createXMLTVFile)
|
||||||
|
func getVideo(program *Program, xmltvProgram *Program, xepgChannel XEPGChannelStruct) {
|
||||||
|
|
||||||
|
var video Video
|
||||||
|
video.Present = xmltvProgram.Video.Present
|
||||||
|
video.Colour = xmltvProgram.Video.Colour
|
||||||
|
video.Aspect = xmltvProgram.Video.Aspect
|
||||||
|
video.Quality = xmltvProgram.Video.Quality
|
||||||
|
|
||||||
|
if len(xmltvProgram.Video.Quality) == 0 {
|
||||||
|
|
||||||
|
if strings.Contains(strings.ToUpper(xepgChannel.XName), " HD") || strings.Contains(strings.ToUpper(xepgChannel.XName), " FHD") {
|
||||||
|
video.Quality = "HDTV"
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.Contains(strings.ToUpper(xepgChannel.XName), " UHD") || strings.Contains(strings.ToUpper(xepgChannel.XName), " 4K") {
|
||||||
|
video.Quality = "UHDTV"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
program.Video = video
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lokale Provider XMLTV Datei laden
|
||||||
|
func getLocalXMLTV(file string, xmltv *XMLTV) (err error) {
|
||||||
|
|
||||||
|
if _, ok := Data.Cache.XMLTV[file]; !ok {
|
||||||
|
|
||||||
|
// Cache initialisieren
|
||||||
|
if len(Data.Cache.XMLTV) == 0 {
|
||||||
|
Data.Cache.XMLTV = make(map[string]XMLTV)
|
||||||
|
}
|
||||||
|
|
||||||
|
// XML Daten lesen
|
||||||
|
content, err := readByteFromFile(file)
|
||||||
|
|
||||||
|
// Lokale XML Datei existiert nicht im Ordner: data
|
||||||
|
if err != nil {
|
||||||
|
ShowError(err, 1004)
|
||||||
|
err = errors.New("Local copy of the file no longer exists")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// XML Datei parsen
|
||||||
|
err = xml.Unmarshal(content, &xmltv)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
Data.Cache.XMLTV[file] = *xmltv
|
||||||
|
|
||||||
|
} else {
|
||||||
|
*xmltv = Data.Cache.XMLTV[file]
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// M3U Datei erstellen
|
||||||
|
func createM3UFile() {
|
||||||
|
|
||||||
|
showInfo("XEPG:" + fmt.Sprintf("Create M3U file (%s)", System.File.M3U))
|
||||||
|
_, err := buildM3U([]string{})
|
||||||
|
if err != nil {
|
||||||
|
ShowError(err, 000)
|
||||||
|
}
|
||||||
|
|
||||||
|
saveMapToJSONFile(System.File.URLS, Data.Cache.StreamingURLS)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// XEPG Datenbank bereinigen
|
||||||
|
func cleanupXEPG() {
|
||||||
|
|
||||||
|
showInfo("XEPG:" + fmt.Sprintf("Cleanup database"))
|
||||||
|
Data.XEPG.XEPGCount = 0
|
||||||
|
|
||||||
|
for id, dxc := range Data.XEPG.Channels {
|
||||||
|
|
||||||
|
var xepgChannel XEPGChannelStruct
|
||||||
|
err := json.Unmarshal([]byte(mapToJSON(dxc)), &xepgChannel)
|
||||||
|
if err == nil {
|
||||||
|
|
||||||
|
if indexOfString(xepgChannel.Name, Data.Cache.Streams.Active) == -1 {
|
||||||
|
delete(Data.XEPG.Channels, id)
|
||||||
|
} else {
|
||||||
|
if xepgChannel.XActive == true {
|
||||||
|
Data.XEPG.XEPGCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
err := saveMapToJSONFile(System.File.XEPG, Data.XEPG.Channels)
|
||||||
|
if err != nil {
|
||||||
|
ShowError(err, 000)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
showInfo("XEPG Channels:" + fmt.Sprintf("%d", Data.XEPG.XEPGCount))
|
||||||
|
|
||||||
|
if len(Data.Streams.Active) > 0 && Data.XEPG.XEPGCount == 0 {
|
||||||
|
showWarning(2005)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Streaming URL für die Channels App generieren
|
||||||
|
func getStreamByChannelID(channelID string) (playlistID, streamURL string, err error) {
|
||||||
|
|
||||||
|
err = errors.New("Channel not found")
|
||||||
|
|
||||||
|
for _, dxc := range Data.XEPG.Channels {
|
||||||
|
|
||||||
|
var xepgChannel XEPGChannelStruct
|
||||||
|
err := json.Unmarshal([]byte(mapToJSON(dxc)), &xepgChannel)
|
||||||
|
|
||||||
|
fmt.Println(xepgChannel.XChannelID)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
|
||||||
|
if channelID == xepgChannel.XChannelID {
|
||||||
|
|
||||||
|
playlistID = xepgChannel.FileM3UID
|
||||||
|
streamURL = xepgChannel.URL
|
||||||
|
|
||||||
|
return playlistID, streamURL, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
function login() {
|
||||||
|
var err:Boolean = false
|
||||||
|
var data = new Object()
|
||||||
|
var div:any = document.getElementById("content")
|
||||||
|
var form:any = document.getElementById("authentication")
|
||||||
|
|
||||||
|
var inputs:any = div.getElementsByTagName("INPUT")
|
||||||
|
|
||||||
|
console.log(inputs)
|
||||||
|
|
||||||
|
for (var i = inputs.length - 1; i >= 0; i--) {
|
||||||
|
|
||||||
|
var key:string = (inputs[i] as HTMLInputElement).name
|
||||||
|
var value:string = (inputs[i] as HTMLInputElement).value
|
||||||
|
|
||||||
|
if (value.length == 0) {
|
||||||
|
inputs[i].style.borderColor = "red"
|
||||||
|
err = true
|
||||||
|
}
|
||||||
|
|
||||||
|
data[key] = value
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err == true) {
|
||||||
|
data = new Object()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.hasOwnProperty("confirm")) {
|
||||||
|
|
||||||
|
if (data["confirm"] != data["password"]) {
|
||||||
|
alert("sdafsd")
|
||||||
|
document.getElementById('password').style.borderColor = "red"
|
||||||
|
document.getElementById('confirm').style.borderColor = "red"
|
||||||
|
|
||||||
|
document.getElementById("err").innerHTML = "{{.account.failed}}"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(data)
|
||||||
|
|
||||||
|
form.submit();
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,663 @@
|
|||||||
|
var SERVER = new Object()
|
||||||
|
var BULK_EDIT:Boolean = false
|
||||||
|
var COLUMN_TO_SORT:number
|
||||||
|
var SEARCH_MAPPING = new Object()
|
||||||
|
var UNDO = new Object()
|
||||||
|
var SERVER_CONNECTION = false
|
||||||
|
var WS_AVAILABLE = false
|
||||||
|
|
||||||
|
|
||||||
|
// Menü
|
||||||
|
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}}", "xteveAutoUpdate,tuner,epgSource,api"))
|
||||||
|
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.files}}", "update,files.update,temp.path,cache.images,xepg.replace.missing.images"))
|
||||||
|
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.streaming}}", "buffer,buffer.size.kb,buffer.timeout,user.agent"))
|
||||||
|
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:string
|
||||||
|
switch(type) {
|
||||||
|
case true: cssClass = "block"; break;
|
||||||
|
case false: cssClass = "none"; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById(elmID).className = cssClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeButtonAction(element, buttonID, attribute) {
|
||||||
|
var value = element.options[element.selectedIndex].value;
|
||||||
|
document.getElementById(buttonID).setAttribute(attribute, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLocalData(dataType, id):object {
|
||||||
|
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():string[] {
|
||||||
|
|
||||||
|
var channels:string[] = 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] as HTMLElement).style.display != "none") {
|
||||||
|
|
||||||
|
if ((trs[i].firstChild.firstChild as HTMLInputElement).checked == true) {
|
||||||
|
channels.push(trs[i].id)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return channels
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectAllChannels() {
|
||||||
|
|
||||||
|
var bulk:Boolean = false
|
||||||
|
var trs = document.getElementById("content_table").getElementsByTagName("TR")
|
||||||
|
|
||||||
|
if ((trs[0].firstChild.firstChild as HTMLInputElement).checked == true) {
|
||||||
|
bulk = true
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 1; i < trs.length; i++) {
|
||||||
|
|
||||||
|
if ((trs[i] as HTMLElement).style.display != "none") {
|
||||||
|
|
||||||
|
switch (bulk) {
|
||||||
|
|
||||||
|
case true:
|
||||||
|
(trs[i].firstChild.firstChild as HTMLInputElement).checked = true
|
||||||
|
break
|
||||||
|
|
||||||
|
case false:
|
||||||
|
(trs[i].firstChild.firstChild as HTMLInputElement).checked = false
|
||||||
|
break
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
function bulkEdit() {
|
||||||
|
|
||||||
|
BULK_EDIT = !BULK_EDIT
|
||||||
|
var className:string
|
||||||
|
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] as HTMLInputElement).checked = false
|
||||||
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
|
||||||
|
var sortObj = new Object();
|
||||||
|
var x, xValue;
|
||||||
|
var tableHeader
|
||||||
|
var sortByString = false
|
||||||
|
|
||||||
|
if (column > 0 && COLUMN_TO_SORT > 0) {
|
||||||
|
tableItems[COLUMN_TO_SORT].className = "pointer";
|
||||||
|
tableItems[column].className = "sortThis";
|
||||||
|
}
|
||||||
|
|
||||||
|
COLUMN_TO_SORT = column;
|
||||||
|
|
||||||
|
|
||||||
|
var rows = (table as HTMLTableElement).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:string[] = ["x-active", "x-channelID", "x-name", "_file.m3u.name", "x-group-title"]
|
||||||
|
|
||||||
|
channels.forEach(id => {
|
||||||
|
|
||||||
|
channelKeys.forEach(key => {
|
||||||
|
|
||||||
|
if (key == "x-active") {
|
||||||
|
|
||||||
|
switch (data[id][key]) {
|
||||||
|
case true:
|
||||||
|
SEARCH_MAPPING[id] = "online "
|
||||||
|
break;
|
||||||
|
|
||||||
|
case false:
|
||||||
|
SEARCH_MAPPING[id] = "offline "
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
SEARCH_MAPPING[id] = SEARCH_MAPPING[id] + data[id][key] + " "
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
function searchInMapping() {
|
||||||
|
|
||||||
|
var searchValue = (document.getElementById("searchMapping") as HTMLInputElement).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]
|
||||||
|
|
||||||
|
switch (element.toLowerCase().includes(searchValue.toLowerCase())) {
|
||||||
|
case true:
|
||||||
|
document.getElementById(id).style.display = ""
|
||||||
|
break;
|
||||||
|
|
||||||
|
case false:
|
||||||
|
document.getElementById(id).style.display = "none"
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
function calculateWrapperHeight() {
|
||||||
|
|
||||||
|
if (document.getElementById("box-wrapper")){
|
||||||
|
|
||||||
|
var elm = document.getElementById("box-wrapper");
|
||||||
|
|
||||||
|
var divs = new Array("myStreamsBox", "clientInfo", "content");
|
||||||
|
var elementsHeight = 0 - elm.offsetHeight;
|
||||||
|
for (var i = 0; i < divs.length; i++) {
|
||||||
|
elementsHeight = elementsHeight + document.getElementById(divs[i]).offsetHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
elm.style.height = window.innerHeight - elementsHeight + "px";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeChannelNumber(element) {
|
||||||
|
|
||||||
|
var dbID = element.parentNode.parentNode.id
|
||||||
|
|
||||||
|
var newNumber:number = parseFloat(element.value)
|
||||||
|
var channelNumbers:number[] = []
|
||||||
|
var data = SERVER["xepg"]["epgMapping"]
|
||||||
|
var channels = getObjKeys(data)
|
||||||
|
|
||||||
|
if (isNaN(newNumber)) {
|
||||||
|
alert("{{.alert.invalidChannelNumber}}")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
channels.forEach(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:Server = new Server(cmd)
|
||||||
|
server.request(data)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleChannelStatus(id:string) {
|
||||||
|
|
||||||
|
var element:any
|
||||||
|
var status:boolean
|
||||||
|
|
||||||
|
if(document.getElementById("active")) {
|
||||||
|
var checkbox = (document.getElementById("active") as HTMLInputElement)
|
||||||
|
status = (checkbox).checked
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var ids:string[] = getAllSelectedChannels()
|
||||||
|
if (ids.length == 0) {
|
||||||
|
ids.push(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
ids.forEach(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 as HTMLInputElement).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]') as HTMLInputElement).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: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 as HTMLInputElement).files[0].name
|
||||||
|
|
||||||
|
var reader = new FileReader();
|
||||||
|
var file = (document.querySelector('input[type=file]') as HTMLInputElement).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:Server = new Server(cmd)
|
||||||
|
server.request(data)
|
||||||
|
|
||||||
|
var updateLogo = (document.getElementById('update-icon') as HTMLInputElement)
|
||||||
|
updateLogo.checked = false
|
||||||
|
updateLogo.className = "changed"
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} else {
|
||||||
|
alert("File could not be loaded")
|
||||||
|
}
|
||||||
|
|
||||||
|
upload.remove()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkUndo(key:string) {
|
||||||
|
|
||||||
|
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:Server = new Server("updateLog")
|
||||||
|
server.request(new Object())
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
tsc *.ts --outDir ../html/js/
|
||||||
@@ -0,0 +1,169 @@
|
|||||||
|
class WizardCategory {
|
||||||
|
DocumentID = "content"
|
||||||
|
|
||||||
|
createCategoryHeadline(value:string):any {
|
||||||
|
var element = document.createElement("H4")
|
||||||
|
element.innerHTML = value
|
||||||
|
return element
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class WizardItem extends WizardCategory {
|
||||||
|
key:string
|
||||||
|
headline:string
|
||||||
|
|
||||||
|
constructor(key:string, headline:string) {
|
||||||
|
super()
|
||||||
|
this.headline = headline
|
||||||
|
this.key = key
|
||||||
|
}
|
||||||
|
|
||||||
|
createWizard():void {
|
||||||
|
var headline = this.createCategoryHeadline(this.headline)
|
||||||
|
var key = this.key
|
||||||
|
var content:PopupContent = new PopupContent()
|
||||||
|
var description:string
|
||||||
|
|
||||||
|
var doc = document.getElementById(this.DocumentID)
|
||||||
|
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)
|
||||||
|
|
||||||
|
description = "{{.wizard.tuner.description}}"
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "epgSource":
|
||||||
|
var text:any[] = ["PMS", "XEPG"]
|
||||||
|
var values:any[] = ["PMS", "XEPG"]
|
||||||
|
|
||||||
|
var select = content.createSelect(text, values, "XEPG", key)
|
||||||
|
select.setAttribute("class", "wizard")
|
||||||
|
select.id = key
|
||||||
|
doc.appendChild(select)
|
||||||
|
|
||||||
|
description = "{{.wizard.epgSource.description}}"
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
|
case "m3u":
|
||||||
|
var input = content.createInput("text", key, "")
|
||||||
|
input.setAttribute("class", "wizard")
|
||||||
|
input.id = key
|
||||||
|
doc.appendChild(input)
|
||||||
|
|
||||||
|
description = "{{.wizard.m3u.description}}"
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
|
case "xmltv":
|
||||||
|
var input = content.createInput("text", key, "")
|
||||||
|
input.setAttribute("class", "wizard")
|
||||||
|
input.id = key
|
||||||
|
doc.appendChild(input)
|
||||||
|
|
||||||
|
description = "{{.wizard.xmltv.description}}"
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
|
default:
|
||||||
|
console.log(key)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
var pre = document.createElement("PRE")
|
||||||
|
pre.innerHTML = description
|
||||||
|
doc.appendChild(pre)
|
||||||
|
|
||||||
|
console.log(headline, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function readyForConfiguration(wizard:number) {
|
||||||
|
|
||||||
|
var server: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:string
|
||||||
|
var value:any
|
||||||
|
|
||||||
|
switch (config[i].tagName) {
|
||||||
|
case "SELECT":
|
||||||
|
name = (config[i] as HTMLSelectElement).name
|
||||||
|
value = (config[i] as HTMLSelectElement).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] as HTMLInputElement).type) {
|
||||||
|
case "text":
|
||||||
|
name = (config[i] as HTMLInputElement).name
|
||||||
|
value = (config[i] as HTMLInputElement).value
|
||||||
|
|
||||||
|
wizard[name] = value
|
||||||
|
break
|
||||||
|
}
|
||||||
|
break
|
||||||
|
|
||||||
|
default:
|
||||||
|
// code...
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
var data = new Object()
|
||||||
|
data["wizard"] = wizard
|
||||||
|
|
||||||
|
var server: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}}"))
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
class Log {
|
||||||
|
|
||||||
|
createLog(entry:string):any {
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function showLogs(bottom:boolean) {
|
||||||
|
|
||||||
|
var log = new Log()
|
||||||
|
|
||||||
|
var logs = SERVER["log"]["log"]
|
||||||
|
var div = document.getElementById("content_log")
|
||||||
|
|
||||||
|
div.innerHTML = ""
|
||||||
|
|
||||||
|
var keys = getObjKeys(logs)
|
||||||
|
|
||||||
|
keys.forEach(logID => {
|
||||||
|
|
||||||
|
var entry = log.createLog(logs[logID])
|
||||||
|
|
||||||
|
div.append(entry)
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(function(){
|
||||||
|
|
||||||
|
if (bottom == true) {
|
||||||
|
|
||||||
|
var wrapper = document.getElementById("box-wrapper");
|
||||||
|
wrapper.scrollTop = wrapper.scrollHeight;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}, 10);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetLogs() {
|
||||||
|
|
||||||
|
var cmd = "resetLogs"
|
||||||
|
var data = new Object()
|
||||||
|
var server:Server = new Server(cmd)
|
||||||
|
server.request(data)
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,147 @@
|
|||||||
|
class Server {
|
||||||
|
protocol:string
|
||||||
|
cmd:string
|
||||||
|
|
||||||
|
constructor(cmd:string) {
|
||||||
|
this.cmd = cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
request(data:Object):any {
|
||||||
|
|
||||||
|
if (SERVER_CONNECTION == true) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
SERVER_CONNECTION = true
|
||||||
|
|
||||||
|
console.log(data)
|
||||||
|
if (this.cmd != "updateLog") {
|
||||||
|
showElement("loading", true)
|
||||||
|
UNDO = new Object()
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(window.location.protocol) {
|
||||||
|
case "http:":
|
||||||
|
this.protocol = "ws://"
|
||||||
|
break
|
||||||
|
case "https://":
|
||||||
|
this.protocol = "wss://"
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
var url = this.protocol + window.location.hostname + ":" + window.location.port + "/data/" + "?Token=" + getCookie("Token")
|
||||||
|
|
||||||
|
data["cmd"] = this.cmd
|
||||||
|
var ws = new WebSocket(url)
|
||||||
|
ws.onopen = function() {
|
||||||
|
|
||||||
|
WS_AVAILABLE = true
|
||||||
|
|
||||||
|
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.")
|
||||||
|
SERVER_CONNECTION = false
|
||||||
|
|
||||||
|
if (WS_AVAILABLE == false) {
|
||||||
|
alert("No websocket connection to xTeVe could be established. Check your network configuration.")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ws.onmessage = function (e) {
|
||||||
|
|
||||||
|
SERVER_CONNECTION = false
|
||||||
|
showElement("loading", false)
|
||||||
|
|
||||||
|
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) {
|
||||||
|
|
||||||
|
alert(response["err"])
|
||||||
|
|
||||||
|
if (response.hasOwnProperty("reload")) {
|
||||||
|
location.reload()
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (response.hasOwnProperty("logoURL")) {
|
||||||
|
var div = (document.getElementById("channel-icon") as HTMLInputElement)
|
||||||
|
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()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCookie(name) {
|
||||||
|
var value = "; " + document.cookie;
|
||||||
|
var parts = value.split("; " + name + "=");
|
||||||
|
if (parts.length == 2) return parts.pop().split(";").shift();
|
||||||
|
}
|
||||||
@@ -0,0 +1,564 @@
|
|||||||
|
class SettingsCategory {
|
||||||
|
DocumentID:string = "content_settings"
|
||||||
|
createCategoryHeadline(value:string):any {
|
||||||
|
var element = document.createElement("H4")
|
||||||
|
element.innerHTML = value
|
||||||
|
return element
|
||||||
|
}
|
||||||
|
|
||||||
|
createHR():any {
|
||||||
|
var element = document.createElement("HR")
|
||||||
|
return element
|
||||||
|
}
|
||||||
|
|
||||||
|
createSettings(settingsKey:string):any {
|
||||||
|
var setting = document.createElement("TR")
|
||||||
|
var content:PopupContent = 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 "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
|
||||||
|
|
||||||
|
// 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 "xteveAutoUpdate":
|
||||||
|
var tdLeft = document.createElement("TD")
|
||||||
|
tdLeft.innerHTML = "{{.settings.xteveAutoUpdate.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 "buffer":
|
||||||
|
var tdLeft = document.createElement("TD")
|
||||||
|
tdLeft.innerHTML = "{{.settings.streamBuffering.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
|
||||||
|
|
||||||
|
// Select
|
||||||
|
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:any[] = ["PMS", "XEPG"]
|
||||||
|
var values:any[] = ["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:any[] = ["5", "10", "20", "30", "40", "50"]
|
||||||
|
var values:any[] = ["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:any[] = ["0.5 MB", "1 MB", "2 MB", "3 MB", "4 MB", "5 MB", "6 MB", "7 MB", "8 MB"]
|
||||||
|
var values:any[] = ["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
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return setting
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
createDescription(settingsKey:string):any {
|
||||||
|
|
||||||
|
var description = document.createElement("TR")
|
||||||
|
var text:string
|
||||||
|
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 "xteveAutoUpdate":
|
||||||
|
text = "{{.settings.xteveAutoUpdate.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 "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 "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
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class SettingsCategoryItem extends SettingsCategory {
|
||||||
|
headline:string
|
||||||
|
settingsKeys:string
|
||||||
|
|
||||||
|
constructor(headline:string, settingsKeys:string) {
|
||||||
|
super()
|
||||||
|
this.headline = headline
|
||||||
|
this.settingsKeys = settingsKeys
|
||||||
|
}
|
||||||
|
|
||||||
|
createCategory():void {
|
||||||
|
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(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())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function showSettings() {
|
||||||
|
console.log("SETTINGS");
|
||||||
|
|
||||||
|
for (let 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 (let i = 0; i < settings.length; i++) {
|
||||||
|
|
||||||
|
var name:string
|
||||||
|
var value:any
|
||||||
|
|
||||||
|
switch (settings[i].tagName) {
|
||||||
|
case "INPUT":
|
||||||
|
|
||||||
|
switch ((settings[i] as HTMLInputElement).type) {
|
||||||
|
case "checkbox":
|
||||||
|
name = (settings[i] as HTMLInputElement).name
|
||||||
|
value = (settings[i] as HTMLInputElement).checked
|
||||||
|
newSettings[name] = value
|
||||||
|
break
|
||||||
|
|
||||||
|
case "text":
|
||||||
|
name = (settings[i] as HTMLInputElement).name
|
||||||
|
value = (settings[i] as HTMLInputElement).value
|
||||||
|
|
||||||
|
switch (name) {
|
||||||
|
case "update":
|
||||||
|
value = value.split(",")
|
||||||
|
value = value.filter(function(e:any) { return e})
|
||||||
|
break
|
||||||
|
|
||||||
|
case "buffer.timeout":
|
||||||
|
value = parseFloat(value)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
newSettings[name] = value
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
|
case "SELECT":
|
||||||
|
name = (settings[i] as HTMLSelectElement).name
|
||||||
|
value = (settings[i] as HTMLSelectElement).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:Server = new Server(cmd)
|
||||||
|
server.request(data)
|
||||||
|
}
|
||||||
@@ -0,0 +1,171 @@
|
|||||||
|
// Copyright 2019 marmei. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT license that can be found in the
|
||||||
|
// LICENSE file.
|
||||||
|
// GitHub: https://github.com/xteve-project/xTeVe
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"./src"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GitHubStruct : GitHub Account. Über diesen Account werden die Updates veröffentlicht
|
||||||
|
type GitHubStruct struct {
|
||||||
|
Branch string
|
||||||
|
Repo string
|
||||||
|
Update bool
|
||||||
|
User string
|
||||||
|
}
|
||||||
|
|
||||||
|
// GitHub : GitHub Account
|
||||||
|
// If you want to fork this project, enter your Github account here. This prevents a newer version of xTeVe from updating your version.
|
||||||
|
var GitHub = GitHubStruct{Branch: "master", User: "xteve-project", Repo: "xTeVe-Downloads", Update: true}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Branch: GitHub Branch
|
||||||
|
User: GitHub Username
|
||||||
|
Repo: GitHub Repository
|
||||||
|
Update: Automatic updates from the GitHub repository [true|false]
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Name : Programname
|
||||||
|
const Name = "xTeVe"
|
||||||
|
|
||||||
|
// Version : Version, die Build Nummer wird in der main func geparst.
|
||||||
|
const Version = "2.0.0.0000"
|
||||||
|
|
||||||
|
// APIVersion : API Version
|
||||||
|
const APIVersion = "1.1.0"
|
||||||
|
|
||||||
|
// Dev : Aktiviert den Entwicklungsmodus. Für den Webserver werden dann die lokalen Dateien verwendet.
|
||||||
|
const Dev = false
|
||||||
|
|
||||||
|
var homeDirectory = fmt.Sprintf("%s%s.%s%s", src.GetUserHomeDirectory(), string(os.PathSeparator), strings.ToLower(Name), string(os.PathSeparator))
|
||||||
|
var samplePath = fmt.Sprintf("%spath%sto%sxteve%s", string(os.PathSeparator), string(os.PathSeparator), string(os.PathSeparator), string(os.PathSeparator))
|
||||||
|
|
||||||
|
var configFolder = flag.String("config", "", ": Config Folder ["+samplePath+"] (default: "+homeDirectory+")")
|
||||||
|
var port = flag.String("port", "", ": Server port [34400] (default: 34400)")
|
||||||
|
|
||||||
|
var gitBranch = flag.String("branch", "", ": Git Branch [master|beta] (default: master)")
|
||||||
|
var debug = flag.Int("debug", 0, ": Debug level [0 - 3] (default: 0)")
|
||||||
|
var h = flag.Bool("h", false, ": Show help")
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
// Build-Nummer von der Versionsnummer trennen
|
||||||
|
var build = strings.Split(Version, ".")
|
||||||
|
|
||||||
|
var system = &src.System
|
||||||
|
system.APIVersion = APIVersion
|
||||||
|
system.Branch = GitHub.Branch
|
||||||
|
system.Build = build[len(build)-1:][0]
|
||||||
|
system.Dev = Dev
|
||||||
|
system.GitHub = GitHub
|
||||||
|
system.Name = Name
|
||||||
|
system.Version = strings.Join(build[0:len(build)-1], ".")
|
||||||
|
|
||||||
|
// Panic !!!
|
||||||
|
defer func() {
|
||||||
|
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
|
||||||
|
fmt.Println()
|
||||||
|
fmt.Println("* * * * * FATAL ERROR * * * * *")
|
||||||
|
fmt.Println("OS: ", runtime.GOOS)
|
||||||
|
fmt.Println("Arch:", runtime.GOARCH)
|
||||||
|
fmt.Println("Err: ", r)
|
||||||
|
fmt.Println()
|
||||||
|
|
||||||
|
pc := make([]uintptr, 20)
|
||||||
|
runtime.Callers(2, pc)
|
||||||
|
|
||||||
|
for i := range pc {
|
||||||
|
|
||||||
|
if runtime.FuncForPC(pc[i]) != nil {
|
||||||
|
|
||||||
|
f := runtime.FuncForPC(pc[i])
|
||||||
|
file, line := f.FileLine(pc[i])
|
||||||
|
|
||||||
|
if string(file)[0:1] != "?" {
|
||||||
|
fmt.Printf("%s:%d %s\n", filepath.Base(file), line, f.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println()
|
||||||
|
fmt.Println("* * * * * * * * * * * * * * * *")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}()
|
||||||
|
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if *h {
|
||||||
|
flag.Usage()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Webserver Port
|
||||||
|
if len(*port) > 0 {
|
||||||
|
system.Flag.Port = *port
|
||||||
|
}
|
||||||
|
|
||||||
|
// Branch
|
||||||
|
system.Flag.Branch = *gitBranch
|
||||||
|
if len(system.Flag.Branch) > 0 {
|
||||||
|
fmt.Println("Git Branch is now:", system.Flag.Branch)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug Level
|
||||||
|
system.Flag.Debug = *debug
|
||||||
|
if system.Flag.Debug > 3 {
|
||||||
|
flag.Usage()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Speicherort für die Konfigurationsdateien
|
||||||
|
if len(*configFolder) > 0 {
|
||||||
|
system.Folder.Config = *configFolder
|
||||||
|
}
|
||||||
|
|
||||||
|
err := src.Init()
|
||||||
|
if err != nil {
|
||||||
|
src.ShowError(err, 0)
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = src.BinaryUpdate()
|
||||||
|
if err != nil {
|
||||||
|
src.ShowError(err, 0)
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = src.StartSystem(false)
|
||||||
|
if err != nil {
|
||||||
|
src.ShowError(err, 0)
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = src.InitMaintenance()
|
||||||
|
if err != nil {
|
||||||
|
src.ShowError(err, 0)
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = src.StartWebserver()
|
||||||
|
if err != nil {
|
||||||
|
src.ShowError(err, 0)
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||