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(); 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"));
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.files}}", "update,files.update,temp.path,cache.images,xepg.replace.missing.images")); settingsCategory.push(new SettingsCategoryItem("{{.settings.category.files}}", "update,files.update,temp.path,cache.images,xepg.replace.missing.images"));
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.streaming}}", "buffer,buffer.size.kb,buffer.timeout,user.agent,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.backup}}", "backup.path,backup.keep"));
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.authentication}}", "authentication.web,authentication.pms,authentication.m3u,authentication.xml,authentication.api")); settingsCategory.push(new SettingsCategoryItem("{{.settings.category.authentication}}", "authentication.web,authentication.pms,authentication.m3u,authentication.xml,authentication.api"));
function showPopUpElement(elm) { function showPopUpElement(elm) {

View File

@@ -305,6 +305,17 @@ var SettingsCategory = /** @class */ (function () {
setting.appendChild(tdLeft); setting.appendChild(tdLeft);
setting.appendChild(tdRight); setting.appendChild(tdRight);
break; 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; return setting;
}; };
@@ -386,6 +397,9 @@ var SettingsCategory = /** @class */ (function () {
case "xepg.replace.missing.images": case "xepg.replace.missing.images":
text = "{{.settings.replaceEmptyImages.description}}"; text = "{{.settings.replaceEmptyImages.description}}";
break; break;
case "udpxy":
text = "{{.settings.udpxy.description}}";
break;
default: default:
text = ""; text = "";
break; break;

View File

@@ -332,6 +332,11 @@
"info_vlc": "VLC connects to the streaming server" "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": { "ffmpegPath": {
"title": "FFmpeg Binary Path", "title": "FFmpeg Binary Path",
"description": "Path to FFmpeg binary.", "description": "Path to FFmpeg binary.",

View File

@@ -15,7 +15,7 @@ var System SystemStruct
var WebScreenLog WebScreenLogStruct var WebScreenLog WebScreenLogStruct
// Settings : Inhalt der settings.json // Settings : Inhalt der settings.json
var Settings SettingsStrcut var Settings SettingsStruct
// Data : Alle Daten werden hier abgelegt. (Lineup, XMLTV) // Data : Alle Daten werden hier abgelegt. (Lineup, XMLTV)
var Data DataStruct var Data DataStruct

View File

@@ -15,7 +15,7 @@ import (
) )
// Einstellungen ändern (WebUI) // 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 oldSettings = jsonToMap(mapToJSON(Settings))
var newSettings = jsonToMap(mapToJSON(request.Settings)) var newSettings = jsonToMap(mapToJSON(request.Settings))
@@ -408,7 +408,7 @@ func deleteLocalProviderFiles(dataID, fileType string) {
} }
// Filtereinstellungen speichern (WebUI) // 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 filterMap = make(map[int64]interface{})
var newData = make(map[int64]interface{}) var newData = make(map[int64]interface{})

View File

@@ -86,6 +86,7 @@ func ShowSystemInfo() {
fmt.Println("Settings [Streaming]") fmt.Println("Settings [Streaming]")
fmt.Println(fmt.Sprintf("Buffer: %s", Settings.Buffer)) 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("Buffer Size: %d KB", Settings.BufferSize))
fmt.Println(fmt.Sprintf("Timeout: %d ms", int(Settings.BufferTimeout))) fmt.Println(fmt.Sprintf("Timeout: %d ms", int(Settings.BufferTimeout)))
fmt.Println(fmt.Sprintf("User Agent: %s", Settings.UserAgent)) fmt.Println(fmt.Sprintf("User Agent: %s", Settings.UserAgent))

View File

@@ -99,6 +99,7 @@ type SystemStruct struct {
} }
URLBase string URLBase string
UDPxy string
Version string Version string
WEB struct { WEB struct {
Menu []string Menu []string
@@ -246,8 +247,8 @@ type Notification struct {
Type string `json:"type,required"` Type string `json:"type,required"`
} }
// SettingsStrcut : Inhalt der settings.json // SettingsStruct : Inhalt der settings.json
type SettingsStrcut struct { type SettingsStruct struct {
API bool `json:"api"` API bool `json:"api"`
AuthenticationAPI bool `json:"authentication.api"` AuthenticationAPI bool `json:"authentication.api"`
AuthenticationM3U bool `json:"authentication.m3u"` AuthenticationM3U bool `json:"authentication.m3u"`
@@ -290,6 +291,7 @@ type SettingsStrcut struct {
UpdateURL string `json:"update.url,omitempty"` UpdateURL string `json:"update.url,omitempty"`
UserAgent string `json:"user.agent"` UserAgent string `json:"user.agent"`
UUID string `json:"uuid"` UUID string `json:"uuid"`
UDPxy string `json:"udpxy"`
Version string `json:"version"` Version string `json:"version"`
XepgReplaceMissingImages bool `json:"xepg.replace.missing.images"` XepgReplaceMissingImages bool `json:"xepg.replace.missing.images"`
XteveAutoUpdate bool `json:"xteveAutoUpdate"` XteveAutoUpdate bool `json:"xteveAutoUpdate"`

View File

@@ -37,6 +37,7 @@ type RequestStruct struct {
FilesUpdate *bool `json:"files.update,omitempty"` FilesUpdate *bool `json:"files.update,omitempty"`
TempPath *string `json:"temp.path,omitempty"` TempPath *string `json:"temp.path,omitempty"`
Tuner *int `json:"tuner,omitempty"` Tuner *int `json:"tuner,omitempty"`
UDPxy *string `json:"udpxy,omitempty"`
Update *[]string `json:"update,omitempty"` Update *[]string `json:"update,omitempty"`
UserAgent *string `json:"user.agent,omitempty"` UserAgent *string `json:"user.agent,omitempty"`
XepgReplaceMissingImages *bool `json:"xepg.replace.missing.images,omitempty"` XepgReplaceMissingImages *bool `json:"xepg.replace.missing.images,omitempty"`
@@ -109,7 +110,7 @@ type ResponseStruct struct {
OpenLink string `json:"openLink,omitempty"` OpenLink string `json:"openLink,omitempty"`
OpenMenu string `json:"openMenu,omitempty"` OpenMenu string `json:"openMenu,omitempty"`
Reload bool `json:"reload,omitempty"` Reload bool `json:"reload,omitempty"`
Settings SettingsStrcut `json:"settings,required"` Settings SettingsStruct `json:"settings,required"`
Status bool `json:"status,required"` Status bool `json:"status,required"`
Token string `json:"token,omitempty"` Token string `json:"token,omitempty"`
Users map[string]interface{} `json:"users,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) // 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) settingsMap, err := loadJSONFileToMap(System.File.Settings)
if err != nil { if err != nil {
@@ -135,6 +135,7 @@ func loadSettings() (settings SettingsStrcut, err error) {
defaults["update"] = []string{"0000"} defaults["update"] = []string{"0000"}
defaults["user.agent"] = System.Name defaults["user.agent"] = System.Name
defaults["uuid"] = createUUID() defaults["uuid"] = createUUID()
defaults["udpxy"] = ""
defaults["version"] = System.DBVersion defaults["version"] = System.DBVersion
defaults["xteveAutoUpdate"] = true defaults["xteveAutoUpdate"] = true
defaults["temp.path"] = System.Folder.Temp defaults["temp.path"] = System.Folder.Temp
@@ -186,7 +187,7 @@ func loadSettings() (settings SettingsStrcut, err error) {
} }
// Einstellungen speichern (xTeVe) // Einstellungen speichern (xTeVe)
func saveSettings(settings SettingsStrcut) (err error) { func saveSettings(settings SettingsStruct) (err error) {
if settings.BackupKeep == 0 { if settings.BackupKeep == 0 {
settings.BackupKeep = 10 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("/api/", API)
http.HandleFunc("/images/", Images) http.HandleFunc("/images/", Images)
http.HandleFunc("/data_images/", DataImages) http.HandleFunc("/data_images/", DataImages)
//http.HandleFunc("/auto/", Auto) //http.HandleFunc("/auto/", Auto)
showInfo("DVR IP:" + System.IPAddress + ":" + Settings.Port) showInfo("DVR IP:" + System.IPAddress + ":" + Settings.Port)
@@ -129,6 +130,12 @@ func Stream(w http.ResponseWriter, r *http.Request) {
return 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 { switch Settings.Buffer {
case "-": case "-":

View File

@@ -22,7 +22,7 @@ menuItems.push(new MainMenuItem("logout", "{{.mainMenu.item.logout}}", "logout.p
// Kategorien für die Einstellungen // Kategorien für die Einstellungen
var settingsCategory = new Array() var settingsCategory = new Array()
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.general}}", "xteveAutoUpdate,tuner,epgSource,api"));settingsCategory.push(new SettingsCategoryItem("{{.settings.category.files}}", "update,files.update,temp.path,cache.images,xepg.replace.missing.images")) 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.backup}}", "backup.path,backup.keep"))
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.authentication}}", "authentication.web,authentication.pms,authentication.m3u,authentication.xml,authentication.api")) settingsCategory.push(new SettingsCategoryItem("{{.settings.category.authentication}}", "authentication.web,authentication.pms,authentication.m3u,authentication.xml,authentication.api"))

View File

@@ -372,6 +372,21 @@ class SettingsCategory {
setting.appendChild(tdRight) setting.appendChild(tdRight)
break 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 return setting
@@ -483,6 +498,10 @@ class SettingsCategory {
text = "{{.settings.replaceEmptyImages.description}}" text = "{{.settings.replaceEmptyImages.description}}"
break break
case "udpxy":
text = "{{.settings.udpxy.description}}"
break
default: default:
text = "" text = ""
break break