Add support for proxying multicast streams through a UDPxy server.
This commit adds support for proxying multicast streams through a UDPxy server. If the stream is multicast, and a udpxy server is set in the configuration, then the channel URL is rewritten to use the UDPxy service configured. The stream URL rewriting is done regardless of the buffer option set. Signed-off-by: 5Ub-Z3r0 <1673590+5Ub-Z3r0@users.noreply.github.com>
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,ffmpeg.path,ffmpeg.options,vlc.path,vlc.options"));
|
||||
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.streaming}}", "buffer,udpxy,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) {
|
||||
|
||||
@@ -305,6 +305,17 @@ var SettingsCategory = /** @class */ (function () {
|
||||
setting.appendChild(tdLeft);
|
||||
setting.appendChild(tdRight);
|
||||
break;
|
||||
case "udpxy":
|
||||
var tdLeft = document.createElement("TD");
|
||||
tdLeft.innerHTML = "{{.settings.udpxy.title}}" + ":";
|
||||
var tdRight = document.createElement("TD");
|
||||
var input = content.createInput("text", "udpxy", data);
|
||||
input.setAttribute("placeholder", "{{.settings.udpxy.placeholder}}");
|
||||
input.setAttribute("onchange", "javascript: this.className = 'changed'");
|
||||
tdRight.appendChild(input);
|
||||
setting.appendChild(tdLeft);
|
||||
setting.appendChild(tdRight);
|
||||
break;
|
||||
}
|
||||
return setting;
|
||||
};
|
||||
@@ -386,6 +397,9 @@ var SettingsCategory = /** @class */ (function () {
|
||||
case "xepg.replace.missing.images":
|
||||
text = "{{.settings.replaceEmptyImages.description}}";
|
||||
break;
|
||||
case "udpxy":
|
||||
text = "{{.settings.udpxy.description}}";
|
||||
break;
|
||||
default:
|
||||
text = "";
|
||||
break;
|
||||
|
||||
@@ -183,52 +183,52 @@
|
||||
"active": {
|
||||
"title": "Active",
|
||||
"placeholder": "",
|
||||
"description": ""
|
||||
"description": ""
|
||||
},
|
||||
"channelName": {
|
||||
"title": "Channel Name",
|
||||
"placeholder": "",
|
||||
"description": ""
|
||||
"description": ""
|
||||
},
|
||||
"description": {
|
||||
"title": "Channel Description",
|
||||
"placeholder": "Used by the Dummy as an XML description",
|
||||
"description": ""
|
||||
"description": ""
|
||||
},
|
||||
"updateChannelName": {
|
||||
"title": "Update Channel Name",
|
||||
"placeholder": "",
|
||||
"description": ""
|
||||
"description": ""
|
||||
},
|
||||
"channelLogo": {
|
||||
"title": "Logo URL",
|
||||
"placeholder": "",
|
||||
"description": ""
|
||||
"description": ""
|
||||
},
|
||||
"updateChannelLogo": {
|
||||
"title": "Update Channel Logo",
|
||||
"placeholder": "",
|
||||
"description": ""
|
||||
"description": ""
|
||||
},
|
||||
"epgCategory": {
|
||||
"title": "EPG Category",
|
||||
"placeholder": "",
|
||||
"description": ""
|
||||
"description": ""
|
||||
},
|
||||
"m3uGroupTitle": {
|
||||
"title": "Group Title (xteve.m3u)",
|
||||
"placeholder": "",
|
||||
"description": ""
|
||||
"description": ""
|
||||
},
|
||||
"xmltvFile": {
|
||||
"title": "XMLTV File",
|
||||
"placeholder": "",
|
||||
"description": ""
|
||||
"description": ""
|
||||
},
|
||||
"xmltvChannel": {
|
||||
"title": "XMLTV Channel",
|
||||
"placeholder": "",
|
||||
"description": ""
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"users": {
|
||||
@@ -332,6 +332,11 @@
|
||||
"info_vlc": "VLC connects to the streaming server"
|
||||
|
||||
},
|
||||
"udpxy": {
|
||||
"title": "UDPxy address",
|
||||
"description": "The address of your UDPxy server. If set, and the channel URLs in the m3u is multicast, xTeVe will rewrite it so that it is accessed via the UDPxy service.",
|
||||
"placeholder": "host:port"
|
||||
},
|
||||
"ffmpegPath": {
|
||||
"title": "FFmpeg Binary Path",
|
||||
"description": "Path to FFmpeg binary.",
|
||||
|
||||
@@ -15,7 +15,7 @@ var System SystemStruct
|
||||
var WebScreenLog WebScreenLogStruct
|
||||
|
||||
// Settings : Inhalt der settings.json
|
||||
var Settings SettingsStrcut
|
||||
var Settings SettingsStruct
|
||||
|
||||
// Data : Alle Daten werden hier abgelegt. (Lineup, XMLTV)
|
||||
var Data DataStruct
|
||||
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
)
|
||||
|
||||
// Einstellungen ändern (WebUI)
|
||||
func updateServerSettings(request RequestStruct) (settings SettingsStrcut, err error) {
|
||||
func updateServerSettings(request RequestStruct) (settings SettingsStruct, err error) {
|
||||
|
||||
var oldSettings = jsonToMap(mapToJSON(Settings))
|
||||
var newSettings = jsonToMap(mapToJSON(request.Settings))
|
||||
@@ -408,7 +408,7 @@ func deleteLocalProviderFiles(dataID, fileType string) {
|
||||
}
|
||||
|
||||
// Filtereinstellungen speichern (WebUI)
|
||||
func saveFilter(request RequestStruct) (settings SettingsStrcut, err error) {
|
||||
func saveFilter(request RequestStruct) (settings SettingsStruct, err error) {
|
||||
|
||||
var filterMap = make(map[int64]interface{})
|
||||
var newData = make(map[int64]interface{})
|
||||
|
||||
@@ -86,6 +86,7 @@ func ShowSystemInfo() {
|
||||
|
||||
fmt.Println("Settings [Streaming]")
|
||||
fmt.Println(fmt.Sprintf("Buffer: %s", Settings.Buffer))
|
||||
fmt.Println(fmt.Sprintf("UDPxy: %s", Settings.UDPxy))
|
||||
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))
|
||||
|
||||
@@ -99,6 +99,7 @@ type SystemStruct struct {
|
||||
}
|
||||
|
||||
URLBase string
|
||||
UDPxy string
|
||||
Version string
|
||||
WEB struct {
|
||||
Menu []string
|
||||
@@ -246,8 +247,8 @@ type Notification struct {
|
||||
Type string `json:"type,required"`
|
||||
}
|
||||
|
||||
// SettingsStrcut : Inhalt der settings.json
|
||||
type SettingsStrcut struct {
|
||||
// SettingsStruct : Inhalt der settings.json
|
||||
type SettingsStruct struct {
|
||||
API bool `json:"api"`
|
||||
AuthenticationAPI bool `json:"authentication.api"`
|
||||
AuthenticationM3U bool `json:"authentication.m3u"`
|
||||
@@ -290,6 +291,7 @@ type SettingsStrcut struct {
|
||||
UpdateURL string `json:"update.url,omitempty"`
|
||||
UserAgent string `json:"user.agent"`
|
||||
UUID string `json:"uuid"`
|
||||
UDPxy string `json:"udpxy"`
|
||||
Version string `json:"version"`
|
||||
XepgReplaceMissingImages bool `json:"xepg.replace.missing.images"`
|
||||
XteveAutoUpdate bool `json:"xteveAutoUpdate"`
|
||||
|
||||
@@ -37,6 +37,7 @@ type RequestStruct struct {
|
||||
FilesUpdate *bool `json:"files.update,omitempty"`
|
||||
TempPath *string `json:"temp.path,omitempty"`
|
||||
Tuner *int `json:"tuner,omitempty"`
|
||||
UDPxy *string `json:"udpxy,omitempty"`
|
||||
Update *[]string `json:"update,omitempty"`
|
||||
UserAgent *string `json:"user.agent,omitempty"`
|
||||
XepgReplaceMissingImages *bool `json:"xepg.replace.missing.images,omitempty"`
|
||||
@@ -109,7 +110,7 @@ type ResponseStruct struct {
|
||||
OpenLink string `json:"openLink,omitempty"`
|
||||
OpenMenu string `json:"openMenu,omitempty"`
|
||||
Reload bool `json:"reload,omitempty"`
|
||||
Settings SettingsStrcut `json:"settings,required"`
|
||||
Settings SettingsStruct `json:"settings,required"`
|
||||
Status bool `json:"status,required"`
|
||||
Token string `json:"token,omitempty"`
|
||||
Users map[string]interface{} `json:"users,omitempty"`
|
||||
|
||||
@@ -90,7 +90,7 @@ func createSystemFiles() (err error) {
|
||||
}
|
||||
|
||||
// Einstellungen laden und default Werte setzen (xTeVe)
|
||||
func loadSettings() (settings SettingsStrcut, err error) {
|
||||
func loadSettings() (settings SettingsStruct, err error) {
|
||||
|
||||
settingsMap, err := loadJSONFileToMap(System.File.Settings)
|
||||
if err != nil {
|
||||
@@ -135,6 +135,7 @@ func loadSettings() (settings SettingsStrcut, err error) {
|
||||
defaults["update"] = []string{"0000"}
|
||||
defaults["user.agent"] = System.Name
|
||||
defaults["uuid"] = createUUID()
|
||||
defaults["udpxy"] = ""
|
||||
defaults["version"] = System.DBVersion
|
||||
defaults["xteveAutoUpdate"] = true
|
||||
defaults["temp.path"] = System.Folder.Temp
|
||||
@@ -186,7 +187,7 @@ func loadSettings() (settings SettingsStrcut, err error) {
|
||||
}
|
||||
|
||||
// Einstellungen speichern (xTeVe)
|
||||
func saveSettings(settings SettingsStrcut) (err error) {
|
||||
func saveSettings(settings SettingsStruct) (err error) {
|
||||
|
||||
if settings.BackupKeep == 0 {
|
||||
settings.BackupKeep = 10
|
||||
|
||||
65
src/webUI.go
65
src/webUI.go
File diff suppressed because one or more lines are too long
@@ -30,6 +30,7 @@ func StartWebserver() (err error) {
|
||||
http.HandleFunc("/api/", API)
|
||||
http.HandleFunc("/images/", Images)
|
||||
http.HandleFunc("/data_images/", DataImages)
|
||||
|
||||
//http.HandleFunc("/auto/", Auto)
|
||||
|
||||
showInfo("DVR IP:" + System.IPAddress + ":" + Settings.Port)
|
||||
@@ -129,6 +130,12 @@ func Stream(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// If an UDPxy host is set, and the stream URL is multicast (i.e. starts with 'udp://@'),
|
||||
// then streamInfo.URL needs to be rewritten to point to UDPxy.
|
||||
if Settings.UDPxy != "" && strings.HasPrefix(streamInfo.URL, "udp://@") {
|
||||
streamInfo.URL = fmt.Sprintf("http://%s/udp/%s/", Settings.UDPxy, strings.TrimPrefix(streamInfo.URL, "udp://@"))
|
||||
}
|
||||
|
||||
switch Settings.Buffer {
|
||||
|
||||
case "-":
|
||||
|
||||
@@ -22,7 +22,7 @@ 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.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,ffmpeg.path,ffmpeg.options,vlc.path,vlc.options"))
|
||||
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.streaming}}", "buffer,udpxy,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"))
|
||||
|
||||
@@ -408,7 +408,7 @@ function changeChannelNumber(element) {
|
||||
})
|
||||
|
||||
for (var i = 0; i < channelNumbers.length; i++) {
|
||||
|
||||
|
||||
if (channelNumbers.indexOf(newNumber) == -1) {
|
||||
break
|
||||
}
|
||||
@@ -422,7 +422,7 @@ function changeChannelNumber(element) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
data[dbID]["x-channelID"] = newNumber.toString()
|
||||
element.value = newNumber
|
||||
|
||||
@@ -461,7 +461,7 @@ function toggleChannelStatus(id:string) {
|
||||
var checkbox = (document.getElementById("active") as HTMLInputElement)
|
||||
status = (checkbox).checked
|
||||
}
|
||||
|
||||
|
||||
|
||||
var ids:string[] = getAllSelectedChannels()
|
||||
if (ids.length == 0) {
|
||||
@@ -482,9 +482,9 @@ function toggleChannelStatus(id:string) {
|
||||
alert(channel["x-name"] + ": Missing XMLTV file / channel")
|
||||
checkbox.checked = false
|
||||
}
|
||||
|
||||
|
||||
channel["x-active"] = false
|
||||
|
||||
|
||||
}
|
||||
|
||||
break
|
||||
@@ -621,9 +621,9 @@ function checkUndo(key:string) {
|
||||
UNDO[key] = JSON.parse(JSON.stringify(SERVER["xepg"][key]));
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -646,9 +646,9 @@ function sortSelect(elem) {
|
||||
|
||||
elem.options[i] = tmpAry[i];
|
||||
if(elem.options[i].value == selectedValue) newSelectedIndex = i;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
elem.selectedIndex = newSelectedIndex; // Set new selected index after sorting
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -372,19 +372,34 @@ class SettingsCategory {
|
||||
setting.appendChild(tdRight)
|
||||
break
|
||||
|
||||
case "udpxy":
|
||||
|
||||
var tdLeft = document.createElement("TD");
|
||||
tdLeft.innerHTML = "{{.settings.udpxy.title}}" + ":"
|
||||
|
||||
var tdRight = document.createElement("TD")
|
||||
var input = content.createInput("text", "udpxy", data)
|
||||
input.setAttribute("placeholder", "{{.settings.udpxy.placeholder}}")
|
||||
input.setAttribute("onchange", "javascript: this.className = 'changed'")
|
||||
tdRight.appendChild(input)
|
||||
|
||||
setting.appendChild(tdLeft)
|
||||
setting.appendChild(tdRight)
|
||||
break
|
||||
|
||||
}
|
||||
|
||||
return setting
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
createDescription(settingsKey:string):any {
|
||||
|
||||
var description = document.createElement("TR")
|
||||
var text:string
|
||||
switch (settingsKey) {
|
||||
|
||||
|
||||
case "authentication.web":
|
||||
text = "{{.settings.authenticationWEB.description}}"
|
||||
break
|
||||
@@ -483,10 +498,14 @@ class SettingsCategory {
|
||||
text = "{{.settings.replaceEmptyImages.description}}"
|
||||
break
|
||||
|
||||
case "udpxy":
|
||||
text = "{{.settings.udpxy.description}}"
|
||||
break
|
||||
|
||||
default:
|
||||
text = ""
|
||||
break
|
||||
|
||||
|
||||
}
|
||||
|
||||
var tdLeft = document.createElement("TD")
|
||||
@@ -499,7 +518,7 @@ class SettingsCategory {
|
||||
|
||||
description.appendChild(tdLeft)
|
||||
description.appendChild(tdRight)
|
||||
|
||||
|
||||
return description
|
||||
|
||||
}
|
||||
@@ -519,12 +538,12 @@ class SettingsCategoryItem extends SettingsCategory {
|
||||
createCategory():void {
|
||||
var headline = this.createCategoryHeadline(this.headline)
|
||||
var settingsKeys = this.settingsKeys
|
||||
|
||||
|
||||
var doc = document.getElementById(this.DocumentID)
|
||||
doc.appendChild(headline)
|
||||
|
||||
// Tabelle für die Kategorie erstellen
|
||||
|
||||
|
||||
var table = document.createElement("TABLE")
|
||||
|
||||
var keys = settingsKeys.split(",")
|
||||
@@ -565,7 +584,7 @@ function showSettings() {
|
||||
for (let i = 0; i < settingsCategory.length; i++) {
|
||||
settingsCategory[i].createCategory()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
function saveSettings() {
|
||||
@@ -627,7 +646,7 @@ function saveSettings() {
|
||||
break
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
var data = new Object()
|
||||
|
||||
Reference in New Issue
Block a user