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();
|
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) {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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.",
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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{})
|
||||||
|
|||||||
@@ -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))
|
||||||
|
|||||||
@@ -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"`
|
||||||
|
|||||||
@@ -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"`
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
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("/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 "-":
|
||||||
|
|||||||
@@ -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"))
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user