Compare commits
24 Commits
2.1.0.0100
...
2.1.1.0111
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
66c01dd1fb | ||
|
|
03e1abbe90 | ||
|
|
469581e280 | ||
|
|
019c98996a | ||
|
|
36db927794 | ||
|
|
f0a49788cc | ||
|
|
72767d7dbd | ||
|
|
8eecbf2b78 | ||
|
|
1a1e37fe15 | ||
|
|
08f6fb60e3 | ||
|
|
eded490ac7 | ||
|
|
ed770b9dbc | ||
|
|
6129b4911a | ||
|
|
477c5f30c1 | ||
|
|
65ddc6f301 | ||
|
|
3ef95c1950 | ||
|
|
3a3798cd2d | ||
|
|
72eb7fb599 | ||
|
|
4b969b8cee | ||
|
|
3d9266dabe | ||
|
|
dc42afcd05 | ||
|
|
20e5e1b545 | ||
|
|
7c87d1d5bd | ||
|
|
ad992eb615 |
@@ -31,7 +31,7 @@ Documentation for setup and configuration is [here](https://github.com/xteve-pro
|
|||||||
* Merge external M3U files
|
* Merge external M3U files
|
||||||
* Merge external XMLTV files
|
* Merge external XMLTV files
|
||||||
* Automatic M3U and XMLTV update
|
* Automatic M3U and XMLTV update
|
||||||
* M3U und XMLTV export
|
* M3U and XMLTV export
|
||||||
|
|
||||||
#### Channel management
|
#### Channel management
|
||||||
* Filtering streams
|
* Filtering streams
|
||||||
@@ -84,7 +84,7 @@ Including:
|
|||||||
---
|
---
|
||||||
|
|
||||||
### xTeVe Beta branch
|
### 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.
|
New features and bug fixes are only available in beta branch. Only after successful testing are they are merged into the master branch.
|
||||||
|
|
||||||
**It is not recommended to use the beta version in a production system.**
|
**It is not recommended to use the beta version in a production system.**
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,20 @@
|
|||||||
|
#### 2.1.0.0106-beta
|
||||||
|
```diff
|
||||||
|
+ User-Agent is now also used by VLC and FFmpeg.
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2.1.0.0105-beta
|
||||||
|
```diff
|
||||||
|
+ Fixed wrong buffer value in log
|
||||||
|
+ New setting: URL protocol for M3U and XML file
|
||||||
|
+ Add xml tag premiere to xteve.xml
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2.1.0.0101-beta
|
||||||
|
```diff
|
||||||
|
+ Reverse proxy fix
|
||||||
|
```
|
||||||
|
|
||||||
#### 2.0.3.0042-beta
|
#### 2.0.3.0042-beta
|
||||||
**Version 2.0.3.0042 changes the settings.json.**
|
**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
|
Settings from the current beta can not be used for the current master version 2.0.3
|
||||||
|
|||||||
@@ -18,9 +18,9 @@ menuItems.push(new MainMenuItem("log", "{{.mainMenu.item.log}}", "log.png", "{{.
|
|||||||
menuItems.push(new MainMenuItem("logout", "{{.mainMenu.item.logout}}", "logout.png", "{{.mainMenu.headline.logout}}"));
|
menuItems.push(new MainMenuItem("logout", "{{.mainMenu.item.logout}}", "logout.png", "{{.mainMenu.headline.logout}}"));
|
||||||
// Kategorien für die Einstellungen
|
// Kategorien für die Einstellungen
|
||||||
var settingsCategory = new Array();
|
var settingsCategory = new Array();
|
||||||
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.general}}", "xteveAutoUpdate,tuner,epgSource,api"));
|
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.general}}", "xteveAutoUpdate,tuner,epgSource,api,scheme.m3u,scheme.xml"));
|
||||||
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) {
|
||||||
|
|||||||
@@ -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 {
|
||||||
@@ -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++) {
|
||||||
@@ -1253,6 +1253,12 @@ function openPopUp(dataType, element) {
|
|||||||
}
|
}
|
||||||
content.appendRow("{{.mapping.channelName.title}}", input);
|
content.appendRow("{{.mapping.channelName.title}}", input);
|
||||||
content.description(data["name"]);
|
content.description(data["name"]);
|
||||||
|
// Beschreibung
|
||||||
|
var dbKey = "x-description";
|
||||||
|
var input = content.createInput("text", dbKey, data[dbKey]);
|
||||||
|
input.setAttribute("placeholder", "{{.mapping.description.placeholder}}");
|
||||||
|
input.setAttribute("onchange", "javascript: this.className = 'changed'");
|
||||||
|
content.appendRow("{{.mapping.description.title}}", input);
|
||||||
// Aktualisierung des Kanalnamens
|
// Aktualisierung des Kanalnamens
|
||||||
if (data.hasOwnProperty("_uuid.key")) {
|
if (data.hasOwnProperty("_uuid.key")) {
|
||||||
if (data["_uuid.key"] != "") {
|
if (data["_uuid.key"] != "") {
|
||||||
|
|||||||
@@ -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,42 @@ 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;
|
||||||
|
case "scheme.m3u":
|
||||||
|
var tdLeft = document.createElement("TD");
|
||||||
|
tdLeft.innerHTML = "{{.settings.schemeM3U.title}}" + ":";
|
||||||
|
var tdRight = document.createElement("TD");
|
||||||
|
var text = ["HTTP", "HTTPS"];
|
||||||
|
var values = ["HTTP", "HTTPS"];
|
||||||
|
var select = content.createSelect(text, values, data, settingsKey);
|
||||||
|
select.setAttribute("onchange", "javascript: this.className = 'changed'");
|
||||||
|
tdRight.appendChild(select);
|
||||||
|
setting.appendChild(tdLeft);
|
||||||
|
setting.appendChild(tdRight);
|
||||||
|
break;
|
||||||
|
case "scheme.xml":
|
||||||
|
var tdLeft = document.createElement("TD");
|
||||||
|
tdLeft.innerHTML = "{{.settings.schemeXML.title}}" + ":";
|
||||||
|
var tdRight = document.createElement("TD");
|
||||||
|
var text = ["HTTP", "HTTPS"];
|
||||||
|
var values = ["HTTP", "HTTPS"];
|
||||||
|
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 +377,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;
|
||||||
@@ -329,6 +410,12 @@ var SettingsCategory = /** @class */ (function () {
|
|||||||
case "xepg.replace.missing.images":
|
case "xepg.replace.missing.images":
|
||||||
text = "{{.settings.replaceEmptyImages.description}}";
|
text = "{{.settings.replaceEmptyImages.description}}";
|
||||||
break;
|
break;
|
||||||
|
case "scheme.m3u":
|
||||||
|
text = "{{.settings.schemeM3U.description}}";
|
||||||
|
break;
|
||||||
|
case "scheme.xml":
|
||||||
|
text = "{{.settings.schemeXML.description}}";
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
text = "";
|
text = "";
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -190,6 +190,11 @@
|
|||||||
"placeholder": "",
|
"placeholder": "",
|
||||||
"description": ""
|
"description": ""
|
||||||
},
|
},
|
||||||
|
"description": {
|
||||||
|
"title": "Channel Description",
|
||||||
|
"placeholder": "Used by the Dummy as an XML description",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
"updateChannelName": {
|
"updateChannelName": {
|
||||||
"title": "Update Channel Name",
|
"title": "Update Channel Name",
|
||||||
"placeholder": "",
|
"placeholder": "",
|
||||||
@@ -302,12 +307,20 @@
|
|||||||
"title": "Number of Tuners",
|
"title": "Number of Tuners",
|
||||||
"description": "Number of parallel connections that can be established to the provider.<br>Available for: Plex, Emby (HDHR), M3U (with active buffer).<br>After a change, xTeVe must be delete in the Plex / Emby DVR settings and set up again."
|
"description": "Number of parallel connections that can be established to the provider.<br>Available for: Plex, Emby (HDHR), M3U (with active buffer).<br>After a change, xTeVe must be delete in the Plex / Emby DVR settings and set up again."
|
||||||
},
|
},
|
||||||
|
"schemeM3U":{
|
||||||
|
"title": "URL protocol for xteve.m3u",
|
||||||
|
"description": "Determines which URL protocol is used for the xTeVe streaming URLs. If you using a reverse proxy over HTTPS, set this to HTTPS."
|
||||||
|
},
|
||||||
|
"schemeXML":{
|
||||||
|
"title": "URL protocol for xteve.xml",
|
||||||
|
"description": "Determines which URL protocol is used for the xTeVe image URLs. If you using a reverse proxy over HTTPS, set this to HTTPS."
|
||||||
|
},
|
||||||
"filesUpdate": {
|
"filesUpdate": {
|
||||||
"title": "Updates all files at startup",
|
"title": "Updates all files at startup",
|
||||||
"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": {
|
||||||
@@ -320,7 +333,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<br>- Re-streaming<br>- Separate tuner limit for each playlist"
|
"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",
|
||||||
@@ -332,8 +370,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. For every HTTP connection, this value is used for the user agent. Should only be changed if xTeVe is blocked.",
|
||||||
"placeholder": "xTeVe"
|
"placeholder": "xTeVe"
|
||||||
},
|
},
|
||||||
"backupPath": {
|
"backupPath": {
|
||||||
|
|||||||
349
src/buffer.go
349
src/buffer.go
@@ -6,6 +6,8 @@ package src
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
@@ -13,6 +15,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"path"
|
"path"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -205,7 +208,17 @@ func bufferingStream(playlistID, streamingURL, channelName string, w http.Respon
|
|||||||
playlist.Streams[streamID] = stream
|
playlist.Streams[streamID] = stream
|
||||||
BufferInformation.Store(playlistID, playlist)
|
BufferInformation.Store(playlistID, playlist)
|
||||||
|
|
||||||
go connectToStreamingServer(streamID, playlistID)
|
switch Settings.Buffer {
|
||||||
|
|
||||||
|
case "xteve":
|
||||||
|
go connectToStreamingServer(streamID, playlistID)
|
||||||
|
case "ffmpeg", "vlc":
|
||||||
|
go thirdPartyBuffer(streamID, playlistID)
|
||||||
|
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
showInfo(fmt.Sprintf("Streaming Status:Playlist: %s - Tuner: %d / %d", playlist.PlaylistName, len(playlist.Streams), playlist.Tuner))
|
showInfo(fmt.Sprintf("Streaming Status:Playlist: %s - Tuner: %d / %d", playlist.PlaylistName, len(playlist.Streams), playlist.Tuner))
|
||||||
|
|
||||||
@@ -215,7 +228,7 @@ func bufferingStream(playlistID, streamingURL, channelName string, w http.Respon
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
w.WriteHeader(200)
|
//w.WriteHeader(200)
|
||||||
|
|
||||||
for { // Loop 1: Warten bis das erste Segment durch den Buffer heruntergeladen wurde
|
for { // Loop 1: Warten bis das erste Segment durch den Buffer heruntergeladen wurde
|
||||||
|
|
||||||
@@ -248,7 +261,9 @@ func bufferingStream(playlistID, streamingURL, channelName string, w http.Respon
|
|||||||
var oldSegments []string
|
var oldSegments []string
|
||||||
|
|
||||||
for { // Loop 2: Temporäre Datein sind vorhanden, Daten können zum Client gesendet werden
|
for { // Loop 2: Temporäre Datein sind vorhanden, Daten können zum Client gesendet werden
|
||||||
|
|
||||||
// HTTP Clientverbindung überwachen
|
// HTTP Clientverbindung überwachen
|
||||||
|
|
||||||
cn, ok := w.(http.CloseNotifier)
|
cn, ok := w.(http.CloseNotifier)
|
||||||
if ok {
|
if ok {
|
||||||
|
|
||||||
@@ -317,10 +332,10 @@ func bufferingStream(playlistID, streamingURL, channelName string, w http.Respon
|
|||||||
|
|
||||||
if streaming == false {
|
if streaming == false {
|
||||||
|
|
||||||
contentType := http.DetectContentType(buffer)
|
contentType := http.DetectContentType(buffer) + "; name=stream.ts"
|
||||||
_ = contentType
|
//_ = contentType
|
||||||
//w.Header().Set("Content-type", "video/mpeg")
|
//w.Header().Set("Content-type", "video/mpeg")
|
||||||
w.Header().Set("Content-type", contentType)
|
w.Header().Add("Content-type", contentType)
|
||||||
w.Header().Set("Content-Length", "0")
|
w.Header().Set("Content-Length", "0")
|
||||||
w.Header().Set("Connection", "close")
|
w.Header().Set("Connection", "close")
|
||||||
|
|
||||||
@@ -333,6 +348,7 @@ func bufferingStream(playlistID, streamingURL, channelName string, w http.Respon
|
|||||||
w.Header().Set("transferMode.dlna.org", "Streaming")
|
w.Header().Set("transferMode.dlna.org", "Streaming")
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
w.WriteHeader(200)
|
||||||
_, err := w.Write(buffer)
|
_, err := w.Write(buffer)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -400,7 +416,7 @@ func getTmpFiles(stream *ThisStream) (tmpFiles []string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(files) > 1 {
|
if len(files) > 2 {
|
||||||
|
|
||||||
for _, file := range files {
|
for _, file := range files {
|
||||||
|
|
||||||
@@ -436,6 +452,9 @@ func getTmpFiles(stream *ThisStream) (tmpFiles []string) {
|
|||||||
|
|
||||||
func killClientConnection(streamID int, playlistID string, force bool) {
|
func killClientConnection(streamID int, playlistID string, force bool) {
|
||||||
|
|
||||||
|
Lock.Lock()
|
||||||
|
defer Lock.Unlock()
|
||||||
|
|
||||||
if p, ok := BufferInformation.Load(playlistID); ok {
|
if p, ok := BufferInformation.Load(playlistID); ok {
|
||||||
|
|
||||||
var playlist = p.(Playlist)
|
var playlist = p.(Playlist)
|
||||||
@@ -460,6 +479,7 @@ func killClientConnection(streamID int, playlistID string, force bool) {
|
|||||||
if clients.Connection <= 0 {
|
if clients.Connection <= 0 {
|
||||||
BufferClients.Delete(playlistID + stream.MD5)
|
BufferClients.Delete(playlistID + stream.MD5)
|
||||||
delete(playlist.Streams, streamID)
|
delete(playlist.Streams, streamID)
|
||||||
|
delete(playlist.Clients, streamID)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -479,6 +499,8 @@ func killClientConnection(streamID int, playlistID string, force bool) {
|
|||||||
func clientConnection(stream ThisStream) (status bool) {
|
func clientConnection(stream ThisStream) (status bool) {
|
||||||
|
|
||||||
status = true
|
status = true
|
||||||
|
Lock.Lock()
|
||||||
|
defer Lock.Unlock()
|
||||||
|
|
||||||
if _, ok := BufferClients.Load(stream.PlaylistID + stream.MD5); !ok {
|
if _, ok := BufferClients.Load(stream.PlaylistID + stream.MD5); !ok {
|
||||||
|
|
||||||
@@ -526,6 +548,9 @@ func connectToStreamingServer(streamID int, playlistID string) {
|
|||||||
var m3u8Segments []string
|
var m3u8Segments []string
|
||||||
var bandwidth BandwidthCalculation
|
var bandwidth BandwidthCalculation
|
||||||
var networkBandwidth = Settings.M3U8AdaptiveBandwidthMBPS * 1e+6
|
var networkBandwidth = Settings.M3U8AdaptiveBandwidthMBPS * 1e+6
|
||||||
|
// Größe des Buffers
|
||||||
|
var bufferSize = Settings.BufferSize
|
||||||
|
var buffer = make([]byte, 1024*bufferSize*2)
|
||||||
|
|
||||||
var defaultSegment = func() {
|
var defaultSegment = func() {
|
||||||
|
|
||||||
@@ -805,8 +830,8 @@ func connectToStreamingServer(streamID int, playlistID string) {
|
|||||||
var fileSize int
|
var fileSize int
|
||||||
|
|
||||||
// Größe des Buffers
|
// Größe des Buffers
|
||||||
buffer := make([]byte, 1024*Settings.BufferSize*2)
|
buffer = make([]byte, 1024*bufferSize*2)
|
||||||
var tmpFileSize = 1024 * Settings.BufferSize * 1
|
var tmpFileSize = 1024 * bufferSize * 1
|
||||||
|
|
||||||
debug = fmt.Sprintf("Buffer Size:%d KB [SERVER CONNECTION]", len(buffer)/1024)
|
debug = fmt.Sprintf("Buffer Size:%d KB [SERVER CONNECTION]", len(buffer)/1024)
|
||||||
showDebug(debug, 3)
|
showDebug(debug, 3)
|
||||||
@@ -882,7 +907,9 @@ func connectToStreamingServer(streamID int, playlistID string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Buffer auf die Festplatte speichern
|
// Buffer auf die Festplatte speichern
|
||||||
if fileSize >= tmpFileSize || n == 0 {
|
if fileSize >= tmpFileSize/2 || n == 0 {
|
||||||
|
|
||||||
|
Lock.Lock()
|
||||||
|
|
||||||
bandwidth.Stop = time.Now()
|
bandwidth.Stop = time.Now()
|
||||||
bandwidth.Size += fileSize
|
bandwidth.Size += fileSize
|
||||||
@@ -902,6 +929,7 @@ func connectToStreamingServer(streamID int, playlistID string) {
|
|||||||
stream.Status = true
|
stream.Status = true
|
||||||
playlist.Streams[streamID] = stream
|
playlist.Streams[streamID] = stream
|
||||||
BufferInformation.Store(playlistID, playlist)
|
BufferInformation.Store(playlistID, playlist)
|
||||||
|
Lock.Unlock()
|
||||||
|
|
||||||
tmpSegment++
|
tmpSegment++
|
||||||
|
|
||||||
@@ -928,6 +956,7 @@ func connectToStreamingServer(streamID int, playlistID string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fileSize = 0
|
fileSize = 0
|
||||||
|
buffer = make([]byte, 1024*bufferSize*2)
|
||||||
|
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
bufferFile.Close()
|
bufferFile.Close()
|
||||||
@@ -1255,12 +1284,6 @@ func parseM3U8(stream *ThisStream) (err error) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
err := checkFile(stream.Folder + "remove")
|
|
||||||
if err == nil {
|
|
||||||
os.RemoveAll(stream.Folder)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1319,14 +1342,306 @@ func switchBandwidth(stream *ThisStream) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Buffer mit FFMPEG
|
||||||
|
func thirdPartyBuffer(streamID int, playlistID string) {
|
||||||
|
|
||||||
|
if p, ok := BufferInformation.Load(playlistID); ok {
|
||||||
|
|
||||||
|
var playlist = p.(Playlist)
|
||||||
|
var debug, path, options, bufferType string
|
||||||
|
var tmpSegment = 1
|
||||||
|
var bufferSize = Settings.BufferSize * 1024
|
||||||
|
var stream = playlist.Streams[streamID]
|
||||||
|
var buf bytes.Buffer
|
||||||
|
var fileSize = 0
|
||||||
|
var streamStatus = make(chan bool)
|
||||||
|
|
||||||
|
var tmpFolder = playlist.Streams[streamID].Folder
|
||||||
|
var url = playlist.Streams[streamID].URL
|
||||||
|
|
||||||
|
stream.Status = false
|
||||||
|
|
||||||
|
bufferType = strings.ToUpper(Settings.Buffer)
|
||||||
|
|
||||||
|
switch Settings.Buffer {
|
||||||
|
|
||||||
|
case "ffmpeg":
|
||||||
|
path = Settings.FFmpegPath
|
||||||
|
options = Settings.FFmpegOptions
|
||||||
|
|
||||||
|
case "vlc":
|
||||||
|
path = Settings.VLCPath
|
||||||
|
options = Settings.VLCOptions
|
||||||
|
|
||||||
|
default:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var addErrorToStream = func(err error) {
|
||||||
|
|
||||||
|
var stream = playlist.Streams[streamID]
|
||||||
|
|
||||||
|
if c, ok := BufferClients.Load(playlistID + stream.MD5); ok {
|
||||||
|
|
||||||
|
var clients = c.(ClientConnection)
|
||||||
|
clients.Error = err
|
||||||
|
BufferClients.Store(playlistID+stream.MD5, clients)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
os.RemoveAll(getPlatformPath(tmpFolder))
|
||||||
|
|
||||||
|
err := checkFolder(tmpFolder)
|
||||||
|
if err != nil {
|
||||||
|
ShowError(err, 0)
|
||||||
|
addErrorToStream(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = checkFile(path)
|
||||||
|
if err != nil {
|
||||||
|
ShowError(err, 0)
|
||||||
|
addErrorToStream(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
showInfo(fmt.Sprintf("%s path:%s", bufferType, path))
|
||||||
|
showInfo("Streaming URL:" + stream.URL)
|
||||||
|
|
||||||
|
var tmpFile = fmt.Sprintf("%s%d.ts", tmpFolder, tmpSegment)
|
||||||
|
|
||||||
|
f, err := os.Create(tmpFile)
|
||||||
|
f.Close()
|
||||||
|
if err != nil {
|
||||||
|
addErrorToStream(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//args = strings.Replace(args, "[USER-AGENT]", Settings.UserAgent, -1)
|
||||||
|
|
||||||
|
// User-Agent setzen
|
||||||
|
var args []string
|
||||||
|
|
||||||
|
for i, a := range strings.Split(options, " ") {
|
||||||
|
|
||||||
|
switch bufferType {
|
||||||
|
case "FFMPEG":
|
||||||
|
a = strings.Replace(a, "[URL]", url, -1)
|
||||||
|
if i == 0 {
|
||||||
|
args = []string{"-user-agent", Settings.UserAgent}
|
||||||
|
}
|
||||||
|
|
||||||
|
args = append(args, a)
|
||||||
|
|
||||||
|
case "VLC":
|
||||||
|
if a == "[URL]" {
|
||||||
|
a = strings.Replace(a, "[URL]", url, -1)
|
||||||
|
args = append(args, a)
|
||||||
|
args = append(args, fmt.Sprintf(":http-user-agent=%s", Settings.UserAgent))
|
||||||
|
|
||||||
|
} else {
|
||||||
|
args = append(args, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
var cmd = exec.Command(path, args...)
|
||||||
|
|
||||||
|
debug = fmt.Sprintf("%s:%s %s", bufferType, path, args)
|
||||||
|
showDebug(debug, 1)
|
||||||
|
|
||||||
|
// Byte-Daten vom Prozess
|
||||||
|
stdOut, err := cmd.StdoutPipe()
|
||||||
|
if err != nil {
|
||||||
|
ShowError(err, 0)
|
||||||
|
cmd.Process.Kill()
|
||||||
|
cmd.Wait()
|
||||||
|
addErrorToStream(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log-Daten vom Prozess
|
||||||
|
logOut, err := cmd.StderrPipe()
|
||||||
|
if err != nil {
|
||||||
|
ShowError(err, 0)
|
||||||
|
cmd.Process.Kill()
|
||||||
|
cmd.Wait()
|
||||||
|
addErrorToStream(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(buf.Bytes()) == 0 && stream.Status == false {
|
||||||
|
showInfo(bufferType + ":Processing data")
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.Start()
|
||||||
|
defer cmd.Wait()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
|
||||||
|
// Log Daten vom Prozess im Dubug Mode 1 anzeigen.
|
||||||
|
scanner := bufio.NewScanner(logOut)
|
||||||
|
scanner.Split(bufio.ScanLines)
|
||||||
|
|
||||||
|
for scanner.Scan() {
|
||||||
|
|
||||||
|
debug = fmt.Sprintf("%s log:%s", bufferType, strings.TrimSpace(scanner.Text()))
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-streamStatus:
|
||||||
|
showDebug(debug, 1)
|
||||||
|
default:
|
||||||
|
showInfo(debug)
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(time.Duration(10) * time.Millisecond)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}()
|
||||||
|
|
||||||
|
f, err = os.OpenFile(tmpFile, os.O_APPEND|os.O_WRONLY, 0600)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
buffer := make([]byte, 1024*4)
|
||||||
|
|
||||||
|
reader := bufio.NewReader(stdOut)
|
||||||
|
|
||||||
|
t := make(chan int)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
|
||||||
|
var timeout = 0
|
||||||
|
for {
|
||||||
|
time.Sleep(time.Duration(1000) * time.Millisecond)
|
||||||
|
timeout++
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-t:
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
t <- timeout
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}()
|
||||||
|
|
||||||
|
for {
|
||||||
|
|
||||||
|
select {
|
||||||
|
case timeout := <-t:
|
||||||
|
if timeout >= 20 && tmpSegment == 1 {
|
||||||
|
cmd.Process.Kill()
|
||||||
|
err = errors.New("Timout")
|
||||||
|
ShowError(err, 4006)
|
||||||
|
addErrorToStream(err)
|
||||||
|
cmd.Wait()
|
||||||
|
f.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if fileSize == 0 && stream.Status == false {
|
||||||
|
showInfo("Streaming Status:Receive data from " + bufferType)
|
||||||
|
}
|
||||||
|
|
||||||
|
if clientConnection(stream) == false {
|
||||||
|
cmd.Process.Kill()
|
||||||
|
f.Close()
|
||||||
|
cmd.Wait()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
n, err := reader.Read(buffer)
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
fileSize = fileSize + len(buffer[:n])
|
||||||
|
|
||||||
|
if _, err := f.Write(buffer[:n]); err != nil {
|
||||||
|
cmd.Process.Kill()
|
||||||
|
ShowError(err, 0)
|
||||||
|
addErrorToStream(err)
|
||||||
|
cmd.Wait()
|
||||||
|
f.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if fileSize >= bufferSize/2 {
|
||||||
|
|
||||||
|
if tmpSegment == 1 && stream.Status == false {
|
||||||
|
close(t)
|
||||||
|
close(streamStatus)
|
||||||
|
showInfo(fmt.Sprintf("Streaming Status:Buffering data from %s", bufferType))
|
||||||
|
}
|
||||||
|
|
||||||
|
f.Close()
|
||||||
|
tmpSegment++
|
||||||
|
|
||||||
|
if stream.Status == false {
|
||||||
|
Lock.Lock()
|
||||||
|
stream.Status = true
|
||||||
|
playlist.Streams[streamID] = stream
|
||||||
|
BufferInformation.Store(playlistID, playlist)
|
||||||
|
Lock.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpFile = fmt.Sprintf("%s%d.ts", tmpFolder, tmpSegment)
|
||||||
|
|
||||||
|
fileSize = 0
|
||||||
|
|
||||||
|
var errCreate, errOpen error
|
||||||
|
f, errCreate = os.Create(tmpFile)
|
||||||
|
f, errOpen = os.OpenFile(tmpFile, os.O_APPEND|os.O_WRONLY, 0600)
|
||||||
|
if errCreate != nil || errOpen != nil {
|
||||||
|
cmd.Process.Kill()
|
||||||
|
ShowError(err, 0)
|
||||||
|
addErrorToStream(err)
|
||||||
|
cmd.Wait()
|
||||||
|
f.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.Process.Kill()
|
||||||
|
cmd.Wait()
|
||||||
|
|
||||||
|
err = errors.New(bufferType + " error")
|
||||||
|
addErrorToStream(err)
|
||||||
|
ShowError(err, 1204)
|
||||||
|
|
||||||
|
time.Sleep(time.Duration(500) * time.Millisecond)
|
||||||
|
clientConnection(stream)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func getTuner(id, playlistType string) (tuner int) {
|
func getTuner(id, playlistType string) (tuner int) {
|
||||||
|
|
||||||
switch Settings.Buffer {
|
switch Settings.Buffer {
|
||||||
|
|
||||||
case false:
|
case "-":
|
||||||
tuner = Settings.Tuner
|
tuner = Settings.Tuner
|
||||||
|
|
||||||
case true:
|
case "xteve", "ffmpeg", "vlc":
|
||||||
|
|
||||||
i, err := strconv.Atoi(getProviderParameter(id, playlistType, "tuner"))
|
i, err := strconv.Atoi(getProviderParameter(id, playlistType, "tuner"))
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
|||||||
@@ -29,6 +29,9 @@ var BufferInformation sync.Map
|
|||||||
// BufferClients : Anzahl der Clients die einen Stream über den Buffer abspielen
|
// BufferClients : Anzahl der Clients die einen Stream über den Buffer abspielen
|
||||||
var BufferClients sync.Map
|
var BufferClients sync.Map
|
||||||
|
|
||||||
|
// Lock : Lock Map
|
||||||
|
var Lock = sync.RWMutex{}
|
||||||
|
|
||||||
// Init : Systeminitialisierung
|
// Init : Systeminitialisierung
|
||||||
func Init() (err error) {
|
func Init() (err error) {
|
||||||
|
|
||||||
@@ -46,6 +49,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
|
||||||
|
|
||||||
@@ -133,7 +140,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
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,6 +223,8 @@ func StartSystem(updateProviderFiles bool) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setURLScheme()
|
||||||
|
|
||||||
// Systeminformationen in der Konsole ausgeben
|
// Systeminformationen in der Konsole ausgeben
|
||||||
showInfo(fmt.Sprintf("UUID:%s", Settings.UUID))
|
showInfo(fmt.Sprintf("UUID:%s", Settings.UUID))
|
||||||
showInfo(fmt.Sprintf("Tuner (Plex / Emby):%d", Settings.Tuner))
|
showInfo(fmt.Sprintf("Tuner (Plex / Emby):%d", Settings.Tuner))
|
||||||
|
|||||||
65
src/data.go
65
src/data.go
@@ -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 {
|
||||||
@@ -94,6 +96,20 @@ 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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
case "scheme.m3u", "scheme.xml":
|
||||||
|
createXEPGFiles = true
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
oldSettings[key] = value
|
oldSettings[key] = value
|
||||||
@@ -128,6 +144,8 @@ func updateServerSettings(request RequestStruct) (settings SettingsStrcut, err e
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setURLScheme()
|
||||||
|
|
||||||
if Settings.AuthenticationWEB == false {
|
if Settings.AuthenticationWEB == false {
|
||||||
|
|
||||||
Settings.AuthenticationAPI = false
|
Settings.AuthenticationAPI = false
|
||||||
@@ -138,6 +156,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 {
|
||||||
|
|
||||||
@@ -463,9 +508,21 @@ func saveXEpgMapping(request RequestStruct) (err error) {
|
|||||||
|
|
||||||
if System.ScanInProgress == 0 {
|
if System.ScanInProgress == 0 {
|
||||||
|
|
||||||
|
System.ScanInProgress = 1
|
||||||
cleanupXEPG()
|
cleanupXEPG()
|
||||||
buildXEPG(true)
|
buildXEPG(true)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
|
||||||
|
createXMLTVFile()
|
||||||
|
createM3UFile()
|
||||||
|
showInfo("XEPG:" + fmt.Sprintf("Ready to use"))
|
||||||
|
go cachingImages()
|
||||||
|
|
||||||
|
System.ScanInProgress = 0
|
||||||
|
|
||||||
|
}()
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// Wenn während des erstellen der Datanbank das Mapping erneut gespeichert wird, wird die Datenbank erst später erneut aktualisiert.
|
// Wenn während des erstellen der Datanbank das Mapping erneut gespeichert wird, wird die Datenbank erst später erneut aktualisiert.
|
||||||
@@ -485,8 +542,16 @@ func saveXEpgMapping(request RequestStruct) (err error) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
System.ScanInProgress = 1
|
||||||
|
|
||||||
cleanupXEPG()
|
cleanupXEPG()
|
||||||
buildXEPG(false)
|
buildXEPG(false)
|
||||||
|
createXMLTVFile()
|
||||||
|
createM3UFile()
|
||||||
|
showInfo("XEPG:" + fmt.Sprintf("Ready to use"))
|
||||||
|
go cachingImages()
|
||||||
|
|
||||||
|
System.ScanInProgress = 0
|
||||||
|
|
||||||
System.BackgroundProcess = false
|
System.BackgroundProcess = false
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ func getCacheImageURL(imageURL string) (cacheImageURL string) {
|
|||||||
|
|
||||||
if indexOfString(urlMD5+fileExtension, Data.Cache.ImagesCache) != -1 {
|
if indexOfString(urlMD5+fileExtension, Data.Cache.ImagesCache) != -1 {
|
||||||
|
|
||||||
cacheImageURL = fmt.Sprintf("%s://%s/images/%s%s", System.ServerProtocol.WEB, System.Domain, urlMD5, fileExtension)
|
cacheImageURL = fmt.Sprintf("%s://%s/images/%s%s", System.ServerProtocol.XML, System.Domain, urlMD5, fileExtension)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
@@ -163,7 +163,7 @@ func uploadLogo(input, filename string) (logoURL string, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
logoURL = fmt.Sprintf("%s://%s/data_images/%s", System.ServerProtocol.WEB, System.Domain, filename)
|
logoURL = fmt.Sprintf("%s://%s/data_images/%s", System.ServerProtocol.XML, System.Domain, filename)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ func ShowSystemInfo() {
|
|||||||
println("---")
|
println("---")
|
||||||
|
|
||||||
fmt.Println("Settings [Streaming]")
|
fmt.Println("Settings [Streaming]")
|
||||||
fmt.Println(fmt.Sprintf("Buffer: %t", Settings.Buffer))
|
fmt.Println(fmt.Sprintf("Buffer: %s", Settings.Buffer))
|
||||||
fmt.Println(fmt.Sprintf("Buffer Size: %d KB", Settings.BufferSize))
|
fmt.Println(fmt.Sprintf("Buffer Size: %d KB", Settings.BufferSize))
|
||||||
fmt.Println(fmt.Sprintf("Timeout: %d ms", int(Settings.BufferTimeout)))
|
fmt.Println(fmt.Sprintf("Timeout: %d ms", int(Settings.BufferTimeout)))
|
||||||
fmt.Println(fmt.Sprintf("User Agent: %s", Settings.UserAgent))
|
fmt.Println(fmt.Sprintf("User Agent: %s", Settings.UserAgent))
|
||||||
|
|||||||
@@ -254,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.")
|
||||||
|
|
||||||
@@ -268,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:
|
||||||
@@ -292,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:
|
||||||
@@ -308,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")
|
||||||
@@ -347,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:
|
||||||
@@ -370,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")
|
||||||
|
|
||||||
|
|||||||
@@ -108,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)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
|||||||
@@ -22,6 +22,16 @@ type SystemStruct struct {
|
|||||||
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
|
||||||
@@ -177,6 +187,7 @@ type XEPGChannelStruct struct {
|
|||||||
XName string `json:"x-name,required"`
|
XName string `json:"x-name,required"`
|
||||||
XUpdateChannelIcon bool `json:"x-update-channel-icon,required"`
|
XUpdateChannelIcon bool `json:"x-update-channel-icon,required"`
|
||||||
XUpdateChannelName bool `json:"x-update-channel-name,required"`
|
XUpdateChannelName bool `json:"x-update-channel-name,required"`
|
||||||
|
XDescription string `json:"x-description,required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// M3UChannelStructXEPG : M3U Struktur für XEPG
|
// M3UChannelStructXEPG : M3U Struktur für XEPG
|
||||||
@@ -242,11 +253,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)
|
||||||
|
|
||||||
@@ -265,6 +280,8 @@ type SettingsStrcut struct {
|
|||||||
MappingFirstChannel float64 `json:"mapping.first.channel"`
|
MappingFirstChannel float64 `json:"mapping.first.channel"`
|
||||||
Port string `json:"port"`
|
Port string `json:"port"`
|
||||||
SSDP bool `json:"ssdp"`
|
SSDP bool `json:"ssdp"`
|
||||||
|
SchemeM3U string `json:"scheme.m3u"`
|
||||||
|
SchemeXML string `json:"scheme.xml"`
|
||||||
TempPath string `json:"temp.path"`
|
TempPath string `json:"temp.path"`
|
||||||
Tuner int `json:"tuner"`
|
Tuner int `json:"tuner"`
|
||||||
Update []string `json:"update"`
|
Update []string `json:"update"`
|
||||||
|
|||||||
@@ -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"`
|
||||||
@@ -37,6 +41,8 @@ type RequestStruct struct {
|
|||||||
UserAgent *string `json:"user.agent,omitempty"`
|
UserAgent *string `json:"user.agent,omitempty"`
|
||||||
XepgReplaceMissingImages *bool `json:"xepg.replace.missing.images,omitempty"`
|
XepgReplaceMissingImages *bool `json:"xepg.replace.missing.images,omitempty"`
|
||||||
XteveAutoUpdate *bool `json:"xteveAutoUpdate,omitempty"`
|
XteveAutoUpdate *bool `json:"xteveAutoUpdate,omitempty"`
|
||||||
|
SchemeM3U *string `json:"scheme.m3u,omitempty"`
|
||||||
|
SchemeXML *string `json:"scheme.xml,omitempty"`
|
||||||
} `json:"settings,omitempty"`
|
} `json:"settings,omitempty"`
|
||||||
|
|
||||||
// Upload Logo
|
// Upload Logo
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ type Program struct {
|
|||||||
PreviouslyShown *PreviouslyShown `xml:"previously-shown"`
|
PreviouslyShown *PreviouslyShown `xml:"previously-shown"`
|
||||||
New *New `xml:"new"`
|
New *New `xml:"new"`
|
||||||
Live *Live `xml:"live"`
|
Live *Live `xml:"live"`
|
||||||
|
Premiere *Live `xml:"premiere"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Title : Programmtitel
|
// Title : Programmtitel
|
||||||
|
|||||||
@@ -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"] = "PMS"
|
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{})
|
||||||
@@ -136,6 +138,8 @@ func loadSettings() (settings SettingsStrcut, err error) {
|
|||||||
defaults["version"] = System.DBVersion
|
defaults["version"] = System.DBVersion
|
||||||
defaults["xteveAutoUpdate"] = true
|
defaults["xteveAutoUpdate"] = true
|
||||||
defaults["temp.path"] = System.Folder.Temp
|
defaults["temp.path"] = System.Folder.Temp
|
||||||
|
defaults["scheme.M3U"] = "HTTP"
|
||||||
|
defaults["scheme.XML"] = "HTTP"
|
||||||
|
|
||||||
// Default Werte setzen
|
// Default Werte setzen
|
||||||
for key, value := range defaults {
|
for key, value := range defaults {
|
||||||
@@ -159,10 +163,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
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,6 +254,14 @@ func setGlobalDomain(domain string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setURLScheme() {
|
||||||
|
|
||||||
|
System.ServerProtocol.M3U = strings.ToLower(Settings.SchemeM3U)
|
||||||
|
System.ServerProtocol.XML = strings.ToLower(Settings.SchemeXML)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// UUID generieren
|
// UUID generieren
|
||||||
func createUUID() (uuid string) {
|
func createUUID() (uuid string) {
|
||||||
uuid = time.Now().Format("2006-01") + "-" + randomString(4) + "-" + randomString(6)
|
uuid = time.Now().Format("2006-01") + "-" + randomString(4) + "-" + randomString(6)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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))
|
||||||
@@ -204,10 +211,37 @@ checkVersion:
|
|||||||
}
|
}
|
||||||
|
|
||||||
case "2.0.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 {
|
||||||
|
|||||||
74
src/webUI.go
74
src/webUI.go
File diff suppressed because one or more lines are too long
@@ -129,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 [%s]", 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))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,16 +163,16 @@ 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.")
|
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)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
@@ -305,10 +316,12 @@ func WS(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
var newToken string
|
var newToken string
|
||||||
|
|
||||||
if r.Header.Get("Origin") != "http://"+r.Host {
|
/*
|
||||||
httpStatusError(w, r, 403)
|
if r.Header.Get("Origin") != "http://"+r.Host {
|
||||||
return
|
httpStatusError(w, r, 403)
|
||||||
}
|
return
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
conn, err := websocket.Upgrade(w, r, w.Header(), 1024, 1024)
|
conn, err := websocket.Upgrade(w, r, w.Header(), 1024, 1024)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
10
src/xepg.go
10
src/xepg.go
@@ -717,6 +717,9 @@ func getProgramData(xepgChannel XEPGChannelStruct) (xepgXML XMLTV, err error) {
|
|||||||
// Live
|
// Live
|
||||||
program.Live = xmltvProgram.Live
|
program.Live = xmltvProgram.Live
|
||||||
|
|
||||||
|
// Premiere
|
||||||
|
program.Premiere = xmltvProgram.Premiere
|
||||||
|
|
||||||
xepgXML.Program = append(xepgXML.Program, program)
|
xepgXML.Program = append(xepgXML.Program, program)
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -759,7 +762,12 @@ func createDummyProgram(xepgChannel XEPGChannelStruct) (dummyXMLTV XMLTV) {
|
|||||||
epg.Start = epgStartTime.Format("20060102150405") + offset
|
epg.Start = epgStartTime.Format("20060102150405") + offset
|
||||||
epg.Stop = epgStopTime.Format("20060102150405") + offset
|
epg.Stop = epgStopTime.Format("20060102150405") + offset
|
||||||
epg.Title = append(epg.Title, &Title{Value: xepgChannel.XName + " (" + epgStartTime.Weekday().String()[0:2] + ". " + epgStartTime.Format("15:04") + " - " + epgStopTime.Format("15:04") + ")", Lang: "en"})
|
epg.Title = append(epg.Title, &Title{Value: xepgChannel.XName + " (" + epgStartTime.Weekday().String()[0:2] + ". " + epgStartTime.Format("15:04") + " - " + epgStopTime.Format("15:04") + ")", Lang: "en"})
|
||||||
epg.Desc = append(epg.Desc, &Desc{Value: "xTeVe: (" + strconv.Itoa(dummyLength) + " Minutes) " + epgStartTime.Weekday().String() + " " + epgStartTime.Format("15:04") + " - " + epgStopTime.Format("15:04"), Lang: "en"})
|
|
||||||
|
if len(xepgChannel.XDescription) == 0 {
|
||||||
|
epg.Desc = append(epg.Desc, &Desc{Value: "xTeVe: (" + strconv.Itoa(dummyLength) + " Minutes) " + epgStartTime.Weekday().String() + " " + epgStartTime.Format("15:04") + " - " + epgStopTime.Format("15:04"), Lang: "en"})
|
||||||
|
} else {
|
||||||
|
epg.Desc = append(epg.Desc, &Desc{Value: xepgChannel.XDescription, Lang: "en"})
|
||||||
|
}
|
||||||
|
|
||||||
if Settings.XepgReplaceMissingImages == true {
|
if Settings.XepgReplaceMissingImages == true {
|
||||||
poster.Src = getCacheImageURL(xepgChannel.TvgLogo)
|
poster.Src = getCacheImageURL(xepgChannel.TvgLogo)
|
||||||
|
|||||||
@@ -21,9 +21,9 @@ menuItems.push(new MainMenuItem("logout", "{{.mainMenu.item.logout}}", "logout.p
|
|||||||
|
|
||||||
// Kategorien für die Einstellungen
|
// Kategorien für die Einstellungen
|
||||||
var settingsCategory = new Array()
|
var settingsCategory = new Array()
|
||||||
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.general}}", "xteveAutoUpdate,tuner,epgSource,api"))
|
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.general}}", "xteveAutoUpdate,tuner,epgSource,api,scheme.m3u,scheme.xml"))
|
||||||
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"))
|
||||||
|
|
||||||
|
|||||||
@@ -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 = "-"
|
||||||
@@ -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()
|
||||||
|
|
||||||
@@ -1532,6 +1532,13 @@ function openPopUp(dataType, element) {
|
|||||||
|
|
||||||
content.description(data["name"])
|
content.description(data["name"])
|
||||||
|
|
||||||
|
// Beschreibung
|
||||||
|
var dbKey:string = "x-description"
|
||||||
|
var input = content.createInput("text", dbKey, data[dbKey])
|
||||||
|
input.setAttribute("placeholder", "{{.mapping.description.placeholder}}")
|
||||||
|
input.setAttribute("onchange", "javascript: this.className = 'changed'")
|
||||||
|
content.appendRow("{{.mapping.description.title}}", input)
|
||||||
|
|
||||||
// Aktualisierung des Kanalnamens
|
// Aktualisierung des Kanalnamens
|
||||||
if (data.hasOwnProperty("_uuid.key")) {
|
if (data.hasOwnProperty("_uuid.key")) {
|
||||||
if (data["_uuid.key"] != "") {
|
if (data["_uuid.key"] != "") {
|
||||||
|
|||||||
@@ -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,54 @@ 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
|
||||||
|
|
||||||
|
case "scheme.m3u":
|
||||||
|
var tdLeft = document.createElement("TD")
|
||||||
|
tdLeft.innerHTML = "{{.settings.schemeM3U.title}}" + ":"
|
||||||
|
|
||||||
|
var tdRight = document.createElement("TD")
|
||||||
|
var text:any[] = ["HTTP", "HTTPS"]
|
||||||
|
var values:any[] = ["HTTP", "HTTPS"]
|
||||||
|
|
||||||
|
var select = content.createSelect(text, values, data, settingsKey)
|
||||||
|
select.setAttribute("onchange", "javascript: this.className = 'changed'")
|
||||||
|
tdRight.appendChild(select)
|
||||||
|
|
||||||
|
setting.appendChild(tdLeft)
|
||||||
|
setting.appendChild(tdRight)
|
||||||
|
break
|
||||||
|
|
||||||
|
case "scheme.xml":
|
||||||
|
var tdLeft = document.createElement("TD")
|
||||||
|
tdLeft.innerHTML = "{{.settings.schemeXML.title}}" + ":"
|
||||||
|
|
||||||
|
var tdRight = document.createElement("TD")
|
||||||
|
var text:any[] = ["HTTP", "HTTPS"]
|
||||||
|
var values:any[] = ["HTTP", "HTTPS"]
|
||||||
|
|
||||||
|
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 +471,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
|
||||||
@@ -409,6 +515,15 @@ class SettingsCategory {
|
|||||||
text = "{{.settings.replaceEmptyImages.description}}"
|
text = "{{.settings.replaceEmptyImages.description}}"
|
||||||
break
|
break
|
||||||
|
|
||||||
|
case "scheme.m3u":
|
||||||
|
text = "{{.settings.schemeM3U.description}}"
|
||||||
|
break
|
||||||
|
|
||||||
|
case "scheme.xml":
|
||||||
|
text = "{{.settings.schemeXML.description}}"
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
text = ""
|
text = ""
|
||||||
break
|
break
|
||||||
|
|||||||
6
xteve.go
6
xteve.go
@@ -35,14 +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.3.0030"
|
const Version = "2.1.1.0111"
|
||||||
|
|
||||||
// DBVersion : Datanbank Version
|
// DBVersion : Datanbank Version
|
||||||
const DBVersion = "2.0.0"
|
const DBVersion = "2.1.0"
|
||||||
|
|
||||||
// APIVersion : API Version
|
// APIVersion : API Version
|
||||||
const APIVersion = "1.1.0"
|
const APIVersion = "1.1.0"
|
||||||
|
|||||||
Reference in New Issue
Block a user