23 Commits

Author SHA1 Message Date
marmei
03e1abbe90 v2.1.1.0110 2019-12-13 20:31:23 +01:00
marmei
469581e280 Add mapping desc. function 2019-12-13 19:01:18 +01:00
marmei
019c98996a Update changelog-beta.md 2019-12-08 2019-12-08 17:49:50 +01:00
marmei
36db927794 v2.1.0.0106 2019-12-08 17:47:54 +01:00
marmei
f0a49788cc User-Agent for FFmpeg and VLC 2019-12-08 17:33:46 +01:00
marmei
72767d7dbd Update changelog-beta.md 2019-12-06 2019-12-06 21:00:44 +01:00
marmei
8eecbf2b78 Update changelog-beta.md 2019-12-06 2019-12-06 20:57:26 +01:00
marmei
1a1e37fe15 v2.1.0.0105: Settings for URI scheme 2019-12-06 20:48:59 +01:00
marmei
08f6fb60e3 Fix log entry, wrong buffer value 2019-11-15 10:09:19 +01:00
marmei
eded490ac7 Update changelog-beta.md 2019-11-06 2019-11-06 18:16:14 +01:00
marmei
ed770b9dbc Merge remote-tracking branch 'origin/master' 2019-11-06 18:11:21 +01:00
marmei
6129b4911a Merge pull request #53 from taeram/patch-1
Small spelling / grammar fixes.
2019-11-05 20:29:12 +01:00
Jesse Patching
477c5f30c1 Small spelling / grammar fixes. 2019-11-04 20:28:45 -07:00
marmei
65ddc6f301 v2.1.2.0101-beta 2019-11-04 18:28:41 +01:00
marmei
3ef95c1950 RV Proxy 2019-11-04 18:23:20 +01:00
marmei
3a3798cd2d WiteHeader 2019-10-23 19:02:54 +02:00
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
dc42afcd05 New beta 2019-10-04 19:39:54 +02:00
marmei
20e5e1b545 Buffer RTSP performance 2019-10-04 19:39:20 +02:00
marmei
7c87d1d5bd Wrong CVLC path 2019-09-27 22:20:19 +02:00
marmei
ad992eb615 Add FFmpeg and VLC support 2019-09-27 19:24:31 +02:00
26 changed files with 1113 additions and 132 deletions

View File

@@ -31,7 +31,7 @@ Documentation for setup and configuration is [here](https://github.com/xteve-pro
* Merge external M3U files
* Merge external XMLTV files
* Automatic M3U and XMLTV update
* M3U und XMLTV export
* M3U and XMLTV export
#### Channel management
* Filtering streams
@@ -84,7 +84,7 @@ 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.
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.**

View File

@@ -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
**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

View File

@@ -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}}"));
// Kategorien für die Einstellungen
var settingsCategory = new Array();
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.general}}", "xteveAutoUpdate,tuner,epgSource,api"));
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.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.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

@@ -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 {
@@ -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++) {
@@ -1253,6 +1253,12 @@ function openPopUp(dataType, element) {
}
content.appendRow("{{.mapping.channelName.title}}", input);
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
if (data.hasOwnProperty("_uuid.key")) {
if (data["_uuid.key"] != "") {

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,42 @@ 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;
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;
};
@@ -308,6 +377,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;
@@ -329,6 +410,12 @@ var SettingsCategory = /** @class */ (function () {
case "xepg.replace.missing.images":
text = "{{.settings.replaceEmptyImages.description}}";
break;
case "scheme.m3u":
text = "{{.settings.schemeM3U.description}}";
break;
case "scheme.xml":
text = "{{.settings.schemeXML.description}}";
break;
default:
text = "";
break;

View File

@@ -190,6 +190,11 @@
"placeholder": "",
"description": ""
},
"description": {
"title": "Channel Description",
"placeholder": "Used by the Dummy as an XML description",
"description": ""
},
"updateChannelName": {
"title": "Update Channel Name",
"placeholder": "",
@@ -302,12 +307,20 @@
"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."
},
"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": {
"title": "Updates all files at startup",
"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": {
@@ -320,7 +333,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<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": {
"title": "Buffer Size",
@@ -332,8 +370,8 @@
"placeholder": "100"
},
"userAgent": {
"title": "User agent",
"description": "User Agent for HTTP requests",
"title": "User Agent",
"description": "User Agent for HTTP requests. For every HTTP connection, this value is used for the user agent. Should only be changed if xTeVe is blocked.",
"placeholder": "xTeVe"
},
"backupPath": {

View File

@@ -6,6 +6,8 @@ package src
*/
import (
"bufio"
"bytes"
"errors"
"fmt"
"io"
@@ -13,6 +15,7 @@ import (
"net/http"
"net/url"
"os"
"os/exec"
"path"
"sort"
"strconv"
@@ -205,7 +208,17 @@ func bufferingStream(playlistID, streamingURL, channelName string, w http.Respon
playlist.Streams[streamID] = stream
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))
@@ -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
@@ -248,7 +261,9 @@ func bufferingStream(playlistID, streamingURL, channelName string, w http.Respon
var oldSegments []string
for { // Loop 2: Temporäre Datein sind vorhanden, Daten können zum Client gesendet werden
// HTTP Clientverbindung überwachen
cn, ok := w.(http.CloseNotifier)
if ok {
@@ -317,10 +332,10 @@ func bufferingStream(playlistID, streamingURL, channelName string, w http.Respon
if streaming == false {
contentType := http.DetectContentType(buffer)
_ = contentType
contentType := http.DetectContentType(buffer) + "; name=stream.ts"
//_ = contentType
//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("Connection", "close")
@@ -333,6 +348,7 @@ func bufferingStream(playlistID, streamingURL, channelName string, w http.Respon
w.Header().Set("transferMode.dlna.org", "Streaming")
*/
w.WriteHeader(200)
_, err := w.Write(buffer)
if err != nil {
@@ -400,7 +416,7 @@ func getTmpFiles(stream *ThisStream) (tmpFiles []string) {
return
}
if len(files) > 1 {
if len(files) > 2 {
for _, file := range files {
@@ -436,6 +452,9 @@ func getTmpFiles(stream *ThisStream) (tmpFiles []string) {
func killClientConnection(streamID int, playlistID string, force bool) {
Lock.Lock()
defer Lock.Unlock()
if p, ok := BufferInformation.Load(playlistID); ok {
var playlist = p.(Playlist)
@@ -460,6 +479,7 @@ func killClientConnection(streamID int, playlistID string, force bool) {
if clients.Connection <= 0 {
BufferClients.Delete(playlistID + stream.MD5)
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) {
status = true
Lock.Lock()
defer Lock.Unlock()
if _, ok := BufferClients.Load(stream.PlaylistID + stream.MD5); !ok {
@@ -526,6 +548,9 @@ func connectToStreamingServer(streamID int, playlistID string) {
var m3u8Segments []string
var bandwidth BandwidthCalculation
var networkBandwidth = Settings.M3U8AdaptiveBandwidthMBPS * 1e+6
// Größe des Buffers
var bufferSize = Settings.BufferSize
var buffer = make([]byte, 1024*bufferSize*2)
var defaultSegment = func() {
@@ -805,8 +830,8 @@ func connectToStreamingServer(streamID int, playlistID string) {
var fileSize int
// Größe des Buffers
buffer := make([]byte, 1024*Settings.BufferSize*2)
var tmpFileSize = 1024 * Settings.BufferSize * 1
buffer = make([]byte, 1024*bufferSize*2)
var tmpFileSize = 1024 * bufferSize * 1
debug = fmt.Sprintf("Buffer Size:%d KB [SERVER CONNECTION]", len(buffer)/1024)
showDebug(debug, 3)
@@ -882,7 +907,9 @@ func connectToStreamingServer(streamID int, playlistID string) {
}
// Buffer auf die Festplatte speichern
if fileSize >= tmpFileSize || n == 0 {
if fileSize >= tmpFileSize/2 || n == 0 {
Lock.Lock()
bandwidth.Stop = time.Now()
bandwidth.Size += fileSize
@@ -902,6 +929,7 @@ func connectToStreamingServer(streamID int, playlistID string) {
stream.Status = true
playlist.Streams[streamID] = stream
BufferInformation.Store(playlistID, playlist)
Lock.Unlock()
tmpSegment++
@@ -928,6 +956,7 @@ func connectToStreamingServer(streamID int, playlistID string) {
}
fileSize = 0
buffer = make([]byte, 1024*bufferSize*2)
if n == 0 {
bufferFile.Close()
@@ -1255,12 +1284,6 @@ func parseM3U8(stream *ThisStream) (err error) {
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
}
// 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) {
switch Settings.Buffer {
case false:
case "-":
tuner = Settings.Tuner
case true:
case "xteve", "ffmpeg", "vlc":
i, err := strconv.Atoi(getProviderParameter(id, playlistType, "tuner"))
if err == nil {

View File

@@ -29,6 +29,9 @@ var BufferInformation sync.Map
// BufferClients : Anzahl der Clients die einen Stream über den Buffer abspielen
var BufferClients sync.Map
// Lock : Lock Map
var Lock = sync.RWMutex{}
// Init : Systeminitialisierung
func Init() (err error) {
@@ -46,6 +49,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
@@ -133,7 +140,6 @@ func Init() (err error) {
// Bedingte Update Änderungen durchführen
err = conditionalUpdateChanges()
if err != nil {
ShowError(err, 0)
return
}
@@ -217,6 +223,8 @@ func StartSystem(updateProviderFiles bool) (err error) {
return
}
setURLScheme()
// Systeminformationen in der Konsole ausgeben
showInfo(fmt.Sprintf("UUID:%s", Settings.UUID))
showInfo(fmt.Sprintf("Tuner (Plex / Emby):%d", Settings.Tuner))

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 {
@@ -94,6 +96,20 @@ 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
}
}
case "scheme.m3u", "scheme.xml":
createXEPGFiles = true
}
oldSettings[key] = value
@@ -128,6 +144,8 @@ func updateServerSettings(request RequestStruct) (settings SettingsStrcut, err e
return
}
setURLScheme()
if Settings.AuthenticationWEB == 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)
if err == nil {
@@ -463,9 +508,21 @@ func saveXEpgMapping(request RequestStruct) (err error) {
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.
@@ -485,8 +542,16 @@ func saveXEpgMapping(request RequestStruct) (err error) {
}
System.ScanInProgress = 1
cleanupXEPG()
buildXEPG(false)
createXMLTVFile()
createM3UFile()
showInfo("XEPG:" + fmt.Sprintf("Ready to use"))
go cachingImages()
System.ScanInProgress = 0
System.BackgroundProcess = false

View File

@@ -45,7 +45,7 @@ func getCacheImageURL(imageURL string) (cacheImageURL string) {
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 {
@@ -163,7 +163,7 @@ func uploadLogo(input, filename string) (logoURL string, err error) {
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

View File

@@ -85,7 +85,7 @@ func ShowSystemInfo() {
println("---")
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("Timeout: %d ms", int(Settings.BufferTimeout)))
fmt.Println(fmt.Sprintf("User Agent: %s", Settings.UserAgent))

View File

@@ -254,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.")
@@ -268,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:
@@ -292,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:
@@ -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.")
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")
@@ -347,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:
@@ -370,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

@@ -108,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

@@ -22,6 +22,16 @@ type SystemStruct struct {
Domain string
DVRLimit int
FFmpeg struct {
DefaultOptions string
Path string
}
VLC struct {
DefaultOptions string
Path string
}
File struct {
Authentication string
M3U string
@@ -177,6 +187,7 @@ type XEPGChannelStruct struct {
XName string `json:"x-name,required"`
XUpdateChannelIcon bool `json:"x-update-channel-icon,required"`
XUpdateChannelName bool `json:"x-update-channel-name,required"`
XDescription string `json:"x-description,required"`
}
// M3UChannelStructXEPG : M3U Struktur für XEPG
@@ -242,11 +253,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)
@@ -265,6 +280,8 @@ type SettingsStrcut struct {
MappingFirstChannel float64 `json:"mapping.first.channel"`
Port string `json:"port"`
SSDP bool `json:"ssdp"`
SchemeM3U string `json:"scheme.m3u"`
SchemeXML string `json:"scheme.xml"`
TempPath string `json:"temp.path"`
Tuner int `json:"tuner"`
Update []string `json:"update"`

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"`
@@ -37,6 +41,8 @@ type RequestStruct struct {
UserAgent *string `json:"user.agent,omitempty"`
XepgReplaceMissingImages *bool `json:"xepg.replace.missing.images,omitempty"`
XteveAutoUpdate *bool `json:"xteveAutoUpdate,omitempty"`
SchemeM3U *string `json:"scheme.m3u,omitempty"`
SchemeXML *string `json:"scheme.xml,omitempty"`
} `json:"settings,omitempty"`
// Upload Logo

View File

@@ -48,6 +48,7 @@ type Program struct {
PreviouslyShown *PreviouslyShown `xml:"previously-shown"`
New *New `xml:"new"`
Live *Live `xml:"live"`
Premiere *Live `xml:"premiere"`
}
// Title : Programmtitel

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"] = "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{})
@@ -136,6 +138,8 @@ func loadSettings() (settings SettingsStrcut, err error) {
defaults["version"] = System.DBVersion
defaults["xteveAutoUpdate"] = true
defaults["temp.path"] = System.Folder.Temp
defaults["scheme.M3U"] = "HTTP"
defaults["scheme.XML"] = "HTTP"
// Default Werte setzen
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))
}
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
}
@@ -233,6 +254,14 @@ func setGlobalDomain(domain string) {
return
}
func setURLScheme() {
System.ServerProtocol.M3U = strings.ToLower(Settings.SchemeM3U)
System.ServerProtocol.XML = strings.ToLower(Settings.SchemeXML)
return
}
// UUID generieren
func createUUID() (uuid string) {
uuid = time.Now().Format("2006-01") + "-" + randomString(4) + "-" + randomString(6)

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))
@@ -204,10 +211,37 @@ checkVersion:
}
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

@@ -129,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 [%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 == true {
if Settings.Buffer != "-" {
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
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:xTeVe is no longer involved, the client connects directly to the streaming server.")
default:
bufferingStream(streamInfo.PlaylistID, streamInfo.URL, streamInfo.Name, w, r)
}
return
@@ -305,10 +316,12 @@ func WS(w http.ResponseWriter, r *http.Request) {
var newToken string
if r.Header.Get("Origin") != "http://"+r.Host {
httpStatusError(w, r, 403)
return
}
/*
if r.Header.Get("Origin") != "http://"+r.Host {
httpStatusError(w, r, 403)
return
}
*/
conn, err := websocket.Upgrade(w, r, w.Header(), 1024, 1024)
if err != nil {

View File

@@ -717,6 +717,9 @@ func getProgramData(xepgChannel XEPGChannelStruct) (xepgXML XMLTV, err error) {
// Live
program.Live = xmltvProgram.Live
// Premiere
program.Premiere = xmltvProgram.Premiere
xepgXML.Program = append(xepgXML.Program, program)
}
@@ -759,7 +762,12 @@ func createDummyProgram(xepgChannel XEPGChannelStruct) (dummyXMLTV XMLTV) {
epg.Start = epgStartTime.Format("20060102150405") + offset
epg.Stop = epgStopTime.Format("20060102150405") + offset
epg.Title = append(epg.Title, &Title{Value: xepgChannel.XName + " (" + epgStartTime.Weekday().String()[0:2] + ". " + epgStartTime.Format("15:04") + " - " + epgStopTime.Format("15:04") + ")", Lang: "en"})
epg.Desc = append(epg.Desc, &Desc{Value: "xTeVe: (" + strconv.Itoa(dummyLength) + " Minutes) " + epgStartTime.Weekday().String() + " " + epgStartTime.Format("15:04") + " - " + epgStopTime.Format("15:04"), Lang: "en"})
if 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 {
poster.Src = getCacheImageURL(xepgChannel.TvgLogo)

View File

@@ -21,9 +21,9 @@ menuItems.push(new MainMenuItem("logout", "{{.mainMenu.item.logout}}", "logout.p
// Kategorien für die Einstellungen
var settingsCategory = new Array()
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.general}}", "xteveAutoUpdate,tuner,epgSource,api"))
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.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.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

@@ -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 = "-"
@@ -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()
@@ -1532,6 +1532,13 @@ function openPopUp(dataType, element) {
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
if (data.hasOwnProperty("_uuid.key")) {
if (data["_uuid.key"] != "") {

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,54 @@ 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
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
@@ -381,6 +471,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
@@ -409,6 +515,15 @@ class SettingsCategory {
text = "{{.settings.replaceEmptyImages.description}}"
break
case "scheme.m3u":
text = "{{.settings.schemeM3U.description}}"
break
case "scheme.xml":
text = "{{.settings.schemeXML.description}}"
break
default:
text = ""
break

View File

@@ -35,14 +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.3.0030"
const Version = "2.1.1.0110"
// DBVersion : Datanbank Version
const DBVersion = "2.0.0"
const DBVersion = "2.1.0"
// APIVersion : API Version
const APIVersion = "1.1.0"