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:
5Ub-Z3r0
2020-05-12 21:29:46 +02:00
parent 2d10fc9313
commit 67b7ba6df9
13 changed files with 119 additions and 70 deletions

View File

@@ -20,7 +20,7 @@ menuItems.push(new MainMenuItem("logout", "{{.mainMenu.item.logout}}", "logout.p
var settingsCategory = new Array();
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.general}}", "xteveAutoUpdate,tuner,epgSource,api"));
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.files}}", "update,files.update,temp.path,cache.images,xepg.replace.missing.images"));
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.streaming}}", "buffer,buffer.size.kb,buffer.timeout,user.agent,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) {

View File

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

View File

@@ -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.",

View File

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

View File

@@ -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{})

View File

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

View File

@@ -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"`

View File

@@ -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"`

View File

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

File diff suppressed because one or more lines are too long

View File

@@ -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 "-":

View File

@@ -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;
}

View File

@@ -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()