Enhance XEPG channel mapping and settings management
continuous-integration/drone/push Build is passing
continuous-integration/drone/push Build is passing
This commit is contained in:
+10
@@ -85,6 +85,16 @@ func updateServerSettings(request RequestStruct) (settings SettingsStruct, err e
|
||||
case "xepg.replace.missing.images":
|
||||
createXEPGFiles = true
|
||||
|
||||
case "xepg.missing.epg.mode":
|
||||
if v, ok := value.(string); ok {
|
||||
mode := strings.ToLower(strings.TrimSpace(v))
|
||||
if mode != "relaxed" {
|
||||
mode = "strict"
|
||||
}
|
||||
value = mode
|
||||
}
|
||||
reloadData = true
|
||||
|
||||
case "backup.path":
|
||||
value = strings.TrimRight(value.(string), string(os.PathSeparator)) + string(os.PathSeparator)
|
||||
err = checkFolder(value.(string))
|
||||
|
||||
+67
-66
@@ -1,100 +1,101 @@
|
||||
package src
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ShowSystemInfo : Systeminformationen anzeigen
|
||||
func ShowSystemInfo() {
|
||||
|
||||
fmt.Print("Creating the information takes a moment...")
|
||||
err := buildDatabaseDVR()
|
||||
if err != nil {
|
||||
ShowError(err, 0)
|
||||
return
|
||||
}
|
||||
fmt.Print("Creating the information takes a moment...")
|
||||
err := buildDatabaseDVR()
|
||||
if err != nil {
|
||||
ShowError(err, 0)
|
||||
return
|
||||
}
|
||||
|
||||
buildXEPG(false)
|
||||
buildXEPG(false)
|
||||
|
||||
fmt.Println("OK")
|
||||
println()
|
||||
fmt.Println("OK")
|
||||
println()
|
||||
|
||||
fmt.Println(fmt.Sprintf("Version: %s %s.%s", System.Name, System.Version, System.Build))
|
||||
fmt.Println(fmt.Sprintf("Branch: %s", System.Branch))
|
||||
fmt.Println(fmt.Sprintf("GitHub: %s/%s | Git update = %t", System.GitHub.User, System.GitHub.Repo, System.GitHub.Update))
|
||||
fmt.Println(fmt.Sprintf("Folder (config): %s", System.Folder.Config))
|
||||
fmt.Println(fmt.Sprintf("Version: %s %s.%s", System.Name, System.Version, System.Build))
|
||||
fmt.Println(fmt.Sprintf("Branch: %s", System.Branch))
|
||||
fmt.Println(fmt.Sprintf("GitHub: %s/%s | Git update = %t", System.GitHub.User, System.GitHub.Repo, System.GitHub.Update))
|
||||
fmt.Println(fmt.Sprintf("Folder (config): %s", System.Folder.Config))
|
||||
|
||||
fmt.Println(fmt.Sprintf("Streams: %d / %d", len(Data.Streams.Active), len(Data.Streams.All)))
|
||||
fmt.Println(fmt.Sprintf("Filter: %d", len(Data.Filter)))
|
||||
fmt.Println(fmt.Sprintf("XEPG Chanels: %d", int(Data.XEPG.XEPGCount)))
|
||||
fmt.Println(fmt.Sprintf("Streams: %d / %d", len(Data.Streams.Active), len(Data.Streams.All)))
|
||||
fmt.Println(fmt.Sprintf("Filter: %d", len(Data.Filter)))
|
||||
fmt.Println(fmt.Sprintf("XEPG Chanels: %d", int(Data.XEPG.XEPGCount)))
|
||||
|
||||
println()
|
||||
fmt.Println(fmt.Sprintf("IPv4 Addresses:"))
|
||||
println()
|
||||
fmt.Println(fmt.Sprintf("IPv4 Addresses:"))
|
||||
|
||||
for i, ipv4 := range System.IPAddressesV4 {
|
||||
for i, ipv4 := range System.IPAddressesV4 {
|
||||
|
||||
switch count := i; {
|
||||
switch count := i; {
|
||||
|
||||
case count < 10:
|
||||
fmt.Println(fmt.Sprintf(" %d. %s", count, ipv4))
|
||||
break
|
||||
case count < 100:
|
||||
fmt.Println(fmt.Sprintf(" %d. %s", count, ipv4))
|
||||
break
|
||||
case count < 10:
|
||||
fmt.Println(fmt.Sprintf(" %d. %s", count, ipv4))
|
||||
break
|
||||
case count < 100:
|
||||
fmt.Println(fmt.Sprintf(" %d. %s", count, ipv4))
|
||||
break
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
println()
|
||||
fmt.Println(fmt.Sprintf("IPv6 Addresses:"))
|
||||
println()
|
||||
fmt.Println(fmt.Sprintf("IPv6 Addresses:"))
|
||||
|
||||
for i, ipv4 := range System.IPAddressesV6 {
|
||||
for i, ipv4 := range System.IPAddressesV6 {
|
||||
|
||||
switch count := i; {
|
||||
switch count := i; {
|
||||
|
||||
case count < 10:
|
||||
fmt.Println(fmt.Sprintf(" %d. %s", count, ipv4))
|
||||
break
|
||||
case count < 100:
|
||||
fmt.Println(fmt.Sprintf(" %d. %s", count, ipv4))
|
||||
break
|
||||
case count < 10:
|
||||
fmt.Println(fmt.Sprintf(" %d. %s", count, ipv4))
|
||||
break
|
||||
case count < 100:
|
||||
fmt.Println(fmt.Sprintf(" %d. %s", count, ipv4))
|
||||
break
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
println("---")
|
||||
println("---")
|
||||
|
||||
fmt.Println("Settings [General]")
|
||||
fmt.Println(fmt.Sprintf("xTeVe Update: %t", Settings.XteveAutoUpdate))
|
||||
fmt.Println(fmt.Sprintf("UUID: %s", Settings.UUID))
|
||||
fmt.Println(fmt.Sprintf("Tuner (Plex / Emby): %d", Settings.Tuner))
|
||||
fmt.Println(fmt.Sprintf("EPG Source: %s", Settings.EpgSource))
|
||||
fmt.Println("Settings [General]")
|
||||
fmt.Println(fmt.Sprintf("xTeVe Update: %t", Settings.XteveAutoUpdate))
|
||||
fmt.Println(fmt.Sprintf("UUID: %s", Settings.UUID))
|
||||
fmt.Println(fmt.Sprintf("Tuner (Plex / Emby): %d", Settings.Tuner))
|
||||
fmt.Println(fmt.Sprintf("EPG Source: %s", Settings.EpgSource))
|
||||
|
||||
println("---")
|
||||
println("---")
|
||||
|
||||
fmt.Println("Settings [Files]")
|
||||
fmt.Println(fmt.Sprintf("Schedule: %s", strings.Join(Settings.Update, ",")))
|
||||
fmt.Println(fmt.Sprintf("Files Update: %t", Settings.FilesUpdate))
|
||||
fmt.Println(fmt.Sprintf("Folder (tmp): %s", Settings.TempPath))
|
||||
fmt.Println(fmt.Sprintf("Image Chaching: %t", Settings.CacheImages))
|
||||
fmt.Println(fmt.Sprintf("Replace EPG Image: %t", Settings.XepgReplaceMissingImages))
|
||||
fmt.Println("Settings [Files]")
|
||||
fmt.Println(fmt.Sprintf("Schedule: %s", strings.Join(Settings.Update, ",")))
|
||||
fmt.Println(fmt.Sprintf("Files Update: %t", Settings.FilesUpdate))
|
||||
fmt.Println(fmt.Sprintf("Folder (tmp): %s", Settings.TempPath))
|
||||
fmt.Println(fmt.Sprintf("Image Chaching: %t", Settings.CacheImages))
|
||||
fmt.Println(fmt.Sprintf("Missing EPG Mode: %s", Settings.XepgMissingEPGMode))
|
||||
fmt.Println(fmt.Sprintf("Replace EPG Image: %t", Settings.XepgReplaceMissingImages))
|
||||
|
||||
println("---")
|
||||
println("---")
|
||||
|
||||
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))
|
||||
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))
|
||||
|
||||
println("---")
|
||||
println("---")
|
||||
|
||||
fmt.Println("Settings [Backup]")
|
||||
fmt.Println(fmt.Sprintf("Folder (backup): %s", Settings.BackupPath))
|
||||
fmt.Println(fmt.Sprintf("Backup Keep: %d", Settings.BackupKeep))
|
||||
fmt.Println("Settings [Backup]")
|
||||
fmt.Println(fmt.Sprintf("Folder (backup): %s", Settings.BackupPath))
|
||||
fmt.Println(fmt.Sprintf("Backup Keep: %d", Settings.BackupKeep))
|
||||
|
||||
}
|
||||
|
||||
@@ -300,6 +300,7 @@ type SettingsStruct struct {
|
||||
UUID string `json:"uuid"`
|
||||
UDPxy string `json:"udpxy"`
|
||||
Version string `json:"version"`
|
||||
XepgMissingEPGMode string `json:"xepg.missing.epg.mode"`
|
||||
XepgReplaceMissingImages bool `json:"xepg.replace.missing.images"`
|
||||
XteveAutoUpdate bool `json:"xteveAutoUpdate"`
|
||||
WizardCompleted bool `json:"wizard.completed"`
|
||||
|
||||
@@ -43,6 +43,7 @@ type RequestStruct struct {
|
||||
UDPxy *string `json:"udpxy,omitempty"`
|
||||
Update *[]string `json:"update,omitempty"`
|
||||
UserAgent *string `json:"user.agent,omitempty"`
|
||||
XepgMissingEPGMode *string `json:"xepg.missing.epg.mode,omitempty"`
|
||||
XepgReplaceMissingImages *bool `json:"xepg.replace.missing.images,omitempty"`
|
||||
XteveAutoUpdate *bool `json:"xteveAutoUpdate,omitempty"`
|
||||
SchemeM3U *string `json:"scheme.m3u,omitempty"`
|
||||
|
||||
@@ -141,6 +141,7 @@ func loadSettings() (settings SettingsStruct, err error) {
|
||||
defaults["language"] = "en"
|
||||
defaults["log.entries.ram"] = 500
|
||||
defaults["mapping.first.channel"] = 1000
|
||||
defaults["xepg.missing.epg.mode"] = "strict"
|
||||
defaults["xepg.replace.missing.images"] = true
|
||||
defaults["m3u8.adaptive.bandwidth.mbps"] = 10
|
||||
defaults["port"] = "34400"
|
||||
@@ -230,6 +231,11 @@ func saveSettings(settings SettingsStruct) (err error) {
|
||||
settings.UserAgent = defaultUserAgent
|
||||
}
|
||||
|
||||
settings.XepgMissingEPGMode = strings.ToLower(strings.TrimSpace(settings.XepgMissingEPGMode))
|
||||
if settings.XepgMissingEPGMode != "relaxed" {
|
||||
settings.XepgMissingEPGMode = "strict"
|
||||
}
|
||||
|
||||
settings.TempPath = strings.TrimRight(settings.TempPath, string(os.PathSeparator)) + string(os.PathSeparator)
|
||||
System.Folder.Temp = settings.TempPath + settings.UUID + string(os.PathSeparator)
|
||||
|
||||
|
||||
+37
-37
File diff suppressed because one or more lines are too long
+215
-7
@@ -9,6 +9,7 @@ import (
|
||||
"path"
|
||||
"runtime"
|
||||
"sort"
|
||||
"unicode"
|
||||
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
@@ -526,10 +527,132 @@ func createXEPGDatabase() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func normalizeXEPGMatchValue(value string) string {
|
||||
value = strings.TrimSpace(value)
|
||||
if len(value) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
return strings.Map(func(r rune) rune {
|
||||
switch {
|
||||
case unicode.IsLetter(r):
|
||||
return unicode.ToLower(r)
|
||||
case unicode.IsDigit(r):
|
||||
return r
|
||||
default:
|
||||
return -1
|
||||
}
|
||||
}, value)
|
||||
}
|
||||
|
||||
func appendXEPGIssueSample(samples map[string]struct{}, value string) {
|
||||
value = strings.TrimSpace(value)
|
||||
if len(value) == 0 {
|
||||
return
|
||||
}
|
||||
samples[value] = struct{}{}
|
||||
}
|
||||
|
||||
func xepgIssueSampleText(samples map[string]struct{}, max int) string {
|
||||
if len(samples) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
list := make([]string, 0, len(samples))
|
||||
for name := range samples {
|
||||
list = append(list, name)
|
||||
}
|
||||
sort.Strings(list)
|
||||
|
||||
if len(list) > max {
|
||||
return strings.Join(list[:max], ", ") + ", ..."
|
||||
}
|
||||
|
||||
return strings.Join(list, ", ")
|
||||
}
|
||||
|
||||
func findXEPGReplacementChannel(xmltvChannels map[string]any, xepgChannel XEPGChannelStruct) (channelID string, channel map[string]any, ok bool) {
|
||||
var candidateValues = map[string]struct{}{}
|
||||
|
||||
addCandidate := func(value string) {
|
||||
normalized := normalizeXEPGMatchValue(value)
|
||||
if len(normalized) == 0 {
|
||||
return
|
||||
}
|
||||
candidateValues[normalized] = struct{}{}
|
||||
}
|
||||
|
||||
addCandidate(xepgChannel.XName)
|
||||
addCandidate(xepgChannel.Name)
|
||||
addCandidate(xepgChannel.TvgName)
|
||||
addCandidate(xepgChannel.TvgID)
|
||||
|
||||
if len(candidateValues) == 0 {
|
||||
return "", nil, false
|
||||
}
|
||||
|
||||
if _, exists := candidateValues[normalizeXEPGMatchValue(xepgChannel.TvgID)]; exists {
|
||||
if direct, found := xmltvChannels[xepgChannel.TvgID].(map[string]any); found {
|
||||
return xepgChannel.TvgID, direct, true
|
||||
}
|
||||
}
|
||||
|
||||
var matches []struct {
|
||||
id string
|
||||
channel map[string]any
|
||||
}
|
||||
|
||||
for id, data := range xmltvChannels {
|
||||
xmltvChannel, castOK := data.(map[string]any)
|
||||
if castOK == false {
|
||||
continue
|
||||
}
|
||||
|
||||
if _, match := candidateValues[normalizeXEPGMatchValue(id)]; match {
|
||||
matches = append(matches, struct {
|
||||
id string
|
||||
channel map[string]any
|
||||
}{id: id, channel: xmltvChannel})
|
||||
continue
|
||||
}
|
||||
|
||||
displayName, hasDisplayName := xmltvChannel["display-name"].(string)
|
||||
if hasDisplayName == false {
|
||||
continue
|
||||
}
|
||||
|
||||
if _, match := candidateValues[normalizeXEPGMatchValue(displayName)]; match {
|
||||
matches = append(matches, struct {
|
||||
id string
|
||||
channel map[string]any
|
||||
}{id: id, channel: xmltvChannel})
|
||||
}
|
||||
}
|
||||
|
||||
if len(matches) != 1 {
|
||||
return "", nil, false
|
||||
}
|
||||
|
||||
return matches[0].id, matches[0].channel, true
|
||||
}
|
||||
|
||||
// Kanäle automatisch zuordnen und das Mapping überprüfen
|
||||
func mapping() (err error) {
|
||||
showInfo("XEPG:" + "Map channels")
|
||||
|
||||
strictMissingEPGMode := Settings.XepgMissingEPGMode != "relaxed"
|
||||
|
||||
missingEPGCount := 0
|
||||
missingXMLTVCount := 0
|
||||
autoRemapCount := 0
|
||||
relaxedKeepCount := 0
|
||||
relaxedDummyCount := 0
|
||||
missingEPGSamples := map[string]struct{}{}
|
||||
missingXMLTVSamples := map[string]struct{}{}
|
||||
autoRemapSamples := map[string]struct{}{}
|
||||
relaxedKeepSamples := map[string]struct{}{}
|
||||
relaxedDummySamples := map[string]struct{}{}
|
||||
|
||||
for xepg, dxc := range Data.XEPG.Channels {
|
||||
|
||||
var xepgChannel XEPGChannelStruct
|
||||
@@ -604,20 +727,68 @@ func mapping() (err error) {
|
||||
}
|
||||
|
||||
} else {
|
||||
if channelID, replacementChannel, remapOK := findXEPGReplacementChannel(value, xepgChannel); remapOK {
|
||||
xepgChannel.XMapping = channelID
|
||||
|
||||
ShowError(fmt.Errorf("Missing EPG data: %s", xepgChannel.Name), 0)
|
||||
showWarning(2302)
|
||||
xepgChannel.XActive = false
|
||||
if logo, ok := replacementChannel["icon"].(string); ok {
|
||||
if xepgChannel.XUpdateChannelIcon == true && len(logo) > 0 {
|
||||
xepgChannel.TvgLogo = logo
|
||||
}
|
||||
}
|
||||
|
||||
autoRemapCount++
|
||||
name := strings.TrimSpace(xepgChannel.Name)
|
||||
if len(name) == 0 {
|
||||
name = strings.TrimSpace(xepgChannel.XName)
|
||||
}
|
||||
if len(name) == 0 {
|
||||
name = xepg
|
||||
}
|
||||
appendXEPGIssueSample(autoRemapSamples, name)
|
||||
} else {
|
||||
name := strings.TrimSpace(xepgChannel.Name)
|
||||
if len(name) == 0 {
|
||||
name = strings.TrimSpace(xepgChannel.XName)
|
||||
}
|
||||
if len(name) == 0 {
|
||||
name = xepg
|
||||
}
|
||||
|
||||
if strictMissingEPGMode == true {
|
||||
missingEPGCount++
|
||||
appendXEPGIssueSample(missingEPGSamples, name)
|
||||
xepgChannel.XActive = false
|
||||
} else {
|
||||
relaxedKeepCount++
|
||||
appendXEPGIssueSample(relaxedKeepSamples, name)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
var fileID = strings.TrimSuffix(getFilenameFromPath(file), path.Ext(getFilenameFromPath(file)))
|
||||
providerName := getProviderParameter(fileID, "xmltv", "name")
|
||||
if len(strings.TrimSpace(providerName)) == 0 {
|
||||
providerName = file
|
||||
}
|
||||
|
||||
ShowError(fmt.Errorf("Missing XMLTV file: %s", getProviderParameter(fileID, "xmltv", "name")), 0)
|
||||
showWarning(2301)
|
||||
xepgChannel.XActive = false
|
||||
if strictMissingEPGMode == true {
|
||||
missingXMLTVCount++
|
||||
appendXEPGIssueSample(missingXMLTVSamples, providerName)
|
||||
xepgChannel.XActive = false
|
||||
} else {
|
||||
relaxedDummyCount++
|
||||
name := strings.TrimSpace(xepgChannel.Name)
|
||||
if len(name) == 0 {
|
||||
name = strings.TrimSpace(xepgChannel.XName)
|
||||
}
|
||||
if len(name) == 0 {
|
||||
name = xepg
|
||||
}
|
||||
appendXEPGIssueSample(relaxedDummySamples, name)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -639,6 +810,28 @@ func mapping() (err error) {
|
||||
|
||||
}
|
||||
|
||||
if autoRemapCount > 0 {
|
||||
showInfo(fmt.Sprintf("XEPG:%d channel mappings were auto-remapped (examples: %s)", autoRemapCount, xepgIssueSampleText(autoRemapSamples, 8)))
|
||||
}
|
||||
|
||||
if missingEPGCount > 0 {
|
||||
showWarning(2302)
|
||||
showInfo(fmt.Sprintf("XEPG:%d channels have missing EPG mappings and were deactivated (examples: %s)", missingEPGCount, xepgIssueSampleText(missingEPGSamples, 8)))
|
||||
}
|
||||
|
||||
if missingXMLTVCount > 0 {
|
||||
showWarning(2301)
|
||||
showInfo(fmt.Sprintf("XEPG:%d channels reference missing XMLTV files and were deactivated (sources: %s)", missingXMLTVCount, xepgIssueSampleText(missingXMLTVSamples, 5)))
|
||||
}
|
||||
|
||||
if relaxedKeepCount > 0 {
|
||||
showInfo(fmt.Sprintf("XEPG:%d channels kept active in relaxed mode despite missing EPG mappings (examples: %s)", relaxedKeepCount, xepgIssueSampleText(relaxedKeepSamples, 8)))
|
||||
}
|
||||
|
||||
if relaxedDummyCount > 0 {
|
||||
showInfo(fmt.Sprintf("XEPG:%d channels will use xTeVe Dummy guide in relaxed mode because XMLTV sources were unavailable (examples: %s)", relaxedDummyCount, xepgIssueSampleText(relaxedDummySamples, 8)))
|
||||
}
|
||||
|
||||
err = saveMapToJSONFile(System.File.XEPG, Data.XEPG.Channels)
|
||||
if err != nil {
|
||||
return
|
||||
@@ -740,6 +933,13 @@ func getProgramData(xepgChannel XEPGChannelStruct) (xepgXML XMLTV, err error) {
|
||||
|
||||
var xmltvFile = System.Folder.Data + xepgChannel.XmltvFile
|
||||
var channelID = xepgChannel.XMapping
|
||||
relaxedMissingEPGMode := Settings.XepgMissingEPGMode == "relaxed"
|
||||
fallbackToDummy := func() {
|
||||
dummyChannel := xepgChannel
|
||||
dummyChannel.XmltvFile = "xTeVe Dummy"
|
||||
dummyChannel.XMapping = "240_Minutes"
|
||||
xepgXML = createDummyProgram(dummyChannel)
|
||||
}
|
||||
|
||||
var xmltv XMLTV
|
||||
|
||||
@@ -749,6 +949,10 @@ func getProgramData(xepgChannel XEPGChannelStruct) (xepgXML XMLTV, err error) {
|
||||
|
||||
err = getLocalXMLTV(xmltvFile, &xmltv)
|
||||
if err != nil {
|
||||
if relaxedMissingEPGMode == true {
|
||||
fallbackToDummy()
|
||||
err = nil
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -822,6 +1026,10 @@ func getProgramData(xepgChannel XEPGChannelStruct) (xepgXML XMLTV, err error) {
|
||||
|
||||
}
|
||||
|
||||
if len(xepgXML.Program) == 0 && relaxedMissingEPGMode == true && xmltvFile != System.Folder.Data+"xTeVe Dummy" {
|
||||
fallbackToDummy()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -835,7 +1043,7 @@ func createDummyProgram(xepgChannel XEPGChannelStruct) (dummyXMLTV XMLTV) {
|
||||
var currentDay = currentTime.Format("20060102")
|
||||
var startTime, _ = time.Parse("20060102150405", currentDay+"000000")
|
||||
|
||||
showInfo("Create Dummy Guide:" + "Time offset" + offset + " - " + xepgChannel.XName)
|
||||
showDebug("Create Dummy Guide:"+"Time offset"+offset+" - "+xepgChannel.XName, 2)
|
||||
|
||||
var dl = strings.Split(xepgChannel.XMapping, "_")
|
||||
dummyLength, err := strconv.Atoi(dl[0])
|
||||
|
||||
Reference in New Issue
Block a user