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
demo
compiler
files
update_xteve*.sh

View File

@@ -65,10 +65,11 @@ Thanks to @alturismo and @LeeD for creating the Docker Images.
**Created by alturismo:**
[xTeVe](https://hub.docker.com/r/alturismo/xteve)
[xTeVe / Guide2go](https://hub.docker.com/r/alturismo/xteve_guide2go)
[xTeVe / Guide2go / owi2plex](https://hub.docker.com/r/alturismo/xteve_g2g_owi)
Including:
- Guide2go: XMLTV grabber for Schedules Direct
- owi2plex: XMLTV file grabber for Enigma receivers
**Created by LeeD:**
[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]
#### 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)
* [websocket](https://github.com/gorilla/websocket)
* [osext](https://github.com/kardianos/osext)
#### Build
1. Download source code
2. Install dependancys
2. Install dependencies
```
go get github.com/koron/go-ssdp
go get github.com/gorilla/websocket
@@ -108,7 +140,7 @@ go build xteve.go
---
## Fork without pull request :mega:
When creating a fork, the xTeVe GitHub account must be changed from the sorce code or the update function disabled.
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:
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]{
width: 80%;
min-width: 35px;
max-width: 60px;
border: 0px;
background-color: #333;
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();
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.general}}", "xteveAutoUpdate,tuner,epgSource,api"));
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.files}}", "update,files.update,temp.path,cache.images,xepg.replace.missing.images"));
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.streaming}}", "buffer,buffer.size.kb,buffer.timeout,user.agent"));
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.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.authentication}}", "authentication.web,authentication.pms,authentication.m3u,authentication.xml,authentication.api"));
function showPopUpElement(elm) {

View File

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

View File

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

View File

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

View File

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

View File

@@ -23,11 +23,12 @@
}
},
"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": {
"fileLoadingError": "File couldn't be loaded",
"invalidChannelNumber": "Invalid channel number"
"invalidChannelNumber": "Invalid channel number",
"missingInput": "Missing input"
},
"button":{
"back": "Back",
@@ -44,7 +45,7 @@
"search": "Search",
"update": "Update",
"craeteAccount": "Create Account",
"resetlogs": "Reset Logs",
"resetLogs": "Reset Logs",
"uploadLogo": "Upload Logo"
},
"filter": {
@@ -242,7 +243,7 @@
},
"password": {
"title": "Password",
"placeholder": "Passoword",
"placeholder": "Password",
"description": ""
},
"confirm": {
@@ -306,7 +307,7 @@
"description": "Updates all playlists, tuner and XMLTV files at startup."
},
"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."
},
"replaceEmptyImages": {
@@ -319,7 +320,32 @@
},
"streamBuffering": {
"title": "Stream Buffer",
"description": "- The stream is passed from xTeVe to Plex / Emby / M3U Player<br>- Small jerking of the streams can be compensated<br>- HLS / M3U8 support"
"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": {
"title": "Buffer Size",
@@ -331,8 +357,8 @@
"placeholder": "100"
},
"userAgent": {
"title": "User agent",
"description": "User Agent for HTTP requests",
"title": "User Agent",
"description": "User Agent for HTTP requests. Only used if xTeVe is selected as the buffer.",
"placeholder": "xTeVe"
},
"backupPath": {
@@ -381,10 +407,12 @@
},
"m3u": {
"title": "M3U Playlist",
"placeholder": "File path or URL of the M3U",
"description": "Local or remote playlists"
},
"xmltv": {
"title": "XMLTV File",
"placeholder": "File path or URL of the XMLTV",
"description": "Local or remote XMLTV file"
}
},

View File

@@ -2,6 +2,7 @@ package src
import (
b64 "encoding/base64"
"errors"
"fmt"
"io/ioutil"
"os"
@@ -124,35 +125,44 @@ func xteveBackup() (archiv string, err error) {
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
b64data := input[strings.IndexByte(input, ',')+1:]
// Base64 in bytes umwandeln und speichern
sDec, err := b64.StdEncoding.DecodeString(b64data)
tmpRestore = System.Folder.Temp + "restore" + string(os.PathSeparator)
err = checkFolder(tmpRestore)
if err != nil {
return
}
var archive = System.Folder.Temp + "restore.zip"
err = writeByteToFile(archive, sDec)
// Zip Archiv in tmp entpacken
err = extractZIP(archive, tmpRestore)
if err != nil {
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)
if err != nil {
return
}
// Neue Config laden um den Port zu überprüfen
newConfig, err := loadJSONFileToMap(System.Folder.Config + "settings.json")
// Neue Config laden um den Port und die Version zu überprüfen
newConfig, err = loadJSONFileToMap(System.Folder.Config + "settings.json")
if err != nil {
ShowError(err, 0)
return
@@ -187,5 +197,78 @@ func xteveRestore(input string) (newWebURL string, err error) {
var url = System.URLBase + "/web/"
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
}

File diff suppressed because it is too large Load Diff

View File

@@ -46,6 +46,10 @@ func Init() (err error) {
System.DVRLimit = 480
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)
Settings.LogEntriesRAM = 500
@@ -83,6 +87,11 @@ func Init() (err error) {
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.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
if os.Geteuid() == 0 {
showWarning(2010)
showWarning(2110)
}
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("Database Version:%s", System.DBVersion))
showInfo(fmt.Sprintf("System IP Addresses:IPv4: %d | IPv6: %d", len(System.IPAddressesV4), len(System.IPAddressesV6)))
showInfo("Hostname:" + System.Hostname)
showInfo(fmt.Sprintf("System Folder:%s", getPlatformPath(System.Folder.Config)))
@@ -127,7 +137,6 @@ func Init() (err error) {
// Bedingte Update Änderungen durchführen
err = conditionalUpdateChanges()
if err != nil {
ShowError(err, 0)
return
}
@@ -160,9 +169,6 @@ func Init() (err error) {
return
}
// DLNA Server starten
go SSDP()
// Branch festlegen
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()
return

View File

@@ -24,6 +24,8 @@ func updateServerSettings(request RequestStruct) (settings SettingsStrcut, err e
var createXEPGFiles = false
var debug string
// -vvv [URL] --sout '#transcode{vcodec=mp4v, acodec=mpga} :standard{access=http, mux=ogg}'
for key, value := range newSettings {
if _, ok := oldSettings[key]; ok {
@@ -37,17 +39,25 @@ func updateServerSettings(request RequestStruct) (settings SettingsStrcut, err e
reloadData = true
case "update":
// Die Formatierung der Uhrzeit überprüfen (0000 - 2359)
for _, i := range newSettings[key].([]interface{}) {
// Leerzeichen aus den Werten entfernen und Formatierung der Uhrzeit überprüfen (0000 - 2359)
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 {
ShowError(err, 1012)
return Settings, err
}
newUpdateTimes = append(newUpdateTimes, v.(string))
}
value = newUpdateTimes
case "cache.images":
cacheImages = true
@@ -86,6 +96,17 @@ func updateServerSettings(request RequestStruct) (settings SettingsStrcut, err e
return
}
case "ffmpeg.path", "vlc.path":
var path = value.(string)
if len(path) > 0 {
err = checkFile(path)
if err != nil {
return
}
}
}
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)
if err == nil {
@@ -358,6 +406,7 @@ func saveFilter(request RequestStruct) (settings SettingsStrcut, err error) {
var filterMap = make(map[int64]interface{})
var newData = make(map[int64]interface{})
var defaultFilter FilterStruct
var newFilter = false
defaultFilter.Active = true
defaultFilter.CaseSensitive = false
@@ -381,6 +430,7 @@ func saveFilter(request RequestStruct) (settings SettingsStrcut, err error) {
if dataID == -1 {
// Neuer Filter
newFilter = true
dataID = createNewID()
filterMap[dataID] = jsonToMap(mapToJSON(defaultFilter))
@@ -389,15 +439,28 @@ func saveFilter(request RequestStruct) (settings SettingsStrcut, err error) {
// Filter aktualisieren / löschen
for key, value := range data.(map[string]interface{}) {
var oldData = filterMap[dataID].(map[string]interface{})
oldData[key] = value
// Filter löschen
if _, ok := data.(map[string]interface{})["delete"]; ok {
delete(filterMap, dataID)
break
}
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
cleanupXEPG()
buildXEPG(true)
if System.ScanInProgress == 0 {
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
}
@@ -604,6 +719,7 @@ func saveWizard(request RequestStruct) (nextStep int, err error) {
}
buildXEPG(false)
System.ScanInProgress = 0
}

View File

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

View File

@@ -6,24 +6,41 @@ import (
"io"
"io/ioutil"
"net/http"
"net/url"
"os"
"path/filepath"
"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)
var fileExtension = filepath.Ext(url)
imageURL = strings.Trim(imageURL, "\r\n")
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 {
Data.Cache.ImagesFiles = append(Data.Cache.ImagesFiles, urlMD5+fileExtension)
}
if Settings.CacheImages == false || System.ImageCachingInProgress == 1 {
return url
if System.ImageCachingInProgress == 1 {
return imageURL
}
if indexOfString(urlMD5+fileExtension, Data.Cache.ImagesCache) != -1 {
@@ -32,15 +49,15 @@ func getCacheImageURL(url string) (cacheImageURL string) {
} else {
if strings.Contains(url, System.Domain+"/images/") == false {
if strings.Contains(imageURL, System.Domain+"/images/") == false {
if indexOfString(url, Data.Cache.ImagesURLS) == -1 {
Data.Cache.ImagesURLS = append(Data.Cache.ImagesURLS, url)
if indexOfString(imageURL, Data.Cache.ImagesURLS) == -1 {
Data.Cache.ImagesURLS = append(Data.Cache.ImagesURLS, imageURL)
}
}
cacheImageURL = url
cacheImageURL = imageURL
}
@@ -57,10 +74,10 @@ func cachingImages() {
showInfo("Image Caching:Images are cached")
for _, url := range Data.Cache.ImagesURLS {
for _, imageURL := range Data.Cache.ImagesURLS {
if len(url) > 0 {
cacheImage(url)
if len(imageURL) > 0 {
cacheImage(imageURL)
}
}
@@ -94,16 +111,16 @@ func cachingImages() {
return
}
func cacheImage(url string) {
func cacheImage(imageURL string) {
var debug string
var urlMD5 = getMD5(url)
var fileExtension = filepath.Ext(url)
var urlMD5 = getMD5(imageURL)
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)
resp, err := http.Get(url)
resp, err := http.Get(imageURL)
if err != nil {
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 (
"errors"
"fmt"
"log"
"net/url"
"regexp"
"strings"
@@ -12,6 +14,7 @@ func MakeInterfaceFromM3U(byteStream []byte) (allChannels []interface{}, err err
var content = string(byteStream)
var channelName string
var uuids []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)
p = strings.Replace(p, `"`, "", -1)
var parameter = strings.Split(p, "=")
var parameter = strings.SplitN(p, "=", 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 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.value"] = value
//os.Exit(0)
break
}
@@ -161,107 +170,13 @@ func MakeInterfaceFromM3U(byteStream []byte) (allChannels []interface{}, err err
return
}
// MakeInterfaceFromM3U2 :
func MakeInterfaceFromM3U2(byteStream []byte) (allChannels []interface{}, err error) {
var content = string(byteStream)
//var allChannels = make([]interface{}, 0)
func indexOfString(element string, data []string) int {
var channels = strings.Split(content, "#EXTINF")
var parseMetaData = func(metaData string) map[string]string {
var values string // Save all values in a key
var channel = make(map[string]string)
var exceptForParameter = `[a-z-A-Z=]*(".*?")`
//var exceptForChannelName = `(,[^.$\n]*|,[^.$\r]*)`
var exceptForChannelName = `(,[^\n]*|,[^\r]*)`
var exceptForStreamingURL = `(\n.*?\n|\r.*?\r|\n.*?\z|\r.*?\z)`
//var exceptForStreamingURL = `^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?`
// Parse all parameters
p := regexp.MustCompile(exceptForParameter)
var parameter = p.FindAllString(metaData, -1)
//fmt.Println(parameter)
for _, i := range parameter {
var remove = i
i = strings.Replace(i, `"`, "", -1)
if strings.Contains(i, "=") {
var item = strings.Split(i, "=")
switch strings.Contains(item[0], "tvg") {
case true:
channel[strings.ToLower(item[0])] = item[1]
case false:
channel[item[0]] = item[1]
}
switch strings.Contains(item[1], "://") {
case false:
values = values + item[1] + " "
}
}
metaData = strings.Replace(metaData, remove, "", 1)
for k, v := range data {
if element == v {
return k
}
// Parse channel name (after the comma)
n := regexp.MustCompile(exceptForChannelName)
var name = n.FindAllString(metaData, 1)
//name[len(name) - 1] = strings.Replace(name[len(name) - 1], `\r`, "", -1)
var channelName string
if len(name) == 0 {
if v, ok := channel["tvg-name"]; ok {
channelName = v
}
} else {
channelName = name[len(name)-1][1:len(name[len(name)-1])]
}
channelName = strings.Replace(channelName, `"`, "", -1)
var replacer = strings.NewReplacer("\n", "", "\r", "")
channel["name"] = replacer.Replace(channelName)
values = values + channelName + " "
// Parse streaming URL
u := regexp.MustCompile(exceptForStreamingURL)
var streamingURL = u.FindAllString(metaData, -1)
var url = strings.Replace(streamingURL[0], "\n", "", -1)
url = strings.Replace(url, "\r", "", -1)
url = strings.Trim(url, "\r\n")
channel["url"] = url
channel["_values"] = values
// Search for a unique ID
for key, value := range channel {
if !strings.Contains(strings.ToLower(key), "tvg-id") {
if strings.Contains(strings.ToLower(key), "id") {
channel["_uuid.key"] = key
channel["_uuid.value"] = value
break
}
}
}
return channel
}
if strings.Contains(channels[0], "#EXTM3U") {
for _, thisStream := range channels {
if !strings.Contains(thisStream, "#EXTM3U") {
var channel = parseMetaData(thisStream)
allChannels = append(allChannels, channel)
}
}
} else {
err = errors.New("No valid m3u file")
}
return
return -1
}

View File

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

View File

@@ -12,6 +12,10 @@ import (
func showInfo(str string) {
if System.Flag.Info == true {
return
}
var max = 22
var msg = strings.SplitN(str, ":", 2)
var length = len(msg[0])
@@ -240,7 +244,9 @@ func getErrMsg(errCode int) (errMsg string) {
case 1012:
errMsg = fmt.Sprintf("Invalid formatting of the time")
case 1013:
errMsg = fmt.Sprintf("Invalid settings file (%s), file must be at least version %s", System.File.Settings, System.Compatibility)
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:
errMsg = fmt.Sprintf("Data could not be saved, invalid keyword")
@@ -248,12 +254,13 @@ func getErrMsg(errCode int) (errMsg string) {
// Datenbank Update
case 1030:
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
case 1050:
errMsg = fmt.Sprintf("Invalid duration specification in the M3U8 playlist.")
// M3U Parser
case 1060:
errMsg = fmt.Sprintf("Invalid characters found in the tvg parameters, streams with invalid parameters were skipped.")
@@ -262,6 +269,8 @@ func getErrMsg(errCode int) (errMsg string) {
errMsg = fmt.Sprintf("Folder could not be created.")
case 1071:
errMsg = fmt.Sprintf("File could not be created")
case 1072:
errMsg = fmt.Sprintf("File not found")
// Backup
case 1090:
@@ -286,6 +295,8 @@ func getErrMsg(errCode int) (errMsg string) {
errMsg = fmt.Sprintf("Steaming URL could not be found in any playlist")
case 1203:
errMsg = fmt.Sprintf("Steaming URL could not be found in any playlist")
case 1204:
errMsg = fmt.Sprintf("Streaming was stopped by third party transcoder (FFmpeg / VLC)")
// Warnings
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.")
case 2010:
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:
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")
case 4005:
errMsg = fmt.Sprintf("Temporary buffer files could not be deleted")
case 4006:
errMsg = fmt.Sprintf("Server connection timeout")
// Buffer (M3U8)
case 4050:
@@ -351,6 +368,8 @@ func getErrMsg(errCode int) (errMsg string) {
// Caching
case 4100:
errMsg = fmt.Sprintf("Unknown content type for downloaded image")
case 4101:
errMsg = fmt.Sprintf("Invalid URL, original URL is used for this image")
// API
case 5000:
@@ -362,7 +381,7 @@ func getErrMsg(errCode int) (errMsg string) {
case 6002:
errMsg = fmt.Sprintf("Update failed")
case 6003:
errMsg = fmt.Sprintf("Server not available")
errMsg = fmt.Sprintf("Update server not available")
case 6004:
errMsg = fmt.Sprintf("xTeVe update available")

View File

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

View File

@@ -68,12 +68,13 @@ type ThisStream struct {
// Segment : URL Segmente (HLS / M3U8)
type Segment struct {
Duration float64
Info bool
Sequence int64
URL string
Version int
Wait float64
Duration float64
Info bool
PlaylistType string
Sequence int64
URL string
Version int
Wait float64
StreamInf struct {
AverageBandwidth int
@@ -107,3 +108,147 @@ type BandwidthCalculation struct {
Stop time.Time
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
AppName string
ARCH string
BackgroundProcess bool
Branch string
Build string
Compatibility string
ConfigurationWizard bool
DBVersion string
Dev bool
DeviceID string
Domain string
DVRLimit int
FFmpeg struct {
DefaultOptions string
Path string
}
VLC struct {
DefaultOptions string
Path string
}
File struct {
Authentication string
M3U string
@@ -31,10 +43,12 @@ type SystemStruct struct {
}
Flag struct {
Branch string
Debug int
Port string
SSDP bool
Branch string
Debug int
Info bool
Port string
Restore string
SSDP bool
}
Folder struct {
@@ -238,11 +252,15 @@ type SettingsStrcut struct {
BackupKeep int `json:"backup.keep"`
BackupPath string `json:"backup.path"`
Branch string `json:"git.branch,omitempty"`
Buffer bool `json:"buffer"`
Buffer string `json:"buffer"`
BufferSize int `json:"buffer.size.kb"`
BufferTimeout float64 `json:"buffer.timeout"`
CacheImages bool `json:"cache.images"`
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
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"`
BackupKeep *int `json:"backup.keep,omitempty"`
BackupPath *string `json:"backup.path,omitempty"`
Buffer *bool `json:"buffer,omitempty"`
Buffer *string `json:"buffer,omitempty"`
BufferSize *int `json:"buffer.size.kb, omitempty"`
BufferTimeout *float64 `json:"buffer.timeout,omitempty"`
CacheImages *bool `json:"cache.images,omitempty"`
EpgSource *string `json:"epgSource,omitempty"`
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"`
TempPath *string `json:"temp.path,omitempty"`
Tuner *int `json:"tuner,omitempty"`

View File

@@ -113,11 +113,13 @@ func loadSettings() (settings SettingsStrcut, err error) {
defaults["authentication.xml"] = false
defaults["backup.keep"] = 10
defaults["backup.path"] = System.Folder.Backup
defaults["buffer"] = false
defaults["buffer"] = "-"
defaults["buffer.size.kb"] = 1024
defaults["buffer.timeout"] = 500
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.update"] = true
defaults["filter"] = make(map[string]interface{})
@@ -133,7 +135,7 @@ func loadSettings() (settings SettingsStrcut, err error) {
defaults["update"] = []string{"0000"}
defaults["user.agent"] = System.Name
defaults["uuid"] = createUUID()
defaults["version"] = System.Version
defaults["version"] = System.DBVersion
defaults["xteveAutoUpdate"] = true
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))
}
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)
// 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
}

View File

@@ -10,8 +10,11 @@ import (
"io/ioutil"
"net"
"os"
"os/exec"
"os/user"
"path/filepath"
"runtime"
"strings"
"text/template"
)
@@ -42,13 +45,25 @@ func checkFolder(path string) (err error) {
return nil
}
// Prüft ob die datei im Dateisystem existiert
// Prüft ob die Datei im Dateisystem existiert
func checkFile(filename string) (err error) {
var file = getPlatformFile(filename)
if _, err = os.Stat(file); os.IsNotExist(err) {
return
return 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
@@ -77,6 +92,7 @@ func GetUserHomeDirectory() (userHomeDirectory string) {
return
}
// Prüft Dateiberechtigung
func checkFilePermission(dir string) (err error) {
var filename = dir + "permission.test"
@@ -115,6 +131,34 @@ func removeOldSystemData() {
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 {
@@ -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()
if err != nil {
return

View File

@@ -41,8 +41,8 @@ func BinaryUpdate() (err error) {
resp, err := http.Get(gitInfo)
if err != nil {
ShowError(err, 0)
return err
ShowError(err, 6003)
return nil
}
if resp.StatusCode != http.StatusOK {
@@ -169,6 +169,13 @@ checkVersion:
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)
if settingsVersion < System.Compatibility {
err = errors.New(getErrMsg(1013))
@@ -189,7 +196,7 @@ checkVersion:
var newFilterMap = convertToNewFilter(oldFilter)
settingsMap["filter"] = newFilterMap
settingsMap["version"] = "1.9.0"
settingsMap["version"] = "2.0.0"
err = saveMapToJSONFile(System.File.Settings, settingsMap)
if err != nil {
@@ -203,11 +210,38 @@ checkVersion:
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
break
}
} 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)
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 {
ShowError(err, 1001)
@@ -117,20 +129,31 @@ func Stream(w http.ResponseWriter, r *http.Request) {
return
}
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)
switch Settings.Buffer {
showInfo("Streaming URL:" + streamInfo.URL)
http.Redirect(w, r, streamInfo.URL, 302)
case "-":
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 == true {
if Settings.Buffer != "-" {
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
switch Settings.Buffer {
case true:
bufferingStream(streamInfo.PlaylistID, streamInfo.URL, streamInfo.Name, w, r)
case false:
case "-":
showInfo("Streaming URL:" + streamInfo.URL)
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
if strings.Contains(path, "xmltv/") {
w.Header().Set("Content-Type", "application/xml")
requestType = "xml"
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)
}
setGlobalDomain(r.Host)
for {
err = conn.ReadJSON(&request)
@@ -447,7 +474,7 @@ func WS(w http.ResponseWriter, r *http.Request) {
file, errNew := xteveBackup()
err = errNew
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":
@@ -457,9 +484,10 @@ func WS(w http.ResponseWriter, r *http.Request) {
if len(request.Base64) > 0 {
newWebURL, err := xteveRestore(request.Base64)
newWebURL, err := xteveRestoreFromWeb(request.Base64)
if err != nil {
ShowError(err, 000)
response.Alert = err.Error()
}
if err == nil {
@@ -528,7 +556,6 @@ func WS(w http.ResponseWriter, r *http.Request) {
response.Settings = Settings
}
setGlobalDomain(r.Host)
response = setDefaultResponseData(response, true)
if System.ConfigurationWizard == true {
response.ConfigurationWizard = System.ConfigurationWizard
@@ -582,8 +609,12 @@ func Web(w http.ResponseWriter, r *http.Request) {
if getFilenameFromPath(requestFile) == "html" {
if len(Data.Streams.All) == 0 && System.ScanInProgress == 0 {
System.ConfigurationWizard = true
if System.ScanInProgress == 0 {
if len(Settings.Files.M3U) == 0 && len(Settings.Files.HDHR) == 0 {
System.ConfigurationWizard = true
}
}
switch System.ConfigurationWizard {
@@ -867,8 +898,6 @@ func API(w http.ResponseWriter, r *http.Request) {
case "status":
fmt.Println("-----------------------------")
os.Exit(0)
response.VersionXteve = System.Version
response.VersionAPI = System.APIVersion
response.StreamsActive = int64(len(Data.Streams.Active))

View File

@@ -174,49 +174,58 @@ func createXEPGMapping() {
return
}
if len(Data.XMLTV.Files) == 0 {
return
}
if len(Data.XMLTV.Files) > 0 {
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 fileID = strings.TrimSuffix(getFilenameFromPath(file), path.Ext(getFilenameFromPath(file)))
showInfo("XEPG:" + "Parse XMLTV file: " + getProviderParameter(fileID, "xmltv", "name"))
var err error
var fileID = strings.TrimSuffix(getFilenameFromPath(file), path.Ext(getFilenameFromPath(file)))
showInfo("XEPG:" + "Parse XMLTV file: " + getProviderParameter(fileID, "xmltv", "name"))
//xmltv, err = getLocalXMLTV(file)
var xmltv XMLTV
//xmltv, err = getLocalXMLTV(file)
var xmltv XMLTV
err = getLocalXMLTV(file, &xmltv)
if err != nil {
Data.XMLTV.Files = append(Data.XMLTV.Files, Data.XMLTV.Files[i+1:]...)
var errMsg = err.Error()
err = errors.New(getProviderParameter(fileID, "xmltv", "name") + ": " + errMsg)
ShowError(err, 000)
}
err = getLocalXMLTV(file, &xmltv)
if err != nil {
Data.XMLTV.Files = append(Data.XMLTV.Files, Data.XMLTV.Files[i+1:]...)
var errMsg = err.Error()
err = errors.New(getProviderParameter(fileID, "xmltv", "name") + ": " + errMsg)
ShowError(err, 000)
}
// XML Parsen (Provider Datei)
if err == nil {
// XML Parsen (Provider Datei)
if err == nil {
// Daten aus der XML Datei in eine temporäre Map schreiben
var xmltvMap = make(map[string]interface{})
// Daten aus der XML Datei in eine temporäre Map schreiben
var xmltvMap = make(map[string]interface{})
for _, c := range xmltv.Channel {
var channel = make(map[string]interface{})
for _, c := range xmltv.Channel {
var channel = make(map[string]interface{})
channel["id"] = c.ID
channel["display-name"] = friendlyDisplayName(*c)
channel["icon"] = c.Icon.Src
channel["id"] = c.ID
channel["display-name"] = friendlyDisplayName(*c)
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
tmpMap = make(map[string]interface{})
return
}
@@ -300,7 +306,6 @@ func createXEPGDatabase() (err error) {
}
if len(xepgChannel.XChannelID) == 0 {
fmt.Println(mapToJSON(xepgChannel))
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 {
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)
// XEPG Datenbank durchlaufen um nach dem Kanal zu suchen.
for xepg, dxc := range Data.XEPG.Channels {
for xepg, dxc := range xepgChannels {
var xepgChannel XEPGChannelStruct
err = json.Unmarshal([]byte(mapToJSON(dxc)), &xepgChannel)
@@ -361,6 +372,7 @@ func createXEPGDatabase() (err error) {
//os.Exit(0)
switch channelExists {
case true:
// Bereits vorhandener Kanal
var xepgChannel XEPGChannelStruct
@@ -583,7 +595,12 @@ func createXMLTVFile() (err error) {
var xepgXML XMLTV
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{}
@@ -749,7 +766,9 @@ func createDummyProgram(xepgChannel XEPGChannelStruct) (dummyXMLTV XMLTV) {
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: ""}
@@ -812,10 +831,19 @@ func getEpisodeNum(program *Program, xmltvProgram *Program, xepgChannel XEPGChan
program.EpisodeNum = xmltvProgram.EpisodeNum
if len(xepgChannel.XCategory) > 0 {
if len(xepgChannel.XCategory) > 0 && xepgChannel.XCategory != "Movie" {
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()
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.general}}", "xteveAutoUpdate,tuner,epgSource,api"))
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.files}}", "update,files.update,temp.path,cache.images,xepg.replace.missing.images"))
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.streaming}}", "buffer,buffer.size.kb,buffer.timeout,user.agent"))
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.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.authentication}}", "authentication.web,authentication.pms,authentication.m3u,authentication.xml,authentication.api"))

View File

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

View File

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

View File

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

View File

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

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]
*/
// Name : Programname
// Name : Programmname
const Name = "xTeVe"
// 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
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 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 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 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")
func main() {
@@ -66,6 +72,7 @@ func main() {
system.APIVersion = APIVersion
system.Branch = GitHub.Branch
system.Build = build[len(build)-1:][0]
system.DBVersion = DBVersion
system.Dev = Dev
system.GitHub = GitHub
system.Name = Name
@@ -115,6 +122,22 @@ func main() {
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
if len(*port) > 0 {
system.Flag.Port = *port
@@ -138,6 +161,25 @@ func main() {
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()
if err != nil {
src.ShowError(err, 0)