Add FFmpeg and VLC support
This commit is contained in:
@@ -20,7 +20,7 @@ menuItems.push(new MainMenuItem("logout", "{{.mainMenu.item.logout}}", "logout.p
|
||||
var settingsCategory = new Array();
|
||||
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.general}}", "xteveAutoUpdate,tuner,epgSource,api"));
|
||||
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.files}}", "update,files.update,temp.path,cache.images,xepg.replace.missing.images"));
|
||||
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.streaming}}", "buffer,buffer.size.kb,buffer.timeout,user.agent"));
|
||||
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.streaming}}", "buffer,buffer.size.kb,buffer.timeout,user.agent,ffmpeg.path,ffmpeg.options,vlc.path,vlc.options"));
|
||||
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.backup}}", "backup.path,backup.keep"));
|
||||
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.authentication}}", "authentication.web,authentication.pms,authentication.m3u,authentication.xml,authentication.api"));
|
||||
function showPopUpElement(elm) {
|
||||
|
||||
@@ -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++) {
|
||||
|
||||
@@ -85,6 +85,50 @@ var SettingsCategory = /** @class */ (function () {
|
||||
setting.appendChild(tdLeft);
|
||||
setting.appendChild(tdRight);
|
||||
break;
|
||||
case "ffmpeg.path":
|
||||
var tdLeft = document.createElement("TD");
|
||||
tdLeft.innerHTML = "{{.settings.ffmpegPath.title}}" + ":";
|
||||
var tdRight = document.createElement("TD");
|
||||
var input = content.createInput("text", "ffmpeg.path", data);
|
||||
input.setAttribute("placeholder", "{{.settings.ffmpegPath.placeholder}}");
|
||||
input.setAttribute("onchange", "javascript: this.className = 'changed'");
|
||||
tdRight.appendChild(input);
|
||||
setting.appendChild(tdLeft);
|
||||
setting.appendChild(tdRight);
|
||||
break;
|
||||
case "ffmpeg.options":
|
||||
var tdLeft = document.createElement("TD");
|
||||
tdLeft.innerHTML = "{{.settings.ffmpegOptions.title}}" + ":";
|
||||
var tdRight = document.createElement("TD");
|
||||
var input = content.createInput("text", "ffmpeg.options", data);
|
||||
input.setAttribute("placeholder", "{{.settings.ffmpegOptions.placeholder}}");
|
||||
input.setAttribute("onchange", "javascript: this.className = 'changed'");
|
||||
tdRight.appendChild(input);
|
||||
setting.appendChild(tdLeft);
|
||||
setting.appendChild(tdRight);
|
||||
break;
|
||||
case "vlc.path":
|
||||
var tdLeft = document.createElement("TD");
|
||||
tdLeft.innerHTML = "{{.settings.vlcPath.title}}" + ":";
|
||||
var tdRight = document.createElement("TD");
|
||||
var input = content.createInput("text", "vlc.path", data);
|
||||
input.setAttribute("placeholder", "{{.settings.vlcPath.placeholder}}");
|
||||
input.setAttribute("onchange", "javascript: this.className = 'changed'");
|
||||
tdRight.appendChild(input);
|
||||
setting.appendChild(tdLeft);
|
||||
setting.appendChild(tdRight);
|
||||
break;
|
||||
case "vlc.options":
|
||||
var tdLeft = document.createElement("TD");
|
||||
tdLeft.innerHTML = "{{.settings.vlcOptions.title}}" + ":";
|
||||
var tdRight = document.createElement("TD");
|
||||
var input = content.createInput("text", "vlc.options", data);
|
||||
input.setAttribute("placeholder", "{{.settings.vlcOptions.placeholder}}");
|
||||
input.setAttribute("onchange", "javascript: this.className = 'changed'");
|
||||
tdRight.appendChild(input);
|
||||
setting.appendChild(tdLeft);
|
||||
setting.appendChild(tdRight);
|
||||
break;
|
||||
// Checkboxen
|
||||
case "authentication.web":
|
||||
var tdLeft = document.createElement("TD");
|
||||
@@ -185,17 +229,6 @@ var SettingsCategory = /** @class */ (function () {
|
||||
setting.appendChild(tdLeft);
|
||||
setting.appendChild(tdRight);
|
||||
break;
|
||||
case "buffer":
|
||||
var tdLeft = document.createElement("TD");
|
||||
tdLeft.innerHTML = "{{.settings.streamBuffering.title}}" + ":";
|
||||
var tdRight = document.createElement("TD");
|
||||
var input = content.createCheckbox(settingsKey);
|
||||
input.checked = data;
|
||||
input.setAttribute("onchange", "javascript: this.className = 'changed'");
|
||||
tdRight.appendChild(input);
|
||||
setting.appendChild(tdLeft);
|
||||
setting.appendChild(tdRight);
|
||||
break;
|
||||
case "api":
|
||||
var tdLeft = document.createElement("TD");
|
||||
tdLeft.innerHTML = "{{.settings.api.title}}" + ":";
|
||||
@@ -260,6 +293,18 @@ var SettingsCategory = /** @class */ (function () {
|
||||
setting.appendChild(tdLeft);
|
||||
setting.appendChild(tdRight);
|
||||
break;
|
||||
case "buffer":
|
||||
var tdLeft = document.createElement("TD");
|
||||
tdLeft.innerHTML = "{{.settings.streamBuffering.title}}" + ":";
|
||||
var tdRight = document.createElement("TD");
|
||||
var text = ["{{.settings.streamBuffering.info_false}}", "xTeVe: ({{.settings.streamBuffering.info_xteve}})", "FFmpeg: ({{.settings.streamBuffering.info_ffmpeg}})", "VLC: ({{.settings.streamBuffering.info_vlc}})"];
|
||||
var values = ["-", "xteve", "ffmpeg", "vlc"];
|
||||
var select = content.createSelect(text, values, data, settingsKey);
|
||||
select.setAttribute("onchange", "javascript: this.className = 'changed'");
|
||||
tdRight.appendChild(select);
|
||||
setting.appendChild(tdLeft);
|
||||
setting.appendChild(tdRight);
|
||||
break;
|
||||
}
|
||||
return setting;
|
||||
};
|
||||
@@ -308,6 +353,18 @@ var SettingsCategory = /** @class */ (function () {
|
||||
case "user.agent":
|
||||
text = "{{.settings.userAgent.description}}";
|
||||
break;
|
||||
case "ffmpeg.path":
|
||||
text = "{{.settings.ffmpegPath.description}}";
|
||||
break;
|
||||
case "ffmpeg.options":
|
||||
text = "{{.settings.ffmpegOptions.description}}";
|
||||
break;
|
||||
case "vlc.path":
|
||||
text = "{{.settings.vlcPath.description}}";
|
||||
break;
|
||||
case "vlc.options":
|
||||
text = "{{.settings.vlcOptions.description}}";
|
||||
break;
|
||||
case "epgSource":
|
||||
text = "{{.settings.epgSource.description}}";
|
||||
break;
|
||||
|
||||
@@ -307,7 +307,7 @@
|
||||
"description": "Updates all playlists, tuner and XMLTV files at startup."
|
||||
},
|
||||
"cacheImages": {
|
||||
"title": "Image caching",
|
||||
"title": "Image Caching",
|
||||
"description": "All images from the XMLTV file are cached, allowing faster rendering of the grid in the client.<br>Downloading the images may take a while and will be done in the background."
|
||||
},
|
||||
"replaceEmptyImages": {
|
||||
@@ -320,7 +320,32 @@
|
||||
},
|
||||
"streamBuffering": {
|
||||
"title": "Stream Buffer",
|
||||
"description": "- The stream is passed from xTeVe to Plex / Emby / M3U Player<br>- Small jerking of the streams can be compensated<br>- HLS / M3U8 support<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 +357,8 @@
|
||||
"placeholder": "100"
|
||||
},
|
||||
"userAgent": {
|
||||
"title": "User agent",
|
||||
"description": "User Agent for HTTP requests",
|
||||
"title": "User Agent",
|
||||
"description": "User Agent for HTTP requests. Only used if xTeVe is selected as the buffer.",
|
||||
"placeholder": "xTeVe"
|
||||
},
|
||||
"backupPath": {
|
||||
|
||||
272
src/buffer.go
272
src/buffer.go
@@ -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 bufferWithFfmpeg(streamID, playlistID)
|
||||
|
||||
default:
|
||||
break
|
||||
|
||||
}
|
||||
|
||||
showInfo(fmt.Sprintf("Streaming Status:Playlist: %s - Tuner: %d / %d", playlist.PlaylistName, len(playlist.Streams), playlist.Tuner))
|
||||
|
||||
@@ -400,7 +413,7 @@ func getTmpFiles(stream *ThisStream) (tmpFiles []string) {
|
||||
return
|
||||
}
|
||||
|
||||
if len(files) > 1 {
|
||||
if len(files) > 2 {
|
||||
|
||||
for _, file := range files {
|
||||
|
||||
@@ -526,6 +539,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 +821,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 +898,7 @@ func connectToStreamingServer(streamID int, playlistID string) {
|
||||
}
|
||||
|
||||
// Buffer auf die Festplatte speichern
|
||||
if fileSize >= tmpFileSize || n == 0 {
|
||||
if fileSize >= tmpFileSize/2 || n == 0 {
|
||||
|
||||
bandwidth.Stop = time.Now()
|
||||
bandwidth.Size += fileSize
|
||||
@@ -928,6 +944,7 @@ func connectToStreamingServer(streamID int, playlistID string) {
|
||||
}
|
||||
|
||||
fileSize = 0
|
||||
buffer = make([]byte, 1024*bufferSize*2)
|
||||
|
||||
if n == 0 {
|
||||
bufferFile.Close()
|
||||
@@ -1319,14 +1336,255 @@ func switchBandwidth(stream *ThisStream) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// Buffer mit FFMPEG
|
||||
func bufferWithFfmpeg(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 fileSize = 0
|
||||
var stream = playlist.Streams[streamID]
|
||||
var buf bytes.Buffer
|
||||
|
||||
var tmpFolder = playlist.Streams[streamID].Folder
|
||||
var url = playlist.Streams[streamID].URL
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
showInfo("Streaming URL:" + stream.URL)
|
||||
|
||||
// Größe des Buffers
|
||||
buffer := make([]byte, 1024*Settings.BufferSize*2)
|
||||
var tmpFileSize = 1024 * Settings.BufferSize * 1
|
||||
|
||||
debug = fmt.Sprintf("Buffer Size:%d KB [SERVER CONNECTION]", len(buffer)/1024)
|
||||
showDebug(debug, 3)
|
||||
|
||||
debug = fmt.Sprintf("Buffer Size:%d KB [CLIENT CONNECTION]", tmpFileSize/1024)
|
||||
showDebug(debug, 3)
|
||||
|
||||
var tmpFile = fmt.Sprintf("%s%d.ts", tmpFolder, tmpSegment)
|
||||
|
||||
bufferFile, err := os.Create(tmpFile)
|
||||
if err != nil {
|
||||
|
||||
addErrorToStream(err)
|
||||
bufferFile.Close()
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
var args = strings.Replace(options, "[URL]", url, -1)
|
||||
var cmd = exec.Command(path, strings.Split(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()
|
||||
|
||||
go func() {
|
||||
|
||||
// Log Daten vom Prozess im Dubug Mode 2 anzeigen.
|
||||
scanner := bufio.NewScanner(logOut)
|
||||
scanner.Split(bufio.ScanLines)
|
||||
|
||||
for scanner.Scan() {
|
||||
debug = fmt.Sprintf("%s:%s", bufferType, strings.TrimSpace(scanner.Text()))
|
||||
showDebug(debug, 1)
|
||||
}
|
||||
|
||||
}()
|
||||
|
||||
scanner := bufio.NewScanner(stdOut)
|
||||
scanner.Split(bufio.ScanBytes)
|
||||
|
||||
for scanner.Scan() {
|
||||
|
||||
if len(buf.Bytes()) == 0 && stream.Status == false {
|
||||
showInfo("Streaming Status:Receive data from " + bufferType)
|
||||
}
|
||||
|
||||
b := scanner.Bytes()
|
||||
|
||||
for _, i := range b {
|
||||
buf.WriteByte(i)
|
||||
}
|
||||
|
||||
fileSize = fileSize + len(b)
|
||||
|
||||
if clientConnection(stream) == false {
|
||||
|
||||
cmd.Process.Kill()
|
||||
bufferFile.Close()
|
||||
|
||||
err = os.RemoveAll(stream.Folder)
|
||||
if err != nil {
|
||||
ShowError(err, 4005)
|
||||
}
|
||||
|
||||
cmd.Wait()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if fileSize >= tmpFileSize/2 {
|
||||
|
||||
if stream.Status == false {
|
||||
showInfo("Streaming Status:Buffering data from " + bufferType)
|
||||
}
|
||||
|
||||
if _, err := bufferFile.Write(buf.Bytes()); err != nil {
|
||||
|
||||
cmd.Process.Kill()
|
||||
ShowError(err, 0)
|
||||
addErrorToStream(err)
|
||||
cmd.Wait()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
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 {
|
||||
|
||||
cmd.Process.Kill()
|
||||
bufferFile.Close()
|
||||
|
||||
err = os.RemoveAll(stream.Folder)
|
||||
if err != nil {
|
||||
ShowError(err, 4005)
|
||||
}
|
||||
|
||||
cmd.Wait()
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
bufferFile, err = os.Create(tmpFile)
|
||||
if err != nil {
|
||||
|
||||
addErrorToStream(err)
|
||||
cmd.Process.Kill()
|
||||
cmd.Wait()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
buf.Reset()
|
||||
|
||||
fileSize = 0
|
||||
|
||||
/*
|
||||
if n == 0 {
|
||||
bufferFile.Close()
|
||||
run.Process.Kill()
|
||||
break
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//cmd.Process.Kill()
|
||||
cmd.Wait()
|
||||
|
||||
err = errors.New(bufferType + " error")
|
||||
addErrorToStream(err)
|
||||
ShowError(err, 1204)
|
||||
|
||||
time.Sleep(time.Duration(1000) * time.Millisecond)
|
||||
clientConnection(stream)
|
||||
|
||||
//os.Exit(0)
|
||||
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 {
|
||||
|
||||
@@ -46,6 +46,10 @@ func Init() (err error) {
|
||||
System.DVRLimit = 480
|
||||
System.Compatibility = "1.4.4"
|
||||
|
||||
// FFmpeg Default Einstellungen
|
||||
System.FFmpeg.DefaultOptions = "-hide_banner -loglevel panic -i [URL] -c copy -f mpegts pipe:1"
|
||||
System.VLC.DefaultOptions = "[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 +137,6 @@ func Init() (err error) {
|
||||
// Bedingte Update Änderungen durchführen
|
||||
err = conditionalUpdateChanges()
|
||||
if err != nil {
|
||||
ShowError(err, 0)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
40
src/data.go
40
src/data.go
@@ -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,17 @@ func updateServerSettings(request RequestStruct) (settings SettingsStrcut, err e
|
||||
return
|
||||
}
|
||||
|
||||
case "ffmpeg.path", "vlc.path":
|
||||
var path = value.(string)
|
||||
if len(path) > 0 {
|
||||
|
||||
err = checkFile(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
oldSettings[key] = value
|
||||
@@ -138,6 +151,33 @@ func updateServerSettings(request RequestStruct) (settings SettingsStrcut, err e
|
||||
|
||||
}
|
||||
|
||||
// Buffer Einstellungen überprüfen
|
||||
if len(Settings.FFmpegOptions) == 0 {
|
||||
Settings.FFmpegOptions = System.FFmpeg.DefaultOptions
|
||||
}
|
||||
|
||||
if len(Settings.VLCOptions) == 0 {
|
||||
Settings.VLCOptions = System.VLC.DefaultOptions
|
||||
}
|
||||
|
||||
switch Settings.Buffer {
|
||||
|
||||
case "ffmpeg":
|
||||
|
||||
if len(Settings.FFmpegPath) == 0 {
|
||||
err = errors.New(getErrMsg(2020))
|
||||
return
|
||||
}
|
||||
|
||||
case "vlc":
|
||||
|
||||
if len(Settings.VLCPath) == 0 {
|
||||
err = errors.New(getErrMsg(2021))
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
err = saveSettings(Settings)
|
||||
if err == nil {
|
||||
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -254,6 +254,8 @@ 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:
|
||||
@@ -292,6 +294,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 +312,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")
|
||||
@@ -370,7 +378,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")
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
@@ -242,11 +252,15 @@ type SettingsStrcut struct {
|
||||
BackupKeep int `json:"backup.keep"`
|
||||
BackupPath string `json:"backup.path"`
|
||||
Branch string `json:"git.branch,omitempty"`
|
||||
Buffer bool `json:"buffer"`
|
||||
Buffer string `json:"buffer"`
|
||||
BufferSize int `json:"buffer.size.kb"`
|
||||
BufferTimeout float64 `json:"buffer.timeout"`
|
||||
CacheImages bool `json:"cache.images"`
|
||||
EpgSource string `json:"epgSource"`
|
||||
FFmpegOptions string `json:"ffmpeg.options"`
|
||||
FFmpegPath string `json:"ffmpeg.path"`
|
||||
VLCOptions string `json:"vlc.options"`
|
||||
VLCPath string `json:"vlc.path"`
|
||||
FileM3U []string `json:"file,omitempty"` // Beim Wizard wird die M3U in ein Slice gespeichert
|
||||
FileXMLTV []string `json:"xmltv,omitempty"` // Altes Speichersystem der Provider XML Datei Slice (Wird für die Umwandlung auf das neue benötigt)
|
||||
|
||||
|
||||
@@ -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"`
|
||||
|
||||
@@ -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{})
|
||||
@@ -159,10 +161,27 @@ func loadSettings() (settings SettingsStrcut, err error) {
|
||||
showInfo(fmt.Sprintf("Git Branch:Switching Git Branch to -> %s", settings.Branch))
|
||||
}
|
||||
|
||||
if len(settings.FFmpegPath) == 0 {
|
||||
settings.FFmpegPath = searchFileInOS("ffmpeg")
|
||||
}
|
||||
|
||||
if len(settings.FFmpegPath) == 0 {
|
||||
settings.FFmpegPath = 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
|
||||
}
|
||||
|
||||
|
||||
@@ -10,8 +10,11 @@ import (
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
@@ -77,6 +80,7 @@ func GetUserHomeDirectory() (userHomeDirectory string) {
|
||||
return
|
||||
}
|
||||
|
||||
// Prüft Dateiberechtigung
|
||||
func checkFilePermission(dir string) (err error) {
|
||||
|
||||
var filename = dir + "permission.test"
|
||||
@@ -115,6 +119,28 @@ 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 {
|
||||
path = strings.Trim(string(out), "\r\n")
|
||||
}
|
||||
|
||||
default:
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
//
|
||||
func removeChildItems(dir string) error {
|
||||
|
||||
@@ -276,6 +302,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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
72
src/webUI.go
72
src/webUI.go
File diff suppressed because one or more lines are too long
@@ -129,20 +129,29 @@ 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)
|
||||
/*
|
||||
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 URL:" + streamInfo.URL)
|
||||
http.Redirect(w, r, streamInfo.URL, 302)
|
||||
|
||||
showInfo("Streaming Info:URL was passed to the client")
|
||||
return
|
||||
}
|
||||
*/
|
||||
|
||||
switch Settings.Buffer {
|
||||
|
||||
case "-":
|
||||
showInfo(fmt.Sprintf("Buffer:false", 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 +161,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
|
||||
|
||||
@@ -23,7 +23,7 @@ menuItems.push(new MainMenuItem("logout", "{{.mainMenu.item.logout}}", "logout.p
|
||||
var settingsCategory = new Array()
|
||||
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.general}}", "xteveAutoUpdate,tuner,epgSource,api"))
|
||||
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.files}}", "update,files.update,temp.path,cache.images,xepg.replace.missing.images"))
|
||||
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.streaming}}", "buffer,buffer.size.kb,buffer.timeout,user.agent"))
|
||||
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.streaming}}", "buffer,buffer.size.kb,buffer.timeout,user.agent,ffmpeg.path,ffmpeg.options,vlc.path,vlc.options"))
|
||||
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.backup}}", "backup.path,backup.keep"))
|
||||
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.authentication}}", "authentication.web,authentication.pms,authentication.m3u,authentication.xml,authentication.api"))
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -89,6 +89,62 @@ class SettingsCategory {
|
||||
setting.appendChild(tdRight)
|
||||
break
|
||||
|
||||
case "ffmpeg.path":
|
||||
var tdLeft = document.createElement("TD")
|
||||
tdLeft.innerHTML = "{{.settings.ffmpegPath.title}}" + ":"
|
||||
|
||||
var tdRight = document.createElement("TD")
|
||||
var input = content.createInput("text", "ffmpeg.path", data)
|
||||
input.setAttribute("placeholder", "{{.settings.ffmpegPath.placeholder}}")
|
||||
input.setAttribute("onchange", "javascript: this.className = 'changed'")
|
||||
tdRight.appendChild(input)
|
||||
|
||||
setting.appendChild(tdLeft)
|
||||
setting.appendChild(tdRight)
|
||||
break
|
||||
|
||||
case "ffmpeg.options":
|
||||
var tdLeft = document.createElement("TD")
|
||||
tdLeft.innerHTML = "{{.settings.ffmpegOptions.title}}" + ":"
|
||||
|
||||
var tdRight = document.createElement("TD")
|
||||
var input = content.createInput("text", "ffmpeg.options", data)
|
||||
input.setAttribute("placeholder", "{{.settings.ffmpegOptions.placeholder}}")
|
||||
input.setAttribute("onchange", "javascript: this.className = 'changed'")
|
||||
tdRight.appendChild(input)
|
||||
|
||||
setting.appendChild(tdLeft)
|
||||
setting.appendChild(tdRight)
|
||||
break
|
||||
|
||||
case "vlc.path":
|
||||
var tdLeft = document.createElement("TD")
|
||||
tdLeft.innerHTML = "{{.settings.vlcPath.title}}" + ":"
|
||||
|
||||
var tdRight = document.createElement("TD")
|
||||
var input = content.createInput("text", "vlc.path", data)
|
||||
input.setAttribute("placeholder", "{{.settings.vlcPath.placeholder}}")
|
||||
input.setAttribute("onchange", "javascript: this.className = 'changed'")
|
||||
tdRight.appendChild(input)
|
||||
|
||||
setting.appendChild(tdLeft)
|
||||
setting.appendChild(tdRight)
|
||||
break
|
||||
|
||||
case "vlc.options":
|
||||
var tdLeft = document.createElement("TD")
|
||||
tdLeft.innerHTML = "{{.settings.vlcOptions.title}}" + ":"
|
||||
|
||||
var tdRight = document.createElement("TD")
|
||||
var input = content.createInput("text", "vlc.options", data)
|
||||
input.setAttribute("placeholder", "{{.settings.vlcOptions.placeholder}}")
|
||||
input.setAttribute("onchange", "javascript: this.className = 'changed'")
|
||||
tdRight.appendChild(input)
|
||||
|
||||
setting.appendChild(tdLeft)
|
||||
setting.appendChild(tdRight)
|
||||
break
|
||||
|
||||
// Checkboxen
|
||||
case "authentication.web":
|
||||
var tdLeft = document.createElement("TD")
|
||||
@@ -216,20 +272,6 @@ class SettingsCategory {
|
||||
setting.appendChild(tdRight)
|
||||
break
|
||||
|
||||
case "buffer":
|
||||
var tdLeft = document.createElement("TD")
|
||||
tdLeft.innerHTML = "{{.settings.streamBuffering.title}}" + ":"
|
||||
|
||||
var tdRight = document.createElement("TD")
|
||||
var input = content.createCheckbox(settingsKey)
|
||||
input.checked = data
|
||||
input.setAttribute("onchange", "javascript: this.className = 'changed'")
|
||||
tdRight.appendChild(input)
|
||||
|
||||
setting.appendChild(tdLeft)
|
||||
setting.appendChild(tdRight)
|
||||
break
|
||||
|
||||
case "api":
|
||||
var tdLeft = document.createElement("TD")
|
||||
tdLeft.innerHTML = "{{.settings.api.title}}" + ":"
|
||||
@@ -314,6 +356,22 @@ class SettingsCategory {
|
||||
setting.appendChild(tdRight)
|
||||
break
|
||||
|
||||
case "buffer":
|
||||
var tdLeft = document.createElement("TD")
|
||||
tdLeft.innerHTML = "{{.settings.streamBuffering.title}}" + ":"
|
||||
|
||||
var tdRight = document.createElement("TD")
|
||||
var text:any[] = ["{{.settings.streamBuffering.info_false}}", "xTeVe: ({{.settings.streamBuffering.info_xteve}})", "FFmpeg: ({{.settings.streamBuffering.info_ffmpeg}})", "VLC: ({{.settings.streamBuffering.info_vlc}})"]
|
||||
var values:any[] = ["-", "xteve", "ffmpeg", "vlc"]
|
||||
|
||||
var select = content.createSelect(text, values, data, settingsKey)
|
||||
select.setAttribute("onchange", "javascript: this.className = 'changed'")
|
||||
tdRight.appendChild(select)
|
||||
|
||||
setting.appendChild(tdLeft)
|
||||
setting.appendChild(tdRight)
|
||||
break
|
||||
|
||||
}
|
||||
|
||||
return setting
|
||||
@@ -381,6 +439,22 @@ class SettingsCategory {
|
||||
text = "{{.settings.userAgent.description}}"
|
||||
break
|
||||
|
||||
case "ffmpeg.path":
|
||||
text = "{{.settings.ffmpegPath.description}}"
|
||||
break
|
||||
|
||||
case "ffmpeg.options":
|
||||
text = "{{.settings.ffmpegOptions.description}}"
|
||||
break
|
||||
|
||||
case "vlc.path":
|
||||
text = "{{.settings.vlcPath.description}}"
|
||||
break
|
||||
|
||||
case "vlc.options":
|
||||
text = "{{.settings.vlcOptions.description}}"
|
||||
break
|
||||
|
||||
case "epgSource":
|
||||
text = "{{.settings.epgSource.description}}"
|
||||
break
|
||||
|
||||
4
xteve.go
4
xteve.go
@@ -39,10 +39,10 @@ var GitHub = GitHubStruct{Branch: "master", User: "xteve-project", Repo: "xTeVe-
|
||||
const Name = "xTeVe"
|
||||
|
||||
// Version : Version, die Build Nummer wird in der main func geparst.
|
||||
const Version = "2.0.3.0030"
|
||||
const Version = "2.0.3.0035"
|
||||
|
||||
// DBVersion : Datanbank Version
|
||||
const DBVersion = "2.0.0"
|
||||
const DBVersion = "2.1.0"
|
||||
|
||||
// APIVersion : API Version
|
||||
const APIVersion = "1.1.0"
|
||||
|
||||
Reference in New Issue
Block a user