Skip to content

Commit d2ff15a

Browse files
Add feature to select binding IPs (marcelGoerentz#30)
* Add functions to update, cancel and close button and finalize the feature. Update note texts * Add update button * Fix bug where only ; will be shown when no IP has been selected
1 parent 9882637 commit d2ff15a

11 files changed

+114
-89
lines changed

html/lang/en.json

+5-5
Original file line numberDiff line numberDiff line change
@@ -461,10 +461,10 @@
461461
"title": "Omit port",
462462
"description": "By activating this checkbox the m3u file will not contain the port in the url"
463463
},
464-
"listeningIp" :
464+
"bindingIPs" :
465465
{
466-
"title": "Listening IPs",
467-
"description": "Enter the listening IPs seprated by an ';' for example: 127.0.0.1;192.168.2.1<br>Changing this option will exit the program! You need to restart it if you didn't configured a restart!"
466+
"title": "Binding IP(s)",
467+
"description": "By clicking on the input a dialogue will open where you can select the IP(s)<br>Please note that these are also the DVR IPs<br>Changing this option and saving the settings will exit the program!<br>You need to restart it if you didn't configured an automatical restart!"
468468
},
469469
"forceHttps":
470470
{
@@ -474,7 +474,7 @@
474474
"useHttps":
475475
{
476476
"title": "Use HTTPS",
477-
"description": "Enable HTTPS protocol for Threadfin, HTTPS Port needs to be set, otherwise the 443 will be used!"
477+
"description": "Enable HTTPS protocol for Threadfin, HTTPS Port needs to be set, otherwise the 443 will be used!<br>Also make sure that server.crt and server.key are placed in the config folder!<br>The key file needs to be unencrypted!"
478478
},
479479
"forceClientHttps":
480480
{
@@ -484,7 +484,7 @@
484484
"threadfinDomain":
485485
{
486486
"title": "Threadfin Domain",
487-
"description": "When not empty, this will rewrite the URLs in the new m3u to a FQDN.<br>Do NOT include http (ex: somedomain.com)</br>"
487+
"description": "When not empty, this will rewrite the URLs in the new m3u to a FQDN.<br>It will also rewrite the shown DVR IP in the server info dialogue<br>Do NOT include http (ex: somedomain.com)</br>"
488488
},
489489
"enableNonAscii":
490490
{

src/info.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ func ShowSystemInfo() {
8484
println("---")
8585

8686
fmt.Println("Network")
87-
fmt.Printf("Listening IPs: %s\n", Settings.ListeningIp)
87+
fmt.Printf("Binding IP(s): %s\n", Settings.BindingIPs)
8888
fmt.Printf("Threadfin Domain: %s\n", Settings.ThreadfinDomain)
8989
fmt.Printf("Use Https: %t\n", Settings.UseHttps)
9090
fmt.Printf("Fort Https to upstream: %t\n", Settings.ForceHttpsToUpstream)

src/struct-system.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ type SettingsStruct struct {
324324
ThreadfinAutoUpdate bool `json:"ThreadfinAutoUpdate"`
325325
StoreBufferInRAM bool `json:"storeBufferInRAM"`
326326
OmitPorts bool `json:"omitPorts"`
327-
ListeningIp string `json:"listeningIp"`
327+
BindingIPs string `json:"bindingIPs"`
328328
ForceHttpsToUpstream bool `json:"forceHttps"`
329329
UseHttps bool `json:"useHttps"`
330330
ForceClientHttps bool `json:"forceClientHttps"`

src/struct-webserver.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ type RequestStruct struct {
4848
SchemeXML *string `json:"scheme.xml,omitempty"`
4949
StoreBufferInRAM *bool `json:"storeBufferInRAM,omitempty"`
5050
OmitPorts *bool `json:"omitPorts,omitempty"`
51-
ListeningIp *string `json:"listeningIp,omitempty"`
51+
BindingIPs *string `json:"bindingIPs,omitempty"`
5252
ForceHttpsToUpstream *bool `json:"forceHttps,omitempty"`
5353
UseHttps *bool `json:"useHttps,omitempty"`
5454
ForceClientHttps *bool `json:"forceClientHttps"`

src/system.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ func loadSettings() (settings SettingsStruct, err error) {
150150
defaults["ssdp"] = true
151151
defaults["storeBufferInRAM"] = true
152152
defaults["omitPorts"] = false
153-
defaults["listeningIp"]= ""
153+
defaults["bindingIPs"]= ""
154154
defaults["forceHttps"] = false
155155
defaults["useHttps"] = false
156156
defaults["threadfinDomain"] = ""

src/webUI.go

+30-30
Large diffs are not rendered by default.

src/webserver.go

+7-4
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ func StartWebserver() (err error) {
4646
regexIpV4, _ := regexp.Compile(`(?:\d{1,3}\.){3}\d{1,3}`)
4747
regexIpV6, _ := regexp.Compile(`(?:[A-Fa-f0-9]{0,4}:){3,7}[a-fA-F0-9]{1,4}`)
4848
var customIps []string
49-
var customIpsV4 = regexIpV4.FindAllString(Settings.ListeningIp, -1)
50-
var customIpsV6 = regexIpV6.FindAllString(Settings.ListeningIp, -1)
49+
var customIpsV4 = regexIpV4.FindAllString(Settings.BindingIPs, -1)
50+
var customIpsV6 = regexIpV6.FindAllString(Settings.BindingIPs, -1)
5151
if customIpsV4 != nil || customIpsV6 != nil {
5252
customIps=make([]string, len(customIpsV4)+ len(customIpsV6))
5353
copy(customIps, customIpsV4)
@@ -56,6 +56,7 @@ func StartWebserver() (err error) {
5656

5757
if customIps != nil {
5858
for _, address := range customIps {
59+
showInfo("DVR IP:" + address + ":" + Settings.Port)
5960
showHighlight(fmt.Sprintf("Web Interface:%s://%s:%s/web/", System.ServerProtocol, address, Settings.Port))
6061
if Settings.UseHttps {
6162
go func(address string) {
@@ -76,10 +77,12 @@ func StartWebserver() (err error) {
7677
}
7778
} else {
7879
for _, ip := range System.IPAddressesV4 {
80+
showInfo("DVR IP:" + ip + ":" + Settings.Port)
7981
showHighlight(fmt.Sprintf("Web Interface:%s://%s:%s/web/", System.ServerProtocol, ip, Settings.Port))
8082
}
8183

8284
for _, ip := range System.IPAddressesV6 {
85+
showInfo("DVR IP:" + ip + ":" + Settings.Port)
8386
showHighlight(fmt.Sprintf("Web Interface:%s://%s:%s/web/", System.ServerProtocol, ip, Settings.Port))
8487
}
8588
if Settings.UseHttps {
@@ -559,7 +562,7 @@ func WS(w http.ResponseWriter, r *http.Request) {
559562
showInfo("WEB:Saving settings")
560563
var authenticationUpdate = Settings.AuthenticationWEB
561564
var previousStoreBufferInRAM = Settings.StoreBufferInRAM
562-
var previousListeningIP = Settings.ListeningIp
565+
var previousBindingIPs = Settings.BindingIPs
563566
var previousUseHttps = Settings.UseHttps
564567
response.Settings, err = updateServerSettings(request)
565568
if err == nil {
@@ -574,7 +577,7 @@ func WS(w http.ResponseWriter, r *http.Request) {
574577
initBufferVFS(Settings.StoreBufferInRAM)
575578
}
576579

577-
if Settings.ListeningIp != previousListeningIP || Settings.UseHttps != previousUseHttps {
580+
if Settings.BindingIPs != previousBindingIPs || Settings.UseHttps != previousUseHttps {
578581
showInfo("WEB:Restart program since listening IP option has been changed!")
579582
os.Exit(0)
580583
}

threadfin.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,13 @@ var GitHub = GitHubStruct{Branch: "Main", User: "marcelGoerentz", Repo: "Threadf
4242
const Name = "Threadfin"
4343

4444
// Version : Version, die Build Nummer wird in der main func geparst.
45-
const Version = "1.4.0-beta"
45+
const Version = "1.4.1-beta"
4646

4747
// DBVersion : Datanbank Version
4848
const DBVersion = "0.5.0"
4949

5050
// APIVersion : API Version
51-
const APIVersion = "1.4.0-beta"
51+
const APIVersion = "1.4.1-beta"
5252

5353
var homeDirectory = fmt.Sprintf("%s%s.%s%s", src.GetUserHomeDirectory(), string(os.PathSeparator), strings.ToLower(Name), string(os.PathSeparator))
5454
var samplePath = fmt.Sprintf("%spath%sto%sthreadfin%s", string(os.PathSeparator), string(os.PathSeparator), string(os.PathSeparator), string(os.PathSeparator))

ts/base_ts.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ menuItems.push(new MainMenuItem("logout", "{{.mainMenu.item.logout}}", "logout.p
4747
var settingsCategory = new Array()
4848
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.general}}", "ThreadfinAutoUpdate,ssdp,tuner,epgSource,epgCategories,epgCategoriesColors,dummy,dummyChannel,ignoreFilters,api"))
4949
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.files}}", "update,files.update,temp.path,cache.images,omitPorts,xepg.replace.missing.images,xepg.replace.channel.title,enableNonAscii"))
50-
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.network}}", "listeningIp,threadfinDomain,useHttps,forceClientHttps,forceHttps"))
50+
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.network}}", "bindingIPs,threadfinDomain,useHttps,forceClientHttps,forceHttps"))
5151
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.streaming}}", "buffer,udpxy,buffer.size.kb,storeBufferInRAM,buffer.timeout,user.agent,ffmpeg.path,ffmpeg.options,vlc.path,vlc.options"))
5252
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.backup}}", "backup.path,backup.keep"))
5353
settingsCategory.push(new SettingsCategoryItem("{{.settings.category.authentication}}", "authentication.web,authentication.pms,authentication.m3u,authentication.xml,authentication.api"))

ts/menu_ts.ts

+59-36
Original file line numberDiff line numberDiff line change
@@ -1098,59 +1098,82 @@ function PageReady() {
10981098
return
10991099
}
11001100

1101+
var checkboxesInitialState: boolean[] = []
1102+
11011103
async function setCheckboxes() {
1102-
var content: PopupContent = new PopupContent()
1103-
var table = document.getElementById("checkboxTable")
1104+
const content: PopupContent = new PopupContent()
1105+
const table = document.getElementById("checkboxTable")
11041106
await new Promise(f => setTimeout(f, 1000));
11051107
if ("clientInfo" in SERVER) {
1106-
let listeningIp: string = SERVER['settings']['listeningIp']
1107-
var listeningIpArray = listeningIp.split(";")
1108-
let systemnIPs: Array<string> = SERVER["clientInfo"]["systemIPs"]
1108+
const bindingIPs: string = SERVER['settings']['bindingIPs']
1109+
const bindingIPspArray = bindingIPs.split(";")
1110+
const systemnIPs: Array<string> = SERVER["clientInfo"]["systemIPs"]
11091111
systemnIPs.forEach((ipAddress, index) => {
11101112
if (!ipAddress.includes('169.254')) {
1111-
var tr = document.createElement('tr')
1112-
var tdLeft = document.createElement('td')
1113-
var tdRight = document.createElement('td')
1114-
1115-
var checkbox = content.createCheckbox(ipAddress, 'ipCheckbox' + index)
1116-
checkbox.checked = false
1117-
listeningIpArray.forEach(element => {
1118-
if (element === ipAddress) {
1119-
checkbox.checked = true
1120-
return;
1121-
}
1122-
});
1123-
var label = document.createElement("label")
1113+
const tr = document.createElement('tr')
1114+
const tdLeft = document.createElement('td')
1115+
const tdRight = document.createElement('td')
1116+
1117+
const checkbox = content.createCheckbox(ipAddress, 'ipCheckbox' + index)
1118+
checkbox.checked = bindingIPspArray.includes(ipAddress)
1119+
1120+
const label = document.createElement("label")
11241121
label.setAttribute("for", "ipCheckbox" + index)
11251122
label.innerHTML = ipAddress
1123+
11261124
tdLeft.appendChild(checkbox)
11271125
tdRight.appendChild(label)
11281126
tr.appendChild(tdLeft)
11291127
tr.appendChild(tdRight)
11301128
table.appendChild(tr)
11311129
}
11321130
});
1133-
var checkbox_container = document.getElementById("checkbox_container")
1134-
var input = content.createInput("button", "buttonSave", "{{.button.save}}")
1135-
input.setAttribute("onclick", 'javascript: saveBindingIPs()')
1136-
input.setAttribute('data-bs-target', '#ip_selection')
1137-
input.setAttribute("data-bs-toggle" , "modal")
1138-
checkbox_container.appendChild(input)
1131+
1132+
const checkbox_container = document.getElementById("checkbox_container");
1133+
const saveButton = createButton(content, "buttonUpdate", "{{.button.update}}", 'javsrcipt: updateBindingIPs()');
1134+
const cancelButton = createButton(content, "buttonCancel", "{{.button.cancel}}");
1135+
checkbox_container.appendChild(saveButton);
1136+
checkbox_container.appendChild(cancelButton);
1137+
1138+
const checkboxes = checkbox_container.querySelectorAll('input[type="checkbox"]');
1139+
checkboxesInitialState = Array.from(checkboxes).map(checkbox => (checkbox as HTMLInputElement).checked);
1140+
const ipSelection = document.getElementById('ip_selection');
1141+
const closeButton = ipSelection.querySelector('button.btn-close')
1142+
closeButton.addEventListener('click', () => resetCheckboxes(checkboxes, checkboxesInitialState))
1143+
cancelButton.addEventListener('click', () => resetCheckboxes(checkboxes, checkboxesInitialState))
11391144
}
11401145
}
11411146

1142-
function saveBindingIPs() {
1143-
var checkboxTable = document.getElementById('checkboxTable')
1144-
var checkboxList = checkboxTable.querySelectorAll('input')
1145-
var listeningIPs: string[] = []
1146-
checkboxList.forEach(checkbox => {
1147-
if (checkbox.checked) {
1148-
listeningIPs.push(checkbox.name)
1149-
}
1150-
});
1151-
var listeningIp = document.getElementById('listeningIp')
1152-
listeningIp.setAttribute('value', listeningIPs.join(';') + ";")
1153-
listeningIp.setAttribute('class', 'changed')
1147+
function createButton(content: PopupContent, id: string, text: string, onClick?: string): HTMLInputElement {
1148+
const button = content.createInput("button", id, text);
1149+
if (onClick) {
1150+
button.setAttribute("onclick", onClick);
1151+
}
1152+
button.setAttribute('data-bs-target', '#ip_selection');
1153+
button.setAttribute("data-bs-toggle", "modal");
1154+
return button;
1155+
}
1156+
1157+
function resetCheckboxes(checkboxes: NodeListOf<Element>, initialStates: boolean[]) {
1158+
checkboxes.forEach((checkbox, index) => {
1159+
(checkbox as HTMLInputElement).checked = initialStates[index]
1160+
})
1161+
}
1162+
1163+
function updateBindingIPs() {
1164+
const checkboxTable = document.getElementById('checkboxTable');
1165+
const checkboxList = checkboxTable.querySelectorAll('input[type="checkbox"]');
1166+
var bindingIPs: string[] = Array.from(checkboxList)
1167+
.filter(checkbox => (checkbox as HTMLInputElement).checked)
1168+
.map(checkbox => (checkbox as HTMLInputElement).name);
1169+
const bindingIPsElement = document.getElementById('bindingIPs');
1170+
if (bindingIPs.length === 0) {
1171+
bindingIPsElement.setAttribute('value', '')
1172+
} else {
1173+
bindingIPsElement.setAttribute('value', bindingIPs.join(';') + ";");
1174+
}
1175+
bindingIPsElement.setAttribute('class', 'changed');
1176+
checkboxesInitialState = Array.from(checkboxList).map(checkbox => (checkbox as HTMLInputElement).checked);
11541177
}
11551178

11561179
function createLayout() {

ts/settings_ts.ts

+6-7
Original file line numberDiff line numberDiff line change
@@ -145,14 +145,13 @@ class SettingsCategory {
145145
setting.appendChild(tdRight)
146146
break
147147

148-
case "listeningIp":
148+
case "bindingIPs":
149149
var tdLeft = document.createElement("TD")
150-
tdLeft.innerHTML = "{{.settings.listeningIp.title}}" + ":"
150+
tdLeft.innerHTML = "{{.settings.bindingIPs.title}}" + ":"
151151
var tdRight = document.createElement("TD")
152-
var input = content.createInput("text", "listeningIp", data)
152+
var input = content.createInput("text", "bindingIPs", data)
153153
input.setAttribute("id", settingsKey)
154-
input.setAttribute("placeholder", "{{.settings.listeningIp.placeholder}}")
155-
input.setAttribute("onchange", "javascript: this.className = 'changed'")
154+
input.setAttribute("placeholder", "{{.settings.bindingIPs.placeholder}}")
156155
input.setAttribute('data-bs-target', '#ip_selection')
157156
input.setAttribute("data-bs-toggle" , "modal")
158157
tdRight.appendChild(input)
@@ -654,8 +653,8 @@ class SettingsCategory {
654653
text = "{{.settings.ThreadfinAutoUpdate.description}}"
655654
break
656655

657-
case "listeningIp":
658-
text = "{{.settings.listeningIp.description}}"
656+
case "bindingIPs":
657+
text = "{{.settings.bindingIPs.description}}"
659658
break
660659

661660
case "backup.keep":

0 commit comments

Comments
 (0)