53 Commits

Author SHA1 Message Date
marmei
72eb7fb599 v2.1.0.0100 2019-10-11 17:59:11 +02:00
marmei
4b969b8cee Merge branch 'beta' 2019-10-11 17:54:06 +02:00
marmei
3d9266dabe XEPG performance 2019-10-11 17:53:20 +02:00
marmei
48218cda50 Update changelog-beta.md 2019-10-04 2019-10-04 19:52:44 +02:00
marmei
dc42afcd05 New beta 2019-10-04 19:39:54 +02:00
marmei
20e5e1b545 Buffer RTSP performance 2019-10-04 19:39:20 +02:00
Aaron Chi
11dd830110 fix #34 2019-10-04 17:08:16 +08:00
marmei
7c87d1d5bd Wrong CVLC path 2019-09-27 22:20:19 +02:00
marmei
6cdd44357b update changelog-beta.md 2019-09-27 19:55:36 +02:00
marmei
ad992eb615 Add FFmpeg and VLC support 2019-09-27 19:24:31 +02:00
marmei
11453c6053 v2.0.3.0030 2019-09-07 12:51:21 +02:00
marmei
81e8ae33d7 Merge branch 'Bugs_in_2.0.2' 2019-09-07 12:07:55 +02:00
marmei
f3be0fca47 Wrong warning (root) 2019-09-05 19:37:55 +02:00
marmei
1c1c89cd74 Update changelog-beta.md 2019-09-04 2019-09-04 21:16:04 +02:00
marmei
3d73dba422 Code removed #1 2019-09-04 20:16:11 +02:00
marmei
c6e74fe11c Interception of repeated save 2019-09-04 20:12:26 +02:00
marmei
18dba46c02 Bug #16 1 2019-09-01 00:24:06 +02:00
marmei
efa55b39a9 Bug #16 0 2019-08-31 20:39:39 +02:00
marmei
717fa68b7e Add an error message if the filter rule is empty.
Set the streaming status to active only once.
2019-08-28 17:56:15 +02:00
marmei
878531ff79 IPTV removed as a source 2019-08-28 16:49:41 +02:00
xteve-project
792fd9a373 Merge pull request #14 from Coledunsby/master
Add support for "video/m2ts" video streams
2019-08-24 13:12:26 +02:00
marmei
a1ec0287ef v2.0.2.0020 2019-08-24 13:10:08 +02:00
marmei
a79e824ef8 Merge branch 'Coledunsby-master' 2019-08-24 13:04:48 +02:00
marmei
c843f424fe update changelog-beta.md 2019-08-23 23:34:25 +02:00
marmei
5c6637c048 Add support for "video/m2ts" video streams 2019-08-23 23:21:41 +02:00
marmei
1062e072d6 update readme.md 2019-08-23 23:17:40 +02:00
Cole Dunsby
d831a099f0 Add support for "video/m2ts" video streams 2019-08-19 22:14:53 -04:00
marmei
a06baef4d3 Bug #9 - Fixed, incorrect original-air-date 2019-08-18 11:19:03 +02:00
marmei
f9d1a45bbd Add original group-title to mapping editor 2019-08-17 08:57:06 +02:00
marmei
8a4fb8ba30 v2.0.1.0010 2019-08-09 19:09:40 +02:00
marmei
28dad4932b v2.0.0.0009 2019-08-09 18:02:24 +02:00
marmei
1769b1e6db v2.0.0.0009-beta 2019-08-09 17:58:34 +02:00
marmei
30ab13f871 Update changelog-beta.md 2019-08-09 2019-08-09 09:47:43 +02:00
marmei
0f37835206 v2.0.0.0008-beta #2 2019-08-09 09:38:11 +02:00
marmei
67fe80b4fd v2.0.0.0008-beta
Pull request: Error in http/https detection. (#6)
2019-08-09 09:31:31 +02:00
marmei
96e10ff51d 2019-08-09 2019-08-09 09:21:42 +02:00
marmei
d550beaa01 Merge pull request #6 from hexrus/patch-1
Error in http/https detection.
2019-08-09 09:19:14 +02:00
hexrus
00ec32456c Error in http/https detection.
window.location.protocol return "https:", not "https://"
2019-08-09 09:53:55 +03:00
marmei
b3600f8a14 2019-07-08 2019-08-07 13:28:47 +02:00
marmei
849493a802 Add: changelog-beta.md
Update: README.MD
2019-08-07 13:23:49 +02:00
marmei
3730d1187d v2.0.0.0007-beta
Add CLI: info
Add CLI: restore
Improves UPnP support for Plex
CSS: Add min-width for Ch.No.
Fixed missing images on cache for localhost
2019-08-07 12:35:56 +02:00
marmei
2a06bf6b01 Add HLS VOD support 2019-08-05 12:28:47 +02:00
marmei
c389b990b4 v2.0.0.0002-beta 2019-08-04 18:10:42 +02:00
marmei
56f3e3389b Unused function removed 2019-08-04 10:35:41 +02:00
marmei
50e6ed274e Update schedule:
Removes all spaces from the values [ 1000] -> [1000]
2019-08-04 09:49:51 +02:00
marmei
8490187d31 v2.0.0.0001 2019-08-03 14:58:37 +02:00
marmei
4dc9dfabf2 Wizard: Add input placeholder (M3U, XMLTV)
Wizard: Alert by empty value (M3U, XMLTV)
2019-08-03 14:52:17 +02:00
marmei
5fc5f773d3 Merge remote-tracking branch 'origin/master' 2019-08-03 14:20:54 +02:00
marmei
c9bc4aedbc Ignore invalid image URLs 2019-08-03 14:20:26 +02:00
xteve-project
51bab26d59 Update issue templates 2019-08-03 11:53:54 +02:00
marmei
0063dcc523 Merge branch 'pr/2' 2019-08-03 09:48:41 +02:00
marmei
23dd3ef08c New Docker from alturismo 2019-08-03 09:41:05 +02:00
Steve
9b84982bab Update README.md
Typos
2019-08-02 16:01:44 -05:00
38 changed files with 2050 additions and 809 deletions

View File

@@ -0,0 +1,20 @@
---
name: Feature request
about: Please only useful feature request.
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

1
.gitignore vendored
View File

@@ -1,4 +1,5 @@
.DS_Store .DS_Store
demo
compiler compiler
files files
update_xteve*.sh update_xteve*.sh

View File

@@ -65,10 +65,11 @@ Thanks to @alturismo and @LeeD for creating the Docker Images.
**Created by alturismo:** **Created by alturismo:**
[xTeVe](https://hub.docker.com/r/alturismo/xteve) [xTeVe](https://hub.docker.com/r/alturismo/xteve)
[xTeVe / Guide2go](https://hub.docker.com/r/alturismo/xteve_guide2go) [xTeVe / Guide2go](https://hub.docker.com/r/alturismo/xteve_guide2go)
[xTeVe / Guide2go / owi2plex](https://hub.docker.com/r/alturismo/xteve_g2g_owi)
Including: Including:
- Guide2go: XMLTV grabber for Schedules Direct - Guide2go: XMLTV grabber for Schedules Direct
- owi2plex: XMLTV file grabber for Enigma receivers
**Created by LeeD:** **Created by LeeD:**
[xTeVe / Guide2go / Zap2XML](https://hub.docker.com/r/dnsforge/xteve) [xTeVe / Guide2go / Zap2XML](https://hub.docker.com/r/dnsforge/xteve)
@@ -82,19 +83,50 @@ Including:
--- ---
### xTeVe Beta branch
New features and bug fixes are only available in beta brunch. Only after successful testing, they are merged into the master branch.
**It is not recommended to use the beta version in a production system.**
With the command line argument `branch` the Git Branch can be changed. xTeVe must be started via the terminal.
#### Switch from master to beta branch:
```
xteve -branch beta
...
[xTeVe] GitHub: https://github.com/xteve-project
[xTeVe] Git Branch: beta [xteve-project]
...
```
#### Switch from beta to master branch:
```
xteve -branch master
...
[xTeVe] GitHub: https://github.com/xteve-project
[xTeVe] Git Branch: master [xteve-project]
...
```
When the branch is changed, an update is only performed if there is a new version and the update function is activated in the settings.
---
## Build from source code [Go / Golang] ## Build from source code [Go / Golang]
#### Requirements #### Requirements
* Go (go1.12.4 or newer) * [Go](https://golang.org) (go1.12.4 or newer)
#### Dependancys #### Dependencies
* [go-ssdp](https://github.com/koron/go-ssdp) * [go-ssdp](https://github.com/koron/go-ssdp)
* [websocket](https://github.com/gorilla/websocket) * [websocket](https://github.com/gorilla/websocket)
* [osext](https://github.com/kardianos/osext) * [osext](https://github.com/kardianos/osext)
#### Build #### Build
1. Download source code 1. Download source code
2. Install dependancys 2. Install dependencies
``` ```
go get github.com/koron/go-ssdp go get github.com/koron/go-ssdp
go get github.com/gorilla/websocket go get github.com/gorilla/websocket
@@ -108,7 +140,7 @@ go build xteve.go
--- ---
## Fork without pull request :mega: ## 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. When creating a fork, the xTeVe GitHub account must be changed from the source code or the update function disabled.
Future updates of the xteve-project would update your fork. :wink: Future updates of the xteve-project would update your fork. :wink:
xteve.go - Line: 29 xteve.go - Line: 29

62
changelog-beta.md Normal file
View File

@@ -0,0 +1,62 @@
#### 2.0.3.0042-beta
**Version 2.0.3.0042 changes the settings.json.**
Settings from the current beta can not be used for the current master version 2.0.3
- New default options for VLC and FFmpeg
- VLC and FFmpeg log entries in the xTeVe log
- Less CPU load with VLC and FFmpeg
#### 2.0.3.0035-beta
```diff
+ FFmpeg support
+ VLC support
```
**Version 2.0.3.0035 changes the settings.json.**
Settings from the current beta can not be used for the current master version 2.0.3
#### 2.0.2.0024-beta
```diff
+ Improved monitoring of the buffer process
+ Update the XEPG database a bit faster
```
##### Fixes
- Error message if filter rule is missing
- Channels are lost when saving again (Mapping)
- Plex log, invalid source: IPTV
#### 2.0.1.0012-beta
```diff
+ Add support for "video/m2ts" video streams (Pull request #14)
```
#### 2.0.1.0011-beta
```diff
+ Original group title is shown in the Mapping Editor
```
##### Fixes
- incorrect original-air-date
#### 2.0.1.0010-beta
```diff
+ Set timestamp to <episode-num system="original-air-date">
```
#### 2.0.0.0008-beta
##### Fixes
- Pull request #6 [Error in http/https detection] window.location.protocol return "https:", not "https://"
#### 2.0.0.0007-beta
```diff
+ Buffer HLS: Add VOD tag from M3U8
+ CLI: Add new arguments [-restore]
+ CLI: Add new arguments [-info]
```
##### Fixes
- Missing images with caching for localhost URL
#### 2.0.0.0001-beta
```diff
+ Wizard: Add HTML input placeholder (M3U, XMLTV)
+ Wizard: Alert by empty value (M3U, XMLTV)
+ Image caching: Ignore invalid image URLs
```

View File

@@ -299,6 +299,8 @@ tbody {
#content_table input[type=text]{ #content_table input[type=text]{
width: 80%; width: 80%;
min-width: 35px;
max-width: 60px;
border: 0px; border: 0px;
background-color: #333; background-color: #333;
margin-left: 5px; margin-left: 5px;

View File

@@ -1,3 +0,0 @@
h1 {
color: green;
}

View File

@@ -20,7 +20,7 @@ menuItems.push(new MainMenuItem("logout", "{{.mainMenu.item.logout}}", "logout.p
var settingsCategory = new Array(); var settingsCategory = new Array();
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.general}}", "xteveAutoUpdate,tuner,epgSource,api")); 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.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.streaming}}", "buffer,buffer.size.kb,buffer.timeout,user.agent,ffmpeg.path,ffmpeg.options,vlc.path,vlc.options"));
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.backup}}", "backup.path,backup.keep")); settingsCategory.push(new SettingsCategoryItem("{{.settings.category.backup}}", "backup.path,backup.keep"));
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.authentication}}", "authentication.web,authentication.pms,authentication.m3u,authentication.xml,authentication.api")); settingsCategory.push(new SettingsCategoryItem("{{.settings.category.authentication}}", "authentication.web,authentication.pms,authentication.m3u,authentication.xml,authentication.api"));
function showPopUpElement(elm) { function showPopUpElement(elm) {

View File

@@ -63,6 +63,7 @@ var WizardItem = /** @class */ (function (_super) {
break; break;
case "m3u": case "m3u":
var input = content.createInput("text", key, ""); var input = content.createInput("text", key, "");
input.setAttribute("placeholder", "{{.wizard.m3u.placeholder}}");
input.setAttribute("class", "wizard"); input.setAttribute("class", "wizard");
input.id = key; input.id = key;
doc.appendChild(input); doc.appendChild(input);
@@ -70,6 +71,7 @@ var WizardItem = /** @class */ (function (_super) {
break; break;
case "xmltv": case "xmltv":
var input = content.createInput("text", key, ""); var input = content.createInput("text", key, "");
input.setAttribute("placeholder", "{{.wizard.xmltv.placeholder}}");
input.setAttribute("class", "wizard"); input.setAttribute("class", "wizard");
input.id = key; input.id = key;
doc.appendChild(input); doc.appendChild(input);
@@ -117,6 +119,11 @@ function saveWizard() {
case "text": case "text":
name = config[i].name; name = config[i].name;
value = config[i].value; value = config[i].value;
if (value.length == 0) {
var msg = name.toUpperCase() + ": " + "{{.alert.missingInput}}";
alert(msg);
return;
}
wizard[name] = value; wizard[name] = value;
break; break;
} }

View File

@@ -127,7 +127,7 @@ var Content = /** @class */ (function () {
var cell = new Cell(); var cell = new Cell();
cell.child = true; cell.child = true;
cell.childType = "P"; cell.childType = "P";
if (SERVER["settings"]["buffer"] == true) { if (SERVER["settings"]["buffer"] != "-") {
cell.value = data[key]["tuner"]; cell.value = data[key]["tuner"];
} }
else { else {
@@ -583,7 +583,7 @@ var ShowContent = /** @class */ (function (_super) {
return; return;
break; break;
case "log": case "log":
var input = this.createInput("button", menuKey, "{{.button.resetlogs}}"); var input = this.createInput("button", menuKey, "{{.button.resetLogs}}");
input.setAttribute("onclick", 'javascript: resetLogs();'); input.setAttribute("onclick", 'javascript: resetLogs();');
interaction.appendChild(input); interaction.appendChild(input);
var wrapper = document.createElement("DIV"); var wrapper = document.createElement("DIV");
@@ -901,7 +901,7 @@ function openPopUp(dataType, element) {
input.setAttribute("placeholder", "{{.playlist.fileM3U.placeholder}}"); input.setAttribute("placeholder", "{{.playlist.fileM3U.placeholder}}");
content.appendRow("{{.playlist.fileM3U.title}}", input); content.appendRow("{{.playlist.fileM3U.title}}", input);
// Tuner // Tuner
if (SERVER["settings"]["buffer"] == true) { if (SERVER["settings"]["buffer"] != "-") {
var text = new Array(); var text = new Array();
var values = new Array(); var values = new Array();
for (var i = 1; i <= 100; i++) { for (var i = 1; i <= 100; i++) {
@@ -971,7 +971,7 @@ function openPopUp(dataType, element) {
input.setAttribute("placeholder", "{{.playlist.fileHDHR.placeholder}}"); input.setAttribute("placeholder", "{{.playlist.fileHDHR.placeholder}}");
content.appendRow("{{.playlist.fileHDHR.title}}", input); content.appendRow("{{.playlist.fileHDHR.title}}", input);
// Tuner // Tuner
if (SERVER["settings"]["buffer"] == true) { if (SERVER["settings"]["buffer"] != "-") {
var text = new Array(); var text = new Array();
var values = new Array(); var values = new Array();
for (var i = 1; i <= 100; i++) { for (var i = 1; i <= 100; i++) {
@@ -1252,6 +1252,7 @@ function openPopUp(dataType, element) {
input.setAttribute("readonly", "true"); input.setAttribute("readonly", "true");
} }
content.appendRow("{{.mapping.channelName.title}}", input); content.appendRow("{{.mapping.channelName.title}}", input);
content.description(data["name"]);
// Aktualisierung des Kanalnamens // Aktualisierung des Kanalnamens
if (data.hasOwnProperty("_uuid.key")) { if (data.hasOwnProperty("_uuid.key")) {
if (data["_uuid.key"] != "") { if (data["_uuid.key"] != "") {
@@ -1287,6 +1288,9 @@ function openPopUp(dataType, element) {
var input = content.createInput("text", dbKey, data[dbKey]); var input = content.createInput("text", dbKey, data[dbKey]);
input.setAttribute("onchange", "javascript: this.className = 'changed'"); input.setAttribute("onchange", "javascript: this.className = 'changed'");
content.appendRow("{{.mapping.m3uGroupTitle.title}}", input); content.appendRow("{{.mapping.m3uGroupTitle.title}}", input);
if (data["group-title"] != undefined) {
content.description(data["group-title"]);
}
// XMLTV Datei // XMLTV Datei
var dbKey = "x-xmltv-file"; var dbKey = "x-xmltv-file";
var xmlFile = data[dbKey]; var xmlFile = data[dbKey];

View File

@@ -16,7 +16,7 @@ var Server = /** @class */ (function () {
case "http:": case "http:":
this.protocol = "ws://"; this.protocol = "ws://";
break; break;
case "https://": case "https:":
this.protocol = "wss://"; this.protocol = "wss://";
break; break;
} }

View File

@@ -85,6 +85,50 @@ var SettingsCategory = /** @class */ (function () {
setting.appendChild(tdLeft); setting.appendChild(tdLeft);
setting.appendChild(tdRight); setting.appendChild(tdRight);
break; break;
case "ffmpeg.path":
var tdLeft = document.createElement("TD");
tdLeft.innerHTML = "{{.settings.ffmpegPath.title}}" + ":";
var tdRight = document.createElement("TD");
var input = content.createInput("text", "ffmpeg.path", data);
input.setAttribute("placeholder", "{{.settings.ffmpegPath.placeholder}}");
input.setAttribute("onchange", "javascript: this.className = 'changed'");
tdRight.appendChild(input);
setting.appendChild(tdLeft);
setting.appendChild(tdRight);
break;
case "ffmpeg.options":
var tdLeft = document.createElement("TD");
tdLeft.innerHTML = "{{.settings.ffmpegOptions.title}}" + ":";
var tdRight = document.createElement("TD");
var input = content.createInput("text", "ffmpeg.options", data);
input.setAttribute("placeholder", "{{.settings.ffmpegOptions.placeholder}}");
input.setAttribute("onchange", "javascript: this.className = 'changed'");
tdRight.appendChild(input);
setting.appendChild(tdLeft);
setting.appendChild(tdRight);
break;
case "vlc.path":
var tdLeft = document.createElement("TD");
tdLeft.innerHTML = "{{.settings.vlcPath.title}}" + ":";
var tdRight = document.createElement("TD");
var input = content.createInput("text", "vlc.path", data);
input.setAttribute("placeholder", "{{.settings.vlcPath.placeholder}}");
input.setAttribute("onchange", "javascript: this.className = 'changed'");
tdRight.appendChild(input);
setting.appendChild(tdLeft);
setting.appendChild(tdRight);
break;
case "vlc.options":
var tdLeft = document.createElement("TD");
tdLeft.innerHTML = "{{.settings.vlcOptions.title}}" + ":";
var tdRight = document.createElement("TD");
var input = content.createInput("text", "vlc.options", data);
input.setAttribute("placeholder", "{{.settings.vlcOptions.placeholder}}");
input.setAttribute("onchange", "javascript: this.className = 'changed'");
tdRight.appendChild(input);
setting.appendChild(tdLeft);
setting.appendChild(tdRight);
break;
// Checkboxen // Checkboxen
case "authentication.web": case "authentication.web":
var tdLeft = document.createElement("TD"); var tdLeft = document.createElement("TD");
@@ -185,17 +229,6 @@ var SettingsCategory = /** @class */ (function () {
setting.appendChild(tdLeft); setting.appendChild(tdLeft);
setting.appendChild(tdRight); setting.appendChild(tdRight);
break; 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": case "api":
var tdLeft = document.createElement("TD"); var tdLeft = document.createElement("TD");
tdLeft.innerHTML = "{{.settings.api.title}}" + ":"; tdLeft.innerHTML = "{{.settings.api.title}}" + ":";
@@ -260,6 +293,18 @@ var SettingsCategory = /** @class */ (function () {
setting.appendChild(tdLeft); setting.appendChild(tdLeft);
setting.appendChild(tdRight); setting.appendChild(tdRight);
break; break;
case "buffer":
var tdLeft = document.createElement("TD");
tdLeft.innerHTML = "{{.settings.streamBuffering.title}}" + ":";
var tdRight = document.createElement("TD");
var text = ["{{.settings.streamBuffering.info_false}}", "xTeVe: ({{.settings.streamBuffering.info_xteve}})", "FFmpeg: ({{.settings.streamBuffering.info_ffmpeg}})", "VLC: ({{.settings.streamBuffering.info_vlc}})"];
var values = ["-", "xteve", "ffmpeg", "vlc"];
var select = content.createSelect(text, values, data, settingsKey);
select.setAttribute("onchange", "javascript: this.className = 'changed'");
tdRight.appendChild(select);
setting.appendChild(tdLeft);
setting.appendChild(tdRight);
break;
} }
return setting; return setting;
}; };
@@ -308,6 +353,18 @@ var SettingsCategory = /** @class */ (function () {
case "user.agent": case "user.agent":
text = "{{.settings.userAgent.description}}"; text = "{{.settings.userAgent.description}}";
break; break;
case "ffmpeg.path":
text = "{{.settings.ffmpegPath.description}}";
break;
case "ffmpeg.options":
text = "{{.settings.ffmpegOptions.description}}";
break;
case "vlc.path":
text = "{{.settings.vlcPath.description}}";
break;
case "vlc.options":
text = "{{.settings.vlcOptions.description}}";
break;
case "epgSource": case "epgSource":
text = "{{.settings.epgSource.description}}"; text = "{{.settings.epgSource.description}}";
break; break;

View File

@@ -23,11 +23,12 @@
} }
}, },
"confirm":{ "confirm":{
"restore": "All data will be replaced with those from the backup.Should the files be restored?" "restore": "All data will be replaced with those from the backup. Should the files be restored?"
}, },
"alert": { "alert": {
"fileLoadingError": "File couldn't be loaded", "fileLoadingError": "File couldn't be loaded",
"invalidChannelNumber": "Invalid channel number" "invalidChannelNumber": "Invalid channel number",
"missingInput": "Missing input"
}, },
"button":{ "button":{
"back": "Back", "back": "Back",
@@ -44,7 +45,7 @@
"search": "Search", "search": "Search",
"update": "Update", "update": "Update",
"craeteAccount": "Create Account", "craeteAccount": "Create Account",
"resetlogs": "Reset Logs", "resetLogs": "Reset Logs",
"uploadLogo": "Upload Logo" "uploadLogo": "Upload Logo"
}, },
"filter": { "filter": {
@@ -242,7 +243,7 @@
}, },
"password": { "password": {
"title": "Password", "title": "Password",
"placeholder": "Passoword", "placeholder": "Password",
"description": "" "description": ""
}, },
"confirm": { "confirm": {
@@ -306,7 +307,7 @@
"description": "Updates all playlists, tuner and XMLTV files at startup." "description": "Updates all playlists, tuner and XMLTV files at startup."
}, },
"cacheImages": { "cacheImages": {
"title": "Image caching", "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." "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": { "replaceEmptyImages": {
@@ -319,7 +320,32 @@
}, },
"streamBuffering": { "streamBuffering": {
"title": "Stream Buffer", "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" "description": "Functions of the buffer:<br>- The stream is passed from xTeVe, FFmpeg or VLC to Plex, Emby or M3U Player<br>- Small jerking of the streams can be compensated<br>- HLS / M3U8 support<br>- RTP / RTPS support (only FFmpeg or VLC)<br>- Re-streaming<br>- Separate tuner limit for each playlist",
"info_false": "No Buffer (Client connects to the streaming server)",
"info_xteve": "xTeVe connects to the streaming server",
"info_ffmpeg": "FFmpeg connects to the streaming server",
"info_vlc": "VLC connects to the streaming server"
},
"ffmpegPath": {
"title": "FFmpeg Binary Path",
"description": "Path to FFmpeg binary.",
"placeholder": "/path/to/ffmpeg"
},
"ffmpegOptions": {
"title": "FFmpeg Options",
"description": "FFmpeg options.<br>Only change if you know what you are doing.<br>Leave blank to set default settings.",
"placeholder": "Leave blank to set default settings"
},
"vlcPath": {
"title": "VLC / CVLC Binary Path",
"description": "Path to VLC / CVLC binary.",
"placeholder": "/path/to/cvlc"
},
"vlcOptions": {
"title": "VLC / CVLC Options",
"description": "VLC / CVLC options.<br>Only change if you know what you are doing.<br>Leave blank to set default settings.",
"placeholder": "Leave blank to set default settings"
}, },
"bufferSize": { "bufferSize": {
"title": "Buffer Size", "title": "Buffer Size",
@@ -331,8 +357,8 @@
"placeholder": "100" "placeholder": "100"
}, },
"userAgent": { "userAgent": {
"title": "User agent", "title": "User Agent",
"description": "User Agent for HTTP requests", "description": "User Agent for HTTP requests. Only used if xTeVe is selected as the buffer.",
"placeholder": "xTeVe" "placeholder": "xTeVe"
}, },
"backupPath": { "backupPath": {
@@ -381,10 +407,12 @@
}, },
"m3u": { "m3u": {
"title": "M3U Playlist", "title": "M3U Playlist",
"placeholder": "File path or URL of the M3U",
"description": "Local or remote playlists" "description": "Local or remote playlists"
}, },
"xmltv": { "xmltv": {
"title": "XMLTV File", "title": "XMLTV File",
"placeholder": "File path or URL of the XMLTV",
"description": "Local or remote XMLTV file" "description": "Local or remote XMLTV file"
} }
}, },

View File

@@ -2,6 +2,7 @@ package src
import ( import (
b64 "encoding/base64" b64 "encoding/base64"
"errors"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
@@ -124,35 +125,44 @@ func xteveBackup() (archiv string, err error) {
return return
} }
func xteveRestore(input string) (newWebURL string, err error) { func xteveRestore(archive string) (newWebURL string, err error) {
var newPort, oldPort string var newPort, oldPort, backupVersion, tmpRestore string
// Base64 Json String in base64 umwandeln tmpRestore = System.Folder.Temp + "restore" + string(os.PathSeparator)
b64data := input[strings.IndexByte(input, ',')+1:]
// Base64 in bytes umwandeln und speichern
sDec, err := b64.StdEncoding.DecodeString(b64data)
err = checkFolder(tmpRestore)
if err != nil { if err != nil {
return return
} }
var archive = System.Folder.Temp + "restore.zip" // Zip Archiv in tmp entpacken
err = extractZIP(archive, tmpRestore)
err = writeByteToFile(archive, sDec)
if err != nil { if err != nil {
return return
} }
// Zip Archiv entpacken // Neue Config laden um den Port und die Version zu überprüfen
newConfig, err := loadJSONFileToMap(tmpRestore + "settings.json")
if err != nil {
ShowError(err, 0)
return
}
backupVersion = newConfig["version"].(string)
if backupVersion < System.Compatibility {
err = errors.New(getErrMsg(1013))
return
}
// Zip Archiv in den Config Ordner entpacken
err = extractZIP(archive, System.Folder.Config) err = extractZIP(archive, System.Folder.Config)
if err != nil { if err != nil {
return return
} }
// Neue Config laden um den Port zu überprüfen // Neue Config laden um den Port und die Version zu überprüfen
newConfig, err := loadJSONFileToMap(System.Folder.Config + "settings.json") newConfig, err = loadJSONFileToMap(System.Folder.Config + "settings.json")
if err != nil { if err != nil {
ShowError(err, 0) ShowError(err, 0)
return return
@@ -187,5 +197,78 @@ func xteveRestore(input string) (newWebURL string, err error) {
var url = System.URLBase + "/web/" var url = System.URLBase + "/web/"
newWebURL = strings.Replace(url, ":"+oldPort, ":"+newPort, 1) newWebURL = strings.Replace(url, ":"+oldPort, ":"+newPort, 1)
os.RemoveAll(tmpRestore)
return
}
func xteveRestoreFromWeb(input string) (newWebURL string, err error) {
// 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
}
newWebURL, err = xteveRestore(archive)
return
}
// XteveRestoreFromCLI : Wiederherstellung über die Kommandozeile
func XteveRestoreFromCLI(archive string) (err error) {
var confirm string
println()
showInfo(fmt.Sprintf("Version:%s Build: %s", System.Version, System.Build))
showInfo(fmt.Sprintf("Backup File:%s", archive))
showInfo(fmt.Sprintf("System Folder:%s", getPlatformPath(System.Folder.Config)))
println()
fmt.Print("All data will be replaced with those from the backup. Should the files be restored? [yes|no]:")
fmt.Scanln(&confirm)
switch strings.ToLower(confirm) {
case "yes":
break
case "no":
return
default:
fmt.Println("Invalid input")
return
}
if len(System.Folder.Config) > 0 {
err = checkFilePermission(System.Folder.Config)
if err != nil {
return
}
_, err = xteveRestore(archive)
if err != nil {
return
}
showHighlight(fmt.Sprintf("Restor:Backup was successfully restored. %s can now be started normally", System.Name))
}
return return
} }

File diff suppressed because it is too large Load Diff

View File

@@ -46,6 +46,10 @@ func Init() (err error) {
System.DVRLimit = 480 System.DVRLimit = 480
System.Compatibility = "1.4.4" System.Compatibility = "1.4.4"
// FFmpeg Default Einstellungen
System.FFmpeg.DefaultOptions = "-hide_banner -loglevel error -i [URL] -c copy -f mpegts pipe:1"
System.VLC.DefaultOptions = "-I dummy [URL] --sout #std{mux=ts,access=file,dst=-}"
// 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) // 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 Settings.LogEntriesRAM = 500
@@ -83,6 +87,11 @@ func Init() (err error) {
return return
} }
if len(System.Flag.Restore) > 0 {
// Einstellungen werden über CLI wiederhergestellt. Weitere Initialisierung ist nicht notwendig.
return
}
System.File.XML = getPlatformFile(fmt.Sprintf("%s%s.xml", System.Folder.Data, System.AppName)) 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)) System.File.M3U = getPlatformFile(fmt.Sprintf("%s%s.m3u", System.Folder.Data, System.AppName))
@@ -104,7 +113,7 @@ func Init() (err error) {
// Überprüfen ob xTeVe als root läuft // Überprüfen ob xTeVe als root läuft
if os.Geteuid() == 0 { if os.Geteuid() == 0 {
showWarning(2010) showWarning(2110)
} }
if System.Flag.Debug > 0 { if System.Flag.Debug > 0 {
@@ -113,6 +122,7 @@ func Init() (err error) {
} }
showInfo(fmt.Sprintf("Version:%s Build: %s", System.Version, System.Build)) showInfo(fmt.Sprintf("Version:%s Build: %s", System.Version, System.Build))
showInfo(fmt.Sprintf("Database Version:%s", System.DBVersion))
showInfo(fmt.Sprintf("System IP Addresses:IPv4: %d | IPv6: %d", len(System.IPAddressesV4), len(System.IPAddressesV6))) showInfo(fmt.Sprintf("System IP Addresses:IPv4: %d | IPv6: %d", len(System.IPAddressesV4), len(System.IPAddressesV6)))
showInfo("Hostname:" + System.Hostname) showInfo("Hostname:" + System.Hostname)
showInfo(fmt.Sprintf("System Folder:%s", getPlatformPath(System.Folder.Config))) showInfo(fmt.Sprintf("System Folder:%s", getPlatformPath(System.Folder.Config)))
@@ -127,7 +137,6 @@ func Init() (err error) {
// Bedingte Update Änderungen durchführen // Bedingte Update Änderungen durchführen
err = conditionalUpdateChanges() err = conditionalUpdateChanges()
if err != nil { if err != nil {
ShowError(err, 0)
return return
} }
@@ -160,9 +169,6 @@ func Init() (err error) {
return return
} }
// DLNA Server starten
go SSDP()
// Branch festlegen // Branch festlegen
System.Branch = Settings.Branch System.Branch = Settings.Branch
@@ -193,6 +199,13 @@ func Init() (err error) {
} }
// DLNA Server starten
err = SSDP()
if err != nil {
return
}
// HTML Datein laden
loadHTMLMap() loadHTMLMap()
return return

View File

@@ -24,6 +24,8 @@ func updateServerSettings(request RequestStruct) (settings SettingsStrcut, err e
var createXEPGFiles = false var createXEPGFiles = false
var debug string var debug string
// -vvv [URL] --sout '#transcode{vcodec=mp4v, acodec=mpga} :standard{access=http, mux=ogg}'
for key, value := range newSettings { for key, value := range newSettings {
if _, ok := oldSettings[key]; ok { if _, ok := oldSettings[key]; ok {
@@ -37,17 +39,25 @@ func updateServerSettings(request RequestStruct) (settings SettingsStrcut, err e
reloadData = true reloadData = true
case "update": case "update":
// Die Formatierung der Uhrzeit überprüfen (0000 - 2359) // Leerzeichen aus den Werten entfernen und Formatierung der Uhrzeit überprüfen (0000 - 2359)
for _, i := range newSettings[key].([]interface{}) { var newUpdateTimes []string
_, err := time.Parse("1504", i.(string)) for _, v := range value.([]interface{}) {
v = strings.Replace(v.(string), " ", "", -1)
_, err := time.Parse("1504", v.(string))
if err != nil { if err != nil {
ShowError(err, 1012) ShowError(err, 1012)
return Settings, err return Settings, err
} }
newUpdateTimes = append(newUpdateTimes, v.(string))
} }
value = newUpdateTimes
case "cache.images": case "cache.images":
cacheImages = true cacheImages = true
@@ -86,6 +96,17 @@ func updateServerSettings(request RequestStruct) (settings SettingsStrcut, err e
return return
} }
case "ffmpeg.path", "vlc.path":
var path = value.(string)
if len(path) > 0 {
err = checkFile(path)
if err != nil {
return
}
}
} }
oldSettings[key] = value oldSettings[key] = value
@@ -130,6 +151,33 @@ func updateServerSettings(request RequestStruct) (settings SettingsStrcut, err e
} }
// Buffer Einstellungen überprüfen
if len(Settings.FFmpegOptions) == 0 {
Settings.FFmpegOptions = System.FFmpeg.DefaultOptions
}
if len(Settings.VLCOptions) == 0 {
Settings.VLCOptions = System.VLC.DefaultOptions
}
switch Settings.Buffer {
case "ffmpeg":
if len(Settings.FFmpegPath) == 0 {
err = errors.New(getErrMsg(2020))
return
}
case "vlc":
if len(Settings.VLCPath) == 0 {
err = errors.New(getErrMsg(2021))
return
}
}
err = saveSettings(Settings) err = saveSettings(Settings)
if err == nil { if err == nil {
@@ -358,6 +406,7 @@ func saveFilter(request RequestStruct) (settings SettingsStrcut, err error) {
var filterMap = make(map[int64]interface{}) var filterMap = make(map[int64]interface{})
var newData = make(map[int64]interface{}) var newData = make(map[int64]interface{})
var defaultFilter FilterStruct var defaultFilter FilterStruct
var newFilter = false
defaultFilter.Active = true defaultFilter.Active = true
defaultFilter.CaseSensitive = false defaultFilter.CaseSensitive = false
@@ -381,6 +430,7 @@ func saveFilter(request RequestStruct) (settings SettingsStrcut, err error) {
if dataID == -1 { if dataID == -1 {
// Neuer Filter // Neuer Filter
newFilter = true
dataID = createNewID() dataID = createNewID()
filterMap[dataID] = jsonToMap(mapToJSON(defaultFilter)) filterMap[dataID] = jsonToMap(mapToJSON(defaultFilter))
@@ -389,15 +439,28 @@ func saveFilter(request RequestStruct) (settings SettingsStrcut, err error) {
// Filter aktualisieren / löschen // Filter aktualisieren / löschen
for key, value := range data.(map[string]interface{}) { for key, value := range data.(map[string]interface{}) {
var oldData = filterMap[dataID].(map[string]interface{})
oldData[key] = value
// Filter löschen // Filter löschen
if _, ok := data.(map[string]interface{})["delete"]; ok { if _, ok := data.(map[string]interface{})["delete"]; ok {
delete(filterMap, dataID) delete(filterMap, dataID)
break break
}
if filter, ok := data.(map[string]interface{})["filter"].(string); ok {
if len(filter) == 0 {
err = errors.New(getErrMsg(1014))
if newFilter == true {
delete(filterMap, dataID)
}
return
}
}
if oldData, ok := filterMap[dataID].(map[string]interface{}); ok {
oldData[key] = value
} }
} }
@@ -438,8 +501,60 @@ func saveXEpgMapping(request RequestStruct) (err error) {
Data.XEPG.Channels = request.EpgMapping Data.XEPG.Channels = request.EpgMapping
cleanupXEPG() if System.ScanInProgress == 0 {
buildXEPG(true)
System.ScanInProgress = 1
cleanupXEPG()
//buildXEPG(true)
go func() {
createXMLTVFile()
createM3UFile()
showInfo("XEPG:" + fmt.Sprintf("Ready to use"))
go cachingImages()
System.ScanInProgress = 0
}()
} else {
// Wenn während des erstellen der Datanbank das Mapping erneut gespeichert wird, wird die Datenbank erst später erneut aktualisiert.
go func() {
if System.BackgroundProcess == true {
return
}
System.BackgroundProcess = true
for {
time.Sleep(time.Duration(1) * time.Second)
if System.ScanInProgress == 0 {
break
}
}
System.ScanInProgress = 1
cleanupXEPG()
//buildXEPG(false)
createXMLTVFile()
createM3UFile()
showInfo("XEPG:" + fmt.Sprintf("Ready to use"))
go cachingImages()
System.ScanInProgress = 0
System.BackgroundProcess = false
}()
}
return return
} }
@@ -604,6 +719,7 @@ func saveWizard(request RequestStruct) (nextStep int, err error) {
} }
buildXEPG(false) buildXEPG(false)
System.ScanInProgress = 0
} }

View File

@@ -95,7 +95,7 @@ func getLineupStatus() (jsonContent []byte, err error) {
lineupStatus.ScanInProgress = System.ScanInProgress lineupStatus.ScanInProgress = System.ScanInProgress
lineupStatus.ScanPossible = 0 lineupStatus.ScanPossible = 0
lineupStatus.Source = "Cable" lineupStatus.Source = "Cable"
lineupStatus.SourceList = []string{"IPTV", "Cable"} lineupStatus.SourceList = []string{"Cable"}
jsonContent, err = json.MarshalIndent(lineupStatus, "", " ") jsonContent, err = json.MarshalIndent(lineupStatus, "", " ")

View File

@@ -6,24 +6,41 @@ import (
"io" "io"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"net/url"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
) )
func getCacheImageURL(url string) (cacheImageURL string) { func getCacheImageURL(imageURL string) (cacheImageURL string) {
url = strings.Trim(url, "\r\n") if Settings.CacheImages == false {
return imageURL
}
var urlMD5 = getMD5(url) imageURL = strings.Trim(imageURL, "\r\n")
var fileExtension = filepath.Ext(url)
p, err := url.Parse(imageURL)
if err != nil {
// URL konnte nicht geparst werden, die ursprüngliche image url wird zurückgegeben
showInfo(fmt.Sprintf("Image Caching:Image URL: %s", imageURL))
showWarning(4101)
return imageURL
}
var urlMD5 = getMD5(imageURL)
var fileExtension = filepath.Ext(p.Path)
if len(fileExtension) == 0 {
// Keine Dateierweiterung vorhanden, die ursprüngliche image url wird zurückgegeben
return imageURL
}
if indexOfString(urlMD5+fileExtension, Data.Cache.ImagesFiles) == -1 { if indexOfString(urlMD5+fileExtension, Data.Cache.ImagesFiles) == -1 {
Data.Cache.ImagesFiles = append(Data.Cache.ImagesFiles, urlMD5+fileExtension) Data.Cache.ImagesFiles = append(Data.Cache.ImagesFiles, urlMD5+fileExtension)
} }
if Settings.CacheImages == false || System.ImageCachingInProgress == 1 { if System.ImageCachingInProgress == 1 {
return url return imageURL
} }
if indexOfString(urlMD5+fileExtension, Data.Cache.ImagesCache) != -1 { if indexOfString(urlMD5+fileExtension, Data.Cache.ImagesCache) != -1 {
@@ -32,15 +49,15 @@ func getCacheImageURL(url string) (cacheImageURL string) {
} else { } else {
if strings.Contains(url, System.Domain+"/images/") == false { if strings.Contains(imageURL, System.Domain+"/images/") == false {
if indexOfString(url, Data.Cache.ImagesURLS) == -1 { if indexOfString(imageURL, Data.Cache.ImagesURLS) == -1 {
Data.Cache.ImagesURLS = append(Data.Cache.ImagesURLS, url) Data.Cache.ImagesURLS = append(Data.Cache.ImagesURLS, imageURL)
} }
} }
cacheImageURL = url cacheImageURL = imageURL
} }
@@ -57,10 +74,10 @@ func cachingImages() {
showInfo("Image Caching:Images are cached") showInfo("Image Caching:Images are cached")
for _, url := range Data.Cache.ImagesURLS { for _, imageURL := range Data.Cache.ImagesURLS {
if len(url) > 0 { if len(imageURL) > 0 {
cacheImage(url) cacheImage(imageURL)
} }
} }
@@ -94,16 +111,16 @@ func cachingImages() {
return return
} }
func cacheImage(url string) { func cacheImage(imageURL string) {
var debug string var debug string
var urlMD5 = getMD5(url) var urlMD5 = getMD5(imageURL)
var fileExtension = filepath.Ext(url) var fileExtension = filepath.Ext(imageURL)
debug = fmt.Sprintf("Image Caching:File: %s Download: %s", urlMD5+fileExtension, url) debug = fmt.Sprintf("Image Caching:File: %s Download: %s", urlMD5+fileExtension, imageURL)
showDebug(debug, 1) showDebug(debug, 1)
resp, err := http.Get(url) resp, err := http.Get(imageURL)
if err != nil { if err != nil {
return return
} }

99
src/info.go Normal file
View File

@@ -0,0 +1,99 @@
package src
import (
"fmt"
"strings"
)
// ShowSystemInfo : Systeminformationen anzeigen
func ShowSystemInfo() {
fmt.Print("Creating the information takes a moment...")
err := buildDatabaseDVR()
if err != nil {
ShowError(err, 0)
return
}
buildXEPG(false)
fmt.Println("OK")
println()
fmt.Println(fmt.Sprintf("Version: %s %s.%s", System.Name, System.Version, System.Build))
fmt.Println(fmt.Sprintf("Branch: %s", System.Branch))
fmt.Println(fmt.Sprintf("GitHub: %s/%s | Git update = %t", System.GitHub.User, System.GitHub.Repo, System.GitHub.Update))
fmt.Println(fmt.Sprintf("Folder (config): %s", System.Folder.Config))
fmt.Println(fmt.Sprintf("Streams: %d / %d", len(Data.Streams.Active), len(Data.Streams.All)))
fmt.Println(fmt.Sprintf("Filter: %d", len(Data.Filter)))
fmt.Println(fmt.Sprintf("XEPG Chanels: %d", int(Data.XEPG.XEPGCount)))
println()
fmt.Println(fmt.Sprintf("IPv4 Addresses:"))
for i, ipv4 := range System.IPAddressesV4 {
switch count := i; {
case count < 10:
fmt.Println(fmt.Sprintf(" %d. %s", count, ipv4))
break
case count < 100:
fmt.Println(fmt.Sprintf(" %d. %s", count, ipv4))
break
}
}
println()
fmt.Println(fmt.Sprintf("IPv6 Addresses:"))
for i, ipv4 := range System.IPAddressesV6 {
switch count := i; {
case count < 10:
fmt.Println(fmt.Sprintf(" %d. %s", count, ipv4))
break
case count < 100:
fmt.Println(fmt.Sprintf(" %d. %s", count, ipv4))
break
}
}
println("---")
fmt.Println("Settings [General]")
fmt.Println(fmt.Sprintf("xTeVe Update: %t", Settings.XteveAutoUpdate))
fmt.Println(fmt.Sprintf("UUID: %s", Settings.UUID))
fmt.Println(fmt.Sprintf("Tuner (Plex / Emby): %d", Settings.Tuner))
fmt.Println(fmt.Sprintf("EPG Source: %s", Settings.EpgSource))
println("---")
fmt.Println("Settings [Files]")
fmt.Println(fmt.Sprintf("Schedule: %s", strings.Join(Settings.Update, ",")))
fmt.Println(fmt.Sprintf("Files Update: %t", Settings.FilesUpdate))
fmt.Println(fmt.Sprintf("Folder (tmp): %s", Settings.TempPath))
fmt.Println(fmt.Sprintf("Image Chaching: %t", Settings.CacheImages))
fmt.Println(fmt.Sprintf("Replace EPG Image: %t", Settings.XepgReplaceMissingImages))
println("---")
fmt.Println("Settings [Streaming]")
fmt.Println(fmt.Sprintf("Buffer: %s", Settings.Buffer))
fmt.Println(fmt.Sprintf("Buffer Size: %d KB", Settings.BufferSize))
fmt.Println(fmt.Sprintf("Timeout: %d ms", int(Settings.BufferTimeout)))
fmt.Println(fmt.Sprintf("User Agent: %s", Settings.UserAgent))
println("---")
fmt.Println("Settings [Backup]")
fmt.Println(fmt.Sprintf("Folder (backup): %s", Settings.BackupPath))
fmt.Println(fmt.Sprintf("Backup Keep: %d", Settings.BackupKeep))
}

View File

@@ -2,6 +2,8 @@ package m3u
import ( import (
"errors" "errors"
"fmt"
"log"
"net/url" "net/url"
"regexp" "regexp"
"strings" "strings"
@@ -12,6 +14,7 @@ func MakeInterfaceFromM3U(byteStream []byte) (allChannels []interface{}, err err
var content = string(byteStream) var content = string(byteStream)
var channelName string var channelName string
var uuids []string
var parseMetaData = func(channel string) (stream map[string]string) { var parseMetaData = func(channel string) (stream map[string]string) {
@@ -53,7 +56,7 @@ func MakeInterfaceFromM3U(byteStream []byte) (allChannels []interface{}, err err
line = strings.Replace(line, p, "", 1) line = strings.Replace(line, p, "", 1)
p = strings.Replace(p, `"`, "", -1) p = strings.Replace(p, `"`, "", -1)
var parameter = strings.Split(p, "=") var parameter = strings.SplitN(p, "=", 2)
if len(parameter) == 2 { if len(parameter) == 2 {
@@ -120,9 +123,15 @@ func MakeInterfaceFromM3U(byteStream []byte) (allChannels []interface{}, err err
if strings.Contains(strings.ToLower(key), "id") { if strings.Contains(strings.ToLower(key), "id") {
if indexOfString(value, uuids) != -1 {
log.Println(fmt.Sprintf("Channel: %s - %s = %s ", stream["name"], key, value))
break
}
uuids = append(uuids, value)
stream["_uuid.key"] = key stream["_uuid.key"] = key
stream["_uuid.value"] = value stream["_uuid.value"] = value
//os.Exit(0)
break break
} }
@@ -161,107 +170,13 @@ func MakeInterfaceFromM3U(byteStream []byte) (allChannels []interface{}, err err
return return
} }
// MakeInterfaceFromM3U2 : func indexOfString(element string, data []string) int {
func MakeInterfaceFromM3U2(byteStream []byte) (allChannels []interface{}, err error) {
var content = string(byteStream)
//var allChannels = make([]interface{}, 0)
var channels = strings.Split(content, "#EXTINF") for k, v := range data {
if element == v {
var parseMetaData = func(metaData string) map[string]string { return k
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") { return -1
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
} }

View File

@@ -43,6 +43,10 @@ func filterThisStream(s interface{}) (status bool) {
for _, filter := range Data.Filter { for _, filter := range Data.Filter {
if filter.Rule == "" {
continue
}
var group, name, search string var group, name, search string
var exclude, include string var exclude, include string
var match = false var match = false

View File

@@ -12,6 +12,10 @@ import (
func showInfo(str string) { func showInfo(str string) {
if System.Flag.Info == true {
return
}
var max = 22 var max = 22
var msg = strings.SplitN(str, ":", 2) var msg = strings.SplitN(str, ":", 2)
var length = len(msg[0]) var length = len(msg[0])
@@ -240,7 +244,9 @@ func getErrMsg(errCode int) (errMsg string) {
case 1012: case 1012:
errMsg = fmt.Sprintf("Invalid formatting of the time") errMsg = fmt.Sprintf("Invalid formatting of the time")
case 1013: case 1013:
errMsg = fmt.Sprintf("Invalid settings file (%s), file must be at least version %s", System.File.Settings, System.Compatibility) errMsg = fmt.Sprintf("Invalid settings file (settings.json), file must be at least version %s", System.Compatibility)
case 1014:
errMsg = fmt.Sprintf("Invalid filter rule")
case 1020: case 1020:
errMsg = fmt.Sprintf("Data could not be saved, invalid keyword") errMsg = fmt.Sprintf("Data could not be saved, invalid keyword")
@@ -248,12 +254,13 @@ func getErrMsg(errCode int) (errMsg string) {
// Datenbank Update // Datenbank Update
case 1030: case 1030:
errMsg = fmt.Sprintf("Invalid settings file (%s)", System.File.Settings) errMsg = fmt.Sprintf("Invalid settings file (%s)", System.File.Settings)
case 1031:
errMsg = fmt.Sprintf("Database error. The database version of your settings is not compatible with this version.")
// M3U Parser // M3U Parser
case 1050: case 1050:
errMsg = fmt.Sprintf("Invalid duration specification in the M3U8 playlist.") errMsg = fmt.Sprintf("Invalid duration specification in the M3U8 playlist.")
// M3U Parser
case 1060: case 1060:
errMsg = fmt.Sprintf("Invalid characters found in the tvg parameters, streams with invalid parameters were skipped.") errMsg = fmt.Sprintf("Invalid characters found in the tvg parameters, streams with invalid parameters were skipped.")
@@ -262,6 +269,8 @@ func getErrMsg(errCode int) (errMsg string) {
errMsg = fmt.Sprintf("Folder could not be created.") errMsg = fmt.Sprintf("Folder could not be created.")
case 1071: case 1071:
errMsg = fmt.Sprintf("File could not be created") errMsg = fmt.Sprintf("File could not be created")
case 1072:
errMsg = fmt.Sprintf("File not found")
// Backup // Backup
case 1090: case 1090:
@@ -286,6 +295,8 @@ func getErrMsg(errCode int) (errMsg string) {
errMsg = fmt.Sprintf("Steaming URL could not be found in any playlist") errMsg = fmt.Sprintf("Steaming URL could not be found in any playlist")
case 1203: case 1203:
errMsg = fmt.Sprintf("Steaming URL could not be found in any playlist") errMsg = fmt.Sprintf("Steaming URL could not be found in any playlist")
case 1204:
errMsg = fmt.Sprintf("Streaming was stopped by third party transcoder (FFmpeg / VLC)")
// Warnings // Warnings
case 2000: case 2000:
@@ -302,6 +313,10 @@ func getErrMsg(errCode int) (errMsg string) {
errMsg = fmt.Sprintf("There are no channels mapped, use the mapping menu to assign EPG data to the channels.") errMsg = fmt.Sprintf("There are no channels mapped, use the mapping menu to assign EPG data to the channels.")
case 2010: case 2010:
errMsg = fmt.Sprintf("No valid streaming URL") errMsg = fmt.Sprintf("No valid streaming URL")
case 2020:
errMsg = fmt.Sprintf("FFmpeg binary was not found. Check the FFmpeg binary path in the xTeVe settings.")
case 2021:
errMsg = fmt.Sprintf("VLC binary was not found. Check the VLC path binary in the xTeVe settings.")
case 2099: case 2099:
errMsg = fmt.Sprintf("Updates have been disabled by the developer") errMsg = fmt.Sprintf("Updates have been disabled by the developer")
@@ -341,6 +356,8 @@ func getErrMsg(errCode int) (errMsg string) {
errMsg = fmt.Sprintf("This error message comes from the provider") errMsg = fmt.Sprintf("This error message comes from the provider")
case 4005: case 4005:
errMsg = fmt.Sprintf("Temporary buffer files could not be deleted") errMsg = fmt.Sprintf("Temporary buffer files could not be deleted")
case 4006:
errMsg = fmt.Sprintf("Server connection timeout")
// Buffer (M3U8) // Buffer (M3U8)
case 4050: case 4050:
@@ -351,6 +368,8 @@ func getErrMsg(errCode int) (errMsg string) {
// Caching // Caching
case 4100: case 4100:
errMsg = fmt.Sprintf("Unknown content type for downloaded image") errMsg = fmt.Sprintf("Unknown content type for downloaded image")
case 4101:
errMsg = fmt.Sprintf("Invalid URL, original URL is used for this image")
// API // API
case 5000: case 5000:
@@ -362,7 +381,7 @@ func getErrMsg(errCode int) (errMsg string) {
case 6002: case 6002:
errMsg = fmt.Sprintf("Update failed") errMsg = fmt.Sprintf("Update failed")
case 6003: case 6003:
errMsg = fmt.Sprintf("Server not available") errMsg = fmt.Sprintf("Update server not available")
case 6004: case 6004:
errMsg = fmt.Sprintf("xTeVe update available") errMsg = fmt.Sprintf("xTeVe update available")

View File

@@ -11,24 +11,26 @@ import (
) )
// SSDP : SSPD / DLNA Server // SSDP : SSPD / DLNA Server
func SSDP() { func SSDP() (err error) {
showInfo(fmt.Sprintf("SSDP / DLNA:%t", Settings.SSDP)) if Settings.SSDP == false || System.Flag.Info == true {
if Settings.SSDP == false {
return return
} }
time.Sleep(10 * time.Second) showInfo(fmt.Sprintf("SSDP / DLNA:%t", Settings.SSDP))
quit := make(chan os.Signal, 1)
signal.Notify(quit, os.Interrupt)
ad, err := ssdp.Advertise( ad, err := ssdp.Advertise(
"upnp:"+System.AppName, // send as "ST" fmt.Sprintf("upnp:rootdevice"), // send as "ST"
System.DeviceID+"::upnp:"+System.AppName, // send as "USN" fmt.Sprintf("uuid:%s::upnp:rootdevice", System.DeviceID), // send as "USN"
System.URLBase+"/device.xml", // send as "LOCATION" fmt.Sprintf("%s/device.xml", System.URLBase), // send as "LOCATION"
System.AppName, // send as "SERVER" System.AppName, // send as "SERVER"
1800) // send as "maxAge" in "CACHE-CONTROL" 1800) // send as "maxAge" in "CACHE-CONTROL"
if err != nil { if err != nil {
ShowError(err, 000) return
} }
// Debug SSDP // Debug SSDP
@@ -36,34 +38,35 @@ func SSDP() {
ssdp.Logger = log.New(os.Stderr, "[SSDP] ", log.LstdFlags) ssdp.Logger = log.New(os.Stderr, "[SSDP] ", log.LstdFlags)
} }
var aliveTick <-chan time.Time go func(adv *ssdp.Advertiser) {
var ai = 10
if ai > 0 { aliveTick := time.Tick(300 * time.Second)
aliveTick = time.Tick(time.Duration(ai) * time.Second)
} else {
aliveTick = make(chan time.Time)
}
quit := make(chan os.Signal, 1) loop:
signal.Notify(quit, os.Interrupt) for {
loop: select {
for { case <-aliveTick:
err = adv.Alive()
if err != nil {
ShowError(err, 0)
adv.Bye()
adv.Close()
break loop
}
select { case <-quit:
adv.Bye()
adv.Close()
os.Exit(0)
break loop
case <-aliveTick: }
ad.Alive()
case <-quit:
os.Exit(0)
break loop
} }
} }(ad)
ad.Bye() return
ad.Close()
} }

View File

@@ -68,12 +68,13 @@ type ThisStream struct {
// Segment : URL Segmente (HLS / M3U8) // Segment : URL Segmente (HLS / M3U8)
type Segment struct { type Segment struct {
Duration float64 Duration float64
Info bool Info bool
Sequence int64 PlaylistType string
URL string Sequence int64
Version int URL string
Wait float64 Version int
Wait float64
StreamInf struct { StreamInf struct {
AverageBandwidth int AverageBandwidth int
@@ -107,3 +108,147 @@ type BandwidthCalculation struct {
Stop time.Time Stop time.Time
TimeDiff float64 TimeDiff float64
} }
/*
var args = "-hide_banner -loglevel panic -re -i " + url + " -codec copy -f mpegts pipe:1"
//var args = "-re -i " + url + " -codec copy -f mpegts pipe:1"
cmd := exec.Command("/usr/local/bin/ffmpeg", strings.Split(args, " ")...)
//run := exec.Command("/usr/local/bin/ffmpeg", "-hide_banner", "-loglevel", "panic", "-re", "-i", url, "-codec", "copy", "-f", "mpegts", "pipe:1")
//run := exec.Command("/usr/local/bin/ffmpeg", "-re", "-i", url, "-codec", "copy", "-f", "mpegts", "pipe:1")
stderr, _ := cmd.StderrPipe()
cmd.Start()
scanner := bufio.NewScanner(stderr)
scanner.Split(bufio.ScanLines)
for scanner.Scan() {
m := scanner.Text()
fmt.Println(m)
}
cmd.Wait()
os.Exit(0)
*/
/*
ffmpegOut, _ := run.StderrPipe()
//run.Start()
scanner = bufio.NewScanner(ffmpegOut)
scanner.Split(bufio.ScanLines)
for scanner.Scan() {
m := scanner.Text()
fmt.Println(m)
}
ffmpegOut, err = run.StdoutPipe()
if err != nil {
ShowError(err, 0)
return
}
stderr, stderrErr := run.StderrPipe()
if stderrErr != nil {
fmt.Println(stderrErr)
}
_ = stderr
if startErr := run.Start(); startErr != nil {
fmt.Println(startErr)
return
}
n, err := ffmpegOut.Read(buffer)
_ = n
_ = stream
_ = fileSize
if err != nil && err != io.EOF {
ShowError(err, 0)
addErrorToStream(err)
return
}
defer bufferFile.Close()
scanner = bufio.NewScanner(ffmpegOut)
for scanner.Scan() {
//fmt.Printf("%s\n", scanner.Text())
//fmt.Println(scanner)
thisLine := scanner.Text()
line := make([]byte, len(thisLine))
buffer = append(buffer, line...)
fmt.Println(len(buffer))
if len(buffer) > tmpFileSize {
if _, err := bufferFile.Write(buffer[:]); err != nil {
ShowError(err, 0)
addErrorToStream(err)
run.Process.Kill()
return
}
buffer = make([]byte, 1024*Settings.BufferSize*2)
debug = fmt.Sprintf("Buffer Status:Done (%s)", tmpFile)
showDebug(debug, 2)
bufferFile.Close()
stream.Status = true
playlist.Streams[streamID] = stream
BufferInformation.Store(playlistID, playlist)
tmpSegment++
tmpFile = fmt.Sprintf("%s%d.ts", tmpFolder, tmpSegment)
if clientConnection(stream) == false {
bufferFile.Close()
run.Process.Kill()
err = os.RemoveAll(stream.Folder)
if err != nil {
ShowError(err, 4005)
}
return
}
bufferFile, err = os.Create(tmpFile)
if err != nil {
addErrorToStream(err)
run.Process.Kill()
return
}
fileSize = 0
if n == 0 {
bufferFile.Close()
run.Process.Kill()
break
}
os.Exit(0)
}
}
*/

View File

@@ -11,15 +11,27 @@ type SystemStruct struct {
APIVersion string APIVersion string
AppName string AppName string
ARCH string ARCH string
BackgroundProcess bool
Branch string Branch string
Build string Build string
Compatibility string Compatibility string
ConfigurationWizard bool ConfigurationWizard bool
DBVersion string
Dev bool Dev bool
DeviceID string DeviceID string
Domain string Domain string
DVRLimit int DVRLimit int
FFmpeg struct {
DefaultOptions string
Path string
}
VLC struct {
DefaultOptions string
Path string
}
File struct { File struct {
Authentication string Authentication string
M3U string M3U string
@@ -31,10 +43,12 @@ type SystemStruct struct {
} }
Flag struct { Flag struct {
Branch string Branch string
Debug int Debug int
Port string Info bool
SSDP bool Port string
Restore string
SSDP bool
} }
Folder struct { Folder struct {
@@ -238,11 +252,15 @@ type SettingsStrcut struct {
BackupKeep int `json:"backup.keep"` BackupKeep int `json:"backup.keep"`
BackupPath string `json:"backup.path"` BackupPath string `json:"backup.path"`
Branch string `json:"git.branch,omitempty"` Branch string `json:"git.branch,omitempty"`
Buffer bool `json:"buffer"` Buffer string `json:"buffer"`
BufferSize int `json:"buffer.size.kb"` BufferSize int `json:"buffer.size.kb"`
BufferTimeout float64 `json:"buffer.timeout"` BufferTimeout float64 `json:"buffer.timeout"`
CacheImages bool `json:"cache.images"` CacheImages bool `json:"cache.images"`
EpgSource string `json:"epgSource"` EpgSource string `json:"epgSource"`
FFmpegOptions string `json:"ffmpeg.options"`
FFmpegPath string `json:"ffmpeg.path"`
VLCOptions string `json:"vlc.options"`
VLCPath string `json:"vlc.path"`
FileM3U []string `json:"file,omitempty"` // Beim Wizard wird die M3U in ein Slice gespeichert 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) FileXMLTV []string `json:"xmltv,omitempty"` // Altes Speichersystem der Provider XML Datei Slice (Wird für die Umwandlung auf das neue benötigt)

View File

@@ -25,11 +25,15 @@ type RequestStruct struct {
AuthenticationXML *bool `json:"authentication.xml,omitempty"` AuthenticationXML *bool `json:"authentication.xml,omitempty"`
BackupKeep *int `json:"backup.keep,omitempty"` BackupKeep *int `json:"backup.keep,omitempty"`
BackupPath *string `json:"backup.path,omitempty"` BackupPath *string `json:"backup.path,omitempty"`
Buffer *bool `json:"buffer,omitempty"` Buffer *string `json:"buffer,omitempty"`
BufferSize *int `json:"buffer.size.kb, omitempty"` BufferSize *int `json:"buffer.size.kb, omitempty"`
BufferTimeout *float64 `json:"buffer.timeout,omitempty"` BufferTimeout *float64 `json:"buffer.timeout,omitempty"`
CacheImages *bool `json:"cache.images,omitempty"` CacheImages *bool `json:"cache.images,omitempty"`
EpgSource *string `json:"epgSource,omitempty"` EpgSource *string `json:"epgSource,omitempty"`
FFmpegOptions *string `json:"ffmpeg.options,omitempty"`
FFmpegPath *string `json:"ffmpeg.path,omitempty"`
VLCOptions *string `json:"vlc.options,omitempty"`
VLCPath *string `json:"vlc.path,omitempty"`
FilesUpdate *bool `json:"files.update,omitempty"` FilesUpdate *bool `json:"files.update,omitempty"`
TempPath *string `json:"temp.path,omitempty"` TempPath *string `json:"temp.path,omitempty"`
Tuner *int `json:"tuner,omitempty"` Tuner *int `json:"tuner,omitempty"`

View File

@@ -113,11 +113,13 @@ func loadSettings() (settings SettingsStrcut, err error) {
defaults["authentication.xml"] = false defaults["authentication.xml"] = false
defaults["backup.keep"] = 10 defaults["backup.keep"] = 10
defaults["backup.path"] = System.Folder.Backup defaults["backup.path"] = System.Folder.Backup
defaults["buffer"] = false defaults["buffer"] = "-"
defaults["buffer.size.kb"] = 1024 defaults["buffer.size.kb"] = 1024
defaults["buffer.timeout"] = 500 defaults["buffer.timeout"] = 500
defaults["cache.images"] = false defaults["cache.images"] = false
defaults["epgSource"] = "XEPG" defaults["epgSource"] = "PMS"
defaults["ffmpeg.options"] = System.FFmpeg.DefaultOptions
defaults["vlc.options"] = System.VLC.DefaultOptions
defaults["files"] = dataMap defaults["files"] = dataMap
defaults["files.update"] = true defaults["files.update"] = true
defaults["filter"] = make(map[string]interface{}) defaults["filter"] = make(map[string]interface{})
@@ -133,7 +135,7 @@ func loadSettings() (settings SettingsStrcut, err error) {
defaults["update"] = []string{"0000"} defaults["update"] = []string{"0000"}
defaults["user.agent"] = System.Name defaults["user.agent"] = System.Name
defaults["uuid"] = createUUID() defaults["uuid"] = createUUID()
defaults["version"] = System.Version defaults["version"] = System.DBVersion
defaults["xteveAutoUpdate"] = true defaults["xteveAutoUpdate"] = true
defaults["temp.path"] = System.Folder.Temp defaults["temp.path"] = System.Folder.Temp
@@ -159,8 +161,27 @@ func loadSettings() (settings SettingsStrcut, err error) {
showInfo(fmt.Sprintf("Git Branch:Switching Git Branch to -> %s", settings.Branch)) showInfo(fmt.Sprintf("Git Branch:Switching Git Branch to -> %s", settings.Branch))
} }
if len(settings.FFmpegPath) == 0 {
settings.FFmpegPath = searchFileInOS("ffmpeg")
}
if len(settings.VLCPath) == 0 {
settings.VLCPath = searchFileInOS("cvlc")
}
settings.Version = System.DBVersion
err = saveSettings(settings) err = saveSettings(settings)
// Warung wenn FFmpeg nicht gefunden wurde
if len(Settings.FFmpegPath) == 0 && Settings.Buffer == "ffmpeg" {
showWarning(2020)
}
if len(Settings.VLCPath) == 0 && Settings.Buffer == "vlc" {
showWarning(2021)
}
return return
} }

View File

@@ -10,8 +10,11 @@ import (
"io/ioutil" "io/ioutil"
"net" "net"
"os" "os"
"os/exec"
"os/user" "os/user"
"path/filepath" "path/filepath"
"runtime"
"strings"
"text/template" "text/template"
) )
@@ -42,13 +45,25 @@ func checkFolder(path string) (err error) {
return nil return nil
} }
// Prüft ob die datei im Dateisystem existiert // Prüft ob die Datei im Dateisystem existiert
func checkFile(filename string) (err error) { func checkFile(filename string) (err error) {
var file = getPlatformFile(filename) var file = getPlatformFile(filename)
if _, err = os.Stat(file); os.IsNotExist(err) { if _, err = os.Stat(file); os.IsNotExist(err) {
return return err
}
fi, err := os.Stat(file)
if err != nil {
return err
}
switch mode := fi.Mode(); {
case mode.IsDir():
err = fmt.Errorf("%s: %s", file, getErrMsg(1072))
case mode.IsRegular():
break
} }
return return
@@ -77,6 +92,7 @@ func GetUserHomeDirectory() (userHomeDirectory string) {
return return
} }
// Prüft Dateiberechtigung
func checkFilePermission(dir string) (err error) { func checkFilePermission(dir string) (err error) {
var filename = dir + "permission.test" var filename = dir + "permission.test"
@@ -115,6 +131,34 @@ func removeOldSystemData() {
os.RemoveAll(System.Folder.Temp) os.RemoveAll(System.Folder.Temp)
} }
// Sucht eine Datei im OS
func searchFileInOS(file string) (path string) {
switch runtime.GOOS {
case "linux", "darwin", "freebsd":
var args = file
var cmd = exec.Command("which", strings.Split(args, " ")...)
out, err := cmd.CombinedOutput()
if err == nil {
var slice = strings.Split(strings.Replace(string(out), "\r\n", "\n", -1), "\n")
if len(slice) > 0 {
path = strings.Trim(slice[0], "\r\n")
}
}
default:
return
}
return
}
// //
func removeChildItems(dir string) error { func removeChildItems(dir string) error {
@@ -276,6 +320,22 @@ func resolveHostIP() (err error) {
} }
if len(System.IPAddress) == 0 {
switch len(System.IPAddressesV4) {
case 0:
if len(System.IPAddressesV6) > 0 {
System.IPAddress = System.IPAddressesV6[0]
}
default:
System.IPAddress = System.IPAddressesV4[0]
}
}
System.Hostname, err = os.Hostname() System.Hostname, err = os.Hostname()
if err != nil { if err != nil {
return return

View File

@@ -41,8 +41,8 @@ func BinaryUpdate() (err error) {
resp, err := http.Get(gitInfo) resp, err := http.Get(gitInfo)
if err != nil { if err != nil {
ShowError(err, 0) ShowError(err, 6003)
return err return nil
} }
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
@@ -169,6 +169,13 @@ checkVersion:
if settingsVersion, ok := settingsMap["version"].(string); ok { if settingsVersion, ok := settingsMap["version"].(string); ok {
if settingsVersion > System.DBVersion {
showInfo("Settings DB Version:" + settingsVersion)
showInfo("System DB Version:" + System.DBVersion)
err = errors.New(getErrMsg(1031))
return
}
// Letzte Kompatible Version (1.4.4) // Letzte Kompatible Version (1.4.4)
if settingsVersion < System.Compatibility { if settingsVersion < System.Compatibility {
err = errors.New(getErrMsg(1013)) err = errors.New(getErrMsg(1013))
@@ -189,7 +196,7 @@ checkVersion:
var newFilterMap = convertToNewFilter(oldFilter) var newFilterMap = convertToNewFilter(oldFilter)
settingsMap["filter"] = newFilterMap settingsMap["filter"] = newFilterMap
settingsMap["version"] = "1.9.0" settingsMap["version"] = "2.0.0"
err = saveMapToJSONFile(System.File.Settings, settingsMap) err = saveMapToJSONFile(System.File.Settings, settingsMap)
if err != nil { if err != nil {
@@ -203,11 +210,38 @@ checkVersion:
return return
} }
case "1.9.0": case "2.0.0":
if oldBuffer, ok := settingsMap["buffer"].(bool); ok {
var newBuffer string
switch oldBuffer {
case true:
newBuffer = "xteve"
case false:
newBuffer = "-"
}
settingsMap["buffer"] = newBuffer
settingsMap["version"] = "2.1.0"
err = saveMapToJSONFile(System.File.Settings, settingsMap)
if err != nil {
return
}
goto checkVersion
} else {
err = errors.New(getErrMsg(1030))
return
}
case "2.1.0":
// Falls es in einem späteren Update Änderungen an der Datenbank gibt, geht es hier weiter // Falls es in einem späteren Update Änderungen an der Datenbank gibt, geht es hier weiter
break break
} }
} else { } else {

File diff suppressed because one or more lines are too long

View File

@@ -34,7 +34,19 @@ func StartWebserver() (err error) {
showInfo("DVR IP:" + System.IPAddress + ":" + Settings.Port) showInfo("DVR IP:" + System.IPAddress + ":" + Settings.Port)
showHighlight(fmt.Sprintf("Web Interface:%s://%s:%s/web/", System.ServerProtocol.WEB, System.IPAddress, Settings.Port)) var ips = len(System.IPAddressesV4) + len(System.IPAddressesV6) - 1
switch ips {
case 0:
showHighlight(fmt.Sprintf("Web Interface:%s://%s:%s/web/", System.ServerProtocol.WEB, System.IPAddress, Settings.Port))
case 1:
showHighlight(fmt.Sprintf("Web Interface:%s://%s:%s/web/ | xTeVe is also available via the other %d IP.", System.ServerProtocol.WEB, System.IPAddress, Settings.Port, ips))
default:
showHighlight(fmt.Sprintf("Web Interface:%s://%s:%s/web/ | xTeVe is also available via the other %d IP's.", System.ServerProtocol.WEB, System.IPAddress, Settings.Port, len(System.IPAddressesV4)+len(System.IPAddressesV6)-1))
}
if err = http.ListenAndServe(":"+port, nil); err != nil { if err = http.ListenAndServe(":"+port, nil); err != nil {
ShowError(err, 1001) ShowError(err, 1001)
@@ -117,20 +129,31 @@ func Stream(w http.ResponseWriter, r *http.Request) {
return return
} }
if strings.Index(streamInfo.URL, "rtsp://") != -1 || strings.Index(streamInfo.URL, "rtp://") != -1 { switch Settings.Buffer {
err = errors.New("RTSP and RTP streams are not supported")
ShowError(err, 2004)
showInfo("Streaming URL:" + streamInfo.URL) case "-":
http.Redirect(w, r, streamInfo.URL, 302) showInfo(fmt.Sprintf("Buffer:false", Settings.Buffer))
case "xteve":
if strings.Index(streamInfo.URL, "rtsp://") != -1 || strings.Index(streamInfo.URL, "rtp://") != -1 {
err = errors.New("RTSP and RTP streams are not supported")
ShowError(err, 2004)
showInfo("Streaming URL:" + streamInfo.URL)
http.Redirect(w, r, streamInfo.URL, 302)
showInfo("Streaming Info:URL was passed to the client")
return
}
showInfo(fmt.Sprintf("Buffer:true [%s]", Settings.Buffer))
default:
showInfo(fmt.Sprintf("Buffer:true [%s]", Settings.Buffer))
showInfo("Streaming Info:URL was passed to the client")
return
} }
showInfo(fmt.Sprintf("Buffer:%t", Settings.Buffer)) if Settings.Buffer != "-" {
if Settings.Buffer == true {
showInfo(fmt.Sprintf("Buffer Size:%d KB", Settings.BufferSize)) showInfo(fmt.Sprintf("Buffer Size:%d KB", Settings.BufferSize))
} }
@@ -140,14 +163,15 @@ func Stream(w http.ResponseWriter, r *http.Request) {
// Prüfen ob der Buffer verwendet werden soll // Prüfen ob der Buffer verwendet werden soll
switch Settings.Buffer { switch Settings.Buffer {
case true: case "-":
bufferingStream(streamInfo.PlaylistID, streamInfo.URL, streamInfo.Name, w, r)
case false:
showInfo("Streaming URL:" + streamInfo.URL) showInfo("Streaming URL:" + streamInfo.URL)
http.Redirect(w, r, streamInfo.URL, 302) http.Redirect(w, r, streamInfo.URL, 302)
showInfo("Streaming Info:URL was passed to the client") showInfo("Streaming Info:URL was passed to the client.")
showInfo("Streaming Info:xTeVe is no longer involved, the client connects directly to the streaming server.")
default:
bufferingStream(streamInfo.PlaylistID, streamInfo.URL, streamInfo.Name, w, r)
} }
@@ -192,6 +216,7 @@ func xTeVe(w http.ResponseWriter, r *http.Request) {
// XMLTV Datei // XMLTV Datei
if strings.Contains(path, "xmltv/") { if strings.Contains(path, "xmltv/") {
w.Header().Set("Content-Type", "application/xml")
requestType = "xml" requestType = "xml"
file = System.Folder.Data + getFilenameFromPath(path) file = System.Folder.Data + getFilenameFromPath(path)
@@ -302,6 +327,8 @@ func WS(w http.ResponseWriter, r *http.Request) {
http.Error(w, "Could not open websocket connection", http.StatusBadRequest) http.Error(w, "Could not open websocket connection", http.StatusBadRequest)
} }
setGlobalDomain(r.Host)
for { for {
err = conn.ReadJSON(&request) err = conn.ReadJSON(&request)
@@ -447,7 +474,7 @@ func WS(w http.ResponseWriter, r *http.Request) {
file, errNew := xteveBackup() file, errNew := xteveBackup()
err = errNew err = errNew
if err == nil { if err == nil {
response.OpenLink = System.URLBase + "/download/" + file response.OpenLink = fmt.Sprintf("%s://%s/download/%s", System.ServerProtocol.WEB, System.Domain, file)
} }
case "xteveRestore": case "xteveRestore":
@@ -457,9 +484,10 @@ func WS(w http.ResponseWriter, r *http.Request) {
if len(request.Base64) > 0 { if len(request.Base64) > 0 {
newWebURL, err := xteveRestore(request.Base64) newWebURL, err := xteveRestoreFromWeb(request.Base64)
if err != nil { if err != nil {
ShowError(err, 000) ShowError(err, 000)
response.Alert = err.Error()
} }
if err == nil { if err == nil {
@@ -528,7 +556,6 @@ func WS(w http.ResponseWriter, r *http.Request) {
response.Settings = Settings response.Settings = Settings
} }
setGlobalDomain(r.Host)
response = setDefaultResponseData(response, true) response = setDefaultResponseData(response, true)
if System.ConfigurationWizard == true { if System.ConfigurationWizard == true {
response.ConfigurationWizard = System.ConfigurationWizard response.ConfigurationWizard = System.ConfigurationWizard
@@ -582,8 +609,12 @@ func Web(w http.ResponseWriter, r *http.Request) {
if getFilenameFromPath(requestFile) == "html" { if getFilenameFromPath(requestFile) == "html" {
if len(Data.Streams.All) == 0 && System.ScanInProgress == 0 { if System.ScanInProgress == 0 {
System.ConfigurationWizard = true
if len(Settings.Files.M3U) == 0 && len(Settings.Files.HDHR) == 0 {
System.ConfigurationWizard = true
}
} }
switch System.ConfigurationWizard { switch System.ConfigurationWizard {
@@ -867,8 +898,6 @@ func API(w http.ResponseWriter, r *http.Request) {
case "status": case "status":
fmt.Println("-----------------------------")
os.Exit(0)
response.VersionXteve = System.Version response.VersionXteve = System.Version
response.VersionAPI = System.APIVersion response.VersionAPI = System.APIVersion
response.StreamsActive = int64(len(Data.Streams.Active)) response.StreamsActive = int64(len(Data.Streams.Active))

View File

@@ -174,49 +174,58 @@ func createXEPGMapping() {
return return
} }
if len(Data.XMLTV.Files) == 0 { if len(Data.XMLTV.Files) > 0 {
return
}
for i := len(Data.XMLTV.Files) - 1; i >= 0; i-- { for i := len(Data.XMLTV.Files) - 1; i >= 0; i-- {
var file = Data.XMLTV.Files[i] var file = Data.XMLTV.Files[i]
var err error var err error
var fileID = strings.TrimSuffix(getFilenameFromPath(file), path.Ext(getFilenameFromPath(file))) var fileID = strings.TrimSuffix(getFilenameFromPath(file), path.Ext(getFilenameFromPath(file)))
showInfo("XEPG:" + "Parse XMLTV file: " + getProviderParameter(fileID, "xmltv", "name")) showInfo("XEPG:" + "Parse XMLTV file: " + getProviderParameter(fileID, "xmltv", "name"))
//xmltv, err = getLocalXMLTV(file) //xmltv, err = getLocalXMLTV(file)
var xmltv XMLTV var xmltv XMLTV
err = getLocalXMLTV(file, &xmltv) err = getLocalXMLTV(file, &xmltv)
if err != nil { if err != nil {
Data.XMLTV.Files = append(Data.XMLTV.Files, Data.XMLTV.Files[i+1:]...) Data.XMLTV.Files = append(Data.XMLTV.Files, Data.XMLTV.Files[i+1:]...)
var errMsg = err.Error() var errMsg = err.Error()
err = errors.New(getProviderParameter(fileID, "xmltv", "name") + ": " + errMsg) err = errors.New(getProviderParameter(fileID, "xmltv", "name") + ": " + errMsg)
ShowError(err, 000) ShowError(err, 000)
} }
// XML Parsen (Provider Datei) // XML Parsen (Provider Datei)
if err == nil { if err == nil {
// Daten aus der XML Datei in eine temporäre Map schreiben // Daten aus der XML Datei in eine temporäre Map schreiben
var xmltvMap = make(map[string]interface{}) var xmltvMap = make(map[string]interface{})
for _, c := range xmltv.Channel { for _, c := range xmltv.Channel {
var channel = make(map[string]interface{}) var channel = make(map[string]interface{})
channel["id"] = c.ID channel["id"] = c.ID
channel["display-name"] = friendlyDisplayName(*c) channel["display-name"] = friendlyDisplayName(*c)
channel["icon"] = c.Icon.Src channel["icon"] = c.Icon.Src
xmltvMap[c.ID] = channel xmltvMap[c.ID] = channel
}
tmpMap[getFilenameFromPath(file)] = xmltvMap
Data.XMLTV.Mapping[getFilenameFromPath(file)] = xmltvMap
} }
tmpMap[getFilenameFromPath(file)] = xmltvMap }
Data.XMLTV.Mapping[getFilenameFromPath(file)] = xmltvMap
Data.XMLTV.Mapping = tmpMap
tmpMap = make(map[string]interface{})
} else {
if System.ConfigurationWizard == false {
showWarning(1007)
} }
} }
@@ -236,11 +245,8 @@ func createXEPGMapping() {
} }
Data.XMLTV.Mapping = tmpMap
Data.XMLTV.Mapping["xTeVe Dummy"] = dummy Data.XMLTV.Mapping["xTeVe Dummy"] = dummy
tmpMap = make(map[string]interface{})
return return
} }
@@ -300,7 +306,6 @@ func createXEPGDatabase() (err error) {
} }
if len(xepgChannel.XChannelID) == 0 { if len(xepgChannel.XChannelID) == 0 {
fmt.Println(mapToJSON(xepgChannel))
delete(Data.XEPG.Channels, id) delete(Data.XEPG.Channels, id)
} }
@@ -310,6 +315,12 @@ func createXEPGDatabase() (err error) {
} }
var xepgChannels = make(map[string]interface{})
for k, v := range Data.XEPG.Channels {
xepgChannels[k] = v
}
for _, dsa := range Data.Streams.Active { for _, dsa := range Data.Streams.Active {
var channelExists = false // Entscheidet ob ein Kanal neu zu Datenbank hinzugefügt werden soll. var channelExists = false // Entscheidet ob ein Kanal neu zu Datenbank hinzugefügt werden soll.
@@ -325,7 +336,7 @@ func createXEPGDatabase() (err error) {
Data.Cache.Streams.Active = append(Data.Cache.Streams.Active, m3uChannel.Name) Data.Cache.Streams.Active = append(Data.Cache.Streams.Active, m3uChannel.Name)
// XEPG Datenbank durchlaufen um nach dem Kanal zu suchen. // XEPG Datenbank durchlaufen um nach dem Kanal zu suchen.
for xepg, dxc := range Data.XEPG.Channels { for xepg, dxc := range xepgChannels {
var xepgChannel XEPGChannelStruct var xepgChannel XEPGChannelStruct
err = json.Unmarshal([]byte(mapToJSON(dxc)), &xepgChannel) err = json.Unmarshal([]byte(mapToJSON(dxc)), &xepgChannel)
@@ -361,6 +372,7 @@ func createXEPGDatabase() (err error) {
//os.Exit(0) //os.Exit(0)
switch channelExists { switch channelExists {
case true: case true:
// Bereits vorhandener Kanal // Bereits vorhandener Kanal
var xepgChannel XEPGChannelStruct var xepgChannel XEPGChannelStruct
@@ -583,7 +595,12 @@ func createXMLTVFile() (err error) {
var xepgXML XMLTV var xepgXML XMLTV
xepgXML.Generator = System.Name xepgXML.Generator = System.Name
xepgXML.Source = fmt.Sprintf("%s - %s", System.Name, System.Version)
if System.Branch == "master" {
xepgXML.Source = fmt.Sprintf("%s - %s", System.Name, System.Version)
} else {
xepgXML.Source = fmt.Sprintf("%s - %s.%s", System.Name, System.Version, System.Build)
}
var tmpProgram = &XMLTV{} var tmpProgram = &XMLTV{}
@@ -749,7 +766,9 @@ func createDummyProgram(xepgChannel XEPGChannelStruct) (dummyXMLTV XMLTV) {
epg.Poster = append(epg.Poster, poster) 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"}) if xepgChannel.XCategory != "Movie" {
epg.EpisodeNum = append(epg.EpisodeNum, &EpisodeNum{Value: epgStartTime.Format("2006-01-02 15:04:05"), System: "original-air-date"})
}
epg.New = &New{Value: ""} epg.New = &New{Value: ""}
@@ -812,10 +831,19 @@ func getEpisodeNum(program *Program, xmltvProgram *Program, xepgChannel XEPGChan
program.EpisodeNum = xmltvProgram.EpisodeNum program.EpisodeNum = xmltvProgram.EpisodeNum
if len(xepgChannel.XCategory) > 0 { if len(xepgChannel.XCategory) > 0 && xepgChannel.XCategory != "Movie" {
if len(xmltvProgram.EpisodeNum) == 0 { if len(xmltvProgram.EpisodeNum) == 0 {
program.EpisodeNum = append(program.EpisodeNum, &EpisodeNum{Value: time.Now().Format("2006-01-02"), System: "original-air-date"})
var timeLayout = "20060102150405"
t, err := time.Parse(timeLayout, strings.Split(xmltvProgram.Start, " ")[0])
if err == nil {
program.EpisodeNum = append(program.EpisodeNum, &EpisodeNum{Value: t.Format("2006-01-02 15:04:05"), System: "original-air-date"})
} else {
ShowError(err, 0)
}
} }
} }

View File

@@ -23,7 +23,7 @@ menuItems.push(new MainMenuItem("logout", "{{.mainMenu.item.logout}}", "logout.p
var settingsCategory = new Array() var settingsCategory = new Array()
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.general}}", "xteveAutoUpdate,tuner,epgSource,api")) 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.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.streaming}}", "buffer,buffer.size.kb,buffer.timeout,user.agent,ffmpeg.path,ffmpeg.options,vlc.path,vlc.options"))
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.backup}}", "backup.path,backup.keep")) settingsCategory.push(new SettingsCategoryItem("{{.settings.category.backup}}", "backup.path,backup.keep"))
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.authentication}}", "authentication.web,authentication.pms,authentication.m3u,authentication.xml,authentication.api")) settingsCategory.push(new SettingsCategoryItem("{{.settings.category.authentication}}", "authentication.web,authentication.pms,authentication.m3u,authentication.xml,authentication.api"))

View File

@@ -62,6 +62,7 @@ class WizardItem extends WizardCategory {
case "m3u": case "m3u":
var input = content.createInput("text", key, "") var input = content.createInput("text", key, "")
input.setAttribute("placeholder", "{{.wizard.m3u.placeholder}}")
input.setAttribute("class", "wizard") input.setAttribute("class", "wizard")
input.id = key input.id = key
doc.appendChild(input) doc.appendChild(input)
@@ -72,6 +73,7 @@ class WizardItem extends WizardCategory {
case "xmltv": case "xmltv":
var input = content.createInput("text", key, "") var input = content.createInput("text", key, "")
input.setAttribute("placeholder", "{{.wizard.xmltv.placeholder}}")
input.setAttribute("class", "wizard") input.setAttribute("class", "wizard")
input.id = key input.id = key
doc.appendChild(input) doc.appendChild(input)
@@ -140,6 +142,12 @@ function saveWizard() {
name = (config[i] as HTMLInputElement).name name = (config[i] as HTMLInputElement).name
value = (config[i] as HTMLInputElement).value value = (config[i] as HTMLInputElement).value
if (value.length == 0) {
var msg = name.toUpperCase() + ": " + "{{.alert.missingInput}}"
alert(msg)
return
}
wizard[name] = value wizard[name] = value
break break
} }

View File

@@ -147,7 +147,7 @@ class Content {
var cell:Cell = new Cell() var cell:Cell = new Cell()
cell.child = true cell.child = true
cell.childType = "P" cell.childType = "P"
if (SERVER["settings"]["buffer"] == true) { if (SERVER["settings"]["buffer"] != "-") {
cell.value = data[key]["tuner"] cell.value = data[key]["tuner"]
} else { } else {
cell.value = "-" cell.value = "-"
@@ -714,7 +714,7 @@ class ShowContent extends Content {
break break
case "log": case "log":
var input = this.createInput("button", menuKey, "{{.button.resetlogs}}") var input = this.createInput("button", menuKey, "{{.button.resetLogs}}")
input.setAttribute("onclick", 'javascript: resetLogs();') input.setAttribute("onclick", 'javascript: resetLogs();')
interaction.appendChild(input) interaction.appendChild(input)
@@ -1113,7 +1113,7 @@ function openPopUp(dataType, element) {
content.appendRow("{{.playlist.fileM3U.title}}", input) content.appendRow("{{.playlist.fileM3U.title}}", input)
// Tuner // Tuner
if (SERVER["settings"]["buffer"] == true) { if (SERVER["settings"]["buffer"] != "-") {
var text:string[] = new Array() var text:string[] = new Array()
var values:string[] = new Array() var values:string[] = new Array()
@@ -1192,7 +1192,7 @@ function openPopUp(dataType, element) {
content.appendRow("{{.playlist.fileHDHR.title}}", input) content.appendRow("{{.playlist.fileHDHR.title}}", input)
// Tuner // Tuner
if (SERVER["settings"]["buffer"] == true) { if (SERVER["settings"]["buffer"] != "-") {
var text:string[] = new Array() var text:string[] = new Array()
var values:string[] = new Array() var values:string[] = new Array()
@@ -1530,6 +1530,8 @@ function openPopUp(dataType, element) {
} }
content.appendRow("{{.mapping.channelName.title}}", input) content.appendRow("{{.mapping.channelName.title}}", input)
content.description(data["name"])
// Aktualisierung des Kanalnamens // Aktualisierung des Kanalnamens
if (data.hasOwnProperty("_uuid.key")) { if (data.hasOwnProperty("_uuid.key")) {
if (data["_uuid.key"] != "") { if (data["_uuid.key"] != "") {
@@ -1570,6 +1572,10 @@ function openPopUp(dataType, element) {
input.setAttribute("onchange", "javascript: this.className = 'changed'") input.setAttribute("onchange", "javascript: this.className = 'changed'")
content.appendRow("{{.mapping.m3uGroupTitle.title}}", input) content.appendRow("{{.mapping.m3uGroupTitle.title}}", input)
if (data["group-title"] != undefined) {
content.description(data["group-title"])
}
// XMLTV Datei // XMLTV Datei
var dbKey:string = "x-xmltv-file" var dbKey:string = "x-xmltv-file"
var xmlFile = data[dbKey] var xmlFile = data[dbKey]

View File

@@ -24,7 +24,7 @@ class Server {
case "http:": case "http:":
this.protocol = "ws://" this.protocol = "ws://"
break break
case "https://": case "https:":
this.protocol = "wss://" this.protocol = "wss://"
break break
} }

View File

@@ -89,6 +89,62 @@ class SettingsCategory {
setting.appendChild(tdRight) setting.appendChild(tdRight)
break break
case "ffmpeg.path":
var tdLeft = document.createElement("TD")
tdLeft.innerHTML = "{{.settings.ffmpegPath.title}}" + ":"
var tdRight = document.createElement("TD")
var input = content.createInput("text", "ffmpeg.path", data)
input.setAttribute("placeholder", "{{.settings.ffmpegPath.placeholder}}")
input.setAttribute("onchange", "javascript: this.className = 'changed'")
tdRight.appendChild(input)
setting.appendChild(tdLeft)
setting.appendChild(tdRight)
break
case "ffmpeg.options":
var tdLeft = document.createElement("TD")
tdLeft.innerHTML = "{{.settings.ffmpegOptions.title}}" + ":"
var tdRight = document.createElement("TD")
var input = content.createInput("text", "ffmpeg.options", data)
input.setAttribute("placeholder", "{{.settings.ffmpegOptions.placeholder}}")
input.setAttribute("onchange", "javascript: this.className = 'changed'")
tdRight.appendChild(input)
setting.appendChild(tdLeft)
setting.appendChild(tdRight)
break
case "vlc.path":
var tdLeft = document.createElement("TD")
tdLeft.innerHTML = "{{.settings.vlcPath.title}}" + ":"
var tdRight = document.createElement("TD")
var input = content.createInput("text", "vlc.path", data)
input.setAttribute("placeholder", "{{.settings.vlcPath.placeholder}}")
input.setAttribute("onchange", "javascript: this.className = 'changed'")
tdRight.appendChild(input)
setting.appendChild(tdLeft)
setting.appendChild(tdRight)
break
case "vlc.options":
var tdLeft = document.createElement("TD")
tdLeft.innerHTML = "{{.settings.vlcOptions.title}}" + ":"
var tdRight = document.createElement("TD")
var input = content.createInput("text", "vlc.options", data)
input.setAttribute("placeholder", "{{.settings.vlcOptions.placeholder}}")
input.setAttribute("onchange", "javascript: this.className = 'changed'")
tdRight.appendChild(input)
setting.appendChild(tdLeft)
setting.appendChild(tdRight)
break
// Checkboxen // Checkboxen
case "authentication.web": case "authentication.web":
var tdLeft = document.createElement("TD") var tdLeft = document.createElement("TD")
@@ -216,20 +272,6 @@ class SettingsCategory {
setting.appendChild(tdRight) setting.appendChild(tdRight)
break 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": case "api":
var tdLeft = document.createElement("TD") var tdLeft = document.createElement("TD")
tdLeft.innerHTML = "{{.settings.api.title}}" + ":" tdLeft.innerHTML = "{{.settings.api.title}}" + ":"
@@ -314,6 +356,22 @@ class SettingsCategory {
setting.appendChild(tdRight) setting.appendChild(tdRight)
break break
case "buffer":
var tdLeft = document.createElement("TD")
tdLeft.innerHTML = "{{.settings.streamBuffering.title}}" + ":"
var tdRight = document.createElement("TD")
var text:any[] = ["{{.settings.streamBuffering.info_false}}", "xTeVe: ({{.settings.streamBuffering.info_xteve}})", "FFmpeg: ({{.settings.streamBuffering.info_ffmpeg}})", "VLC: ({{.settings.streamBuffering.info_vlc}})"]
var values:any[] = ["-", "xteve", "ffmpeg", "vlc"]
var select = content.createSelect(text, values, data, settingsKey)
select.setAttribute("onchange", "javascript: this.className = 'changed'")
tdRight.appendChild(select)
setting.appendChild(tdLeft)
setting.appendChild(tdRight)
break
} }
return setting return setting
@@ -381,6 +439,22 @@ class SettingsCategory {
text = "{{.settings.userAgent.description}}" text = "{{.settings.userAgent.description}}"
break break
case "ffmpeg.path":
text = "{{.settings.ffmpegPath.description}}"
break
case "ffmpeg.options":
text = "{{.settings.ffmpegOptions.description}}"
break
case "vlc.path":
text = "{{.settings.vlcPath.description}}"
break
case "vlc.options":
text = "{{.settings.vlcOptions.description}}"
break
case "epgSource": case "epgSource":
text = "{{.settings.epgSource.description}}" text = "{{.settings.epgSource.description}}"
break break

View File

@@ -35,11 +35,14 @@ var GitHub = GitHubStruct{Branch: "master", User: "xteve-project", Repo: "xTeVe-
Update: Automatic updates from the GitHub repository [true|false] Update: Automatic updates from the GitHub repository [true|false]
*/ */
// Name : Programname // Name : Programmname
const Name = "xTeVe" const Name = "xTeVe"
// Version : Version, die Build Nummer wird in der main func geparst. // Version : Version, die Build Nummer wird in der main func geparst.
const Version = "2.0.0.0000" const Version = "2.1.0.0100"
// DBVersion : Datanbank Version
const DBVersion = "2.1.0"
// APIVersion : API Version // APIVersion : API Version
const APIVersion = "1.1.0" const APIVersion = "1.1.0"
@@ -49,12 +52,15 @@ const Dev = false
var homeDirectory = fmt.Sprintf("%s%s.%s%s", src.GetUserHomeDirectory(), string(os.PathSeparator), strings.ToLower(Name), string(os.PathSeparator)) 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 samplePath = fmt.Sprintf("%spath%sto%sxteve%s", string(os.PathSeparator), string(os.PathSeparator), string(os.PathSeparator), string(os.PathSeparator))
var sampleRestore = fmt.Sprintf("%spath%sto%sfile%s", string(os.PathSeparator), string(os.PathSeparator), string(os.PathSeparator), string(os.PathSeparator))
var configFolder = flag.String("config", "", ": Config Folder ["+samplePath+"] (default: "+homeDirectory+")") var configFolder = flag.String("config", "", ": Config Folder ["+samplePath+"] (default: "+homeDirectory+")")
var port = flag.String("port", "", ": Server port [34400] (default: 34400)") var port = flag.String("port", "", ": Server port [34400] (default: 34400)")
var restore = flag.String("restore", "", ": Restore from backup ["+sampleRestore+"xteve_backup.zip]")
var gitBranch = flag.String("branch", "", ": Git Branch [master|beta] (default: master)") var gitBranch = flag.String("branch", "", ": Git Branch [master|beta] (default: master)")
var debug = flag.Int("debug", 0, ": Debug level [0 - 3] (default: 0)") var debug = flag.Int("debug", 0, ": Debug level [0 - 3] (default: 0)")
var info = flag.Bool("info", false, ": Show system info")
var h = flag.Bool("h", false, ": Show help") var h = flag.Bool("h", false, ": Show help")
func main() { func main() {
@@ -66,6 +72,7 @@ func main() {
system.APIVersion = APIVersion system.APIVersion = APIVersion
system.Branch = GitHub.Branch system.Branch = GitHub.Branch
system.Build = build[len(build)-1:][0] system.Build = build[len(build)-1:][0]
system.DBVersion = DBVersion
system.Dev = Dev system.Dev = Dev
system.GitHub = GitHub system.GitHub = GitHub
system.Name = Name system.Name = Name
@@ -115,6 +122,22 @@ func main() {
return return
} }
// Systeminformationen anzeigen
if *info {
system.Flag.Info = true
err := src.Init()
if err != nil {
src.ShowError(err, 0)
os.Exit(0)
}
src.ShowSystemInfo()
return
}
// Webserver Port // Webserver Port
if len(*port) > 0 { if len(*port) > 0 {
system.Flag.Port = *port system.Flag.Port = *port
@@ -138,6 +161,25 @@ func main() {
system.Folder.Config = *configFolder system.Folder.Config = *configFolder
} }
// Backup wiederherstellen
if len(*restore) > 0 {
system.Flag.Restore = *restore
err := src.Init()
if err != nil {
src.ShowError(err, 0)
os.Exit(0)
}
err = src.XteveRestoreFromCLI(*restore)
if err != nil {
src.ShowError(err, 0)
}
os.Exit(0)
}
err := src.Init() err := src.Init()
if err != nil { if err != nil {
src.ShowError(err, 0) src.ShowError(err, 0)