990 lines
22 KiB
Go
990 lines
22 KiB
Go
package src
|
|
|
|
import (
|
|
"encoding/json"
|
|
"encoding/xml"
|
|
"errors"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"path"
|
|
"runtime"
|
|
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// Provider XMLTV Datei überprüfen
|
|
func checkXMLCompatibility(id string, body []byte) (err error) {
|
|
|
|
var xmltv XMLTV
|
|
var compatibility = make(map[string]int)
|
|
|
|
err = xml.Unmarshal(body, &xmltv)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
compatibility["xmltv.channels"] = len(xmltv.Channel)
|
|
compatibility["xmltv.programs"] = len(xmltv.Program)
|
|
|
|
setProviderCompatibility(id, "xmltv", compatibility)
|
|
|
|
return
|
|
}
|
|
|
|
// XEPG Daten erstellen
|
|
func buildXEPG(background bool) {
|
|
|
|
if System.ScanInProgress == 1 {
|
|
return
|
|
}
|
|
|
|
System.ScanInProgress = 1
|
|
|
|
if Settings.EpgSource == "XEPG" {
|
|
|
|
switch background {
|
|
|
|
case true:
|
|
|
|
go func() {
|
|
|
|
createXEPGMapping()
|
|
createXEPGDatabase()
|
|
mapping()
|
|
cleanupXEPG()
|
|
createXMLTVFile()
|
|
createM3UFile()
|
|
go cachingImages()
|
|
|
|
showInfo("XEPG:" + fmt.Sprintf("Ready to use"))
|
|
|
|
System.ScanInProgress = 0
|
|
|
|
// Cache löschen
|
|
/*
|
|
Data.Cache.XMLTV = make(map[string]XMLTV)
|
|
Data.Cache.XMLTV = nil
|
|
*/
|
|
runtime.GC()
|
|
|
|
}()
|
|
|
|
case false:
|
|
|
|
createXEPGMapping()
|
|
createXEPGDatabase()
|
|
mapping()
|
|
cleanupXEPG()
|
|
|
|
go func() {
|
|
|
|
createXMLTVFile()
|
|
createM3UFile()
|
|
go cachingImages()
|
|
showInfo("XEPG:" + fmt.Sprintf("Ready to use"))
|
|
|
|
System.ScanInProgress = 0
|
|
|
|
// Cache löschen
|
|
//Data.Cache.XMLTV = make(map[string]XMLTV)
|
|
//Data.Cache.XMLTV = nil
|
|
runtime.GC()
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
getLineup()
|
|
System.ScanInProgress = 0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// XEPG Daten aktualisieren
|
|
func updateXEPG(background bool) {
|
|
|
|
if System.ScanInProgress == 1 {
|
|
return
|
|
}
|
|
|
|
System.ScanInProgress = 1
|
|
|
|
if Settings.EpgSource == "XEPG" {
|
|
|
|
switch background {
|
|
|
|
case false:
|
|
|
|
createXEPGDatabase()
|
|
mapping()
|
|
cleanupXEPG()
|
|
|
|
go func() {
|
|
|
|
createXMLTVFile()
|
|
createM3UFile()
|
|
showInfo("XEPG:" + fmt.Sprintf("Ready to use"))
|
|
|
|
System.ScanInProgress = 0
|
|
|
|
}()
|
|
|
|
case true:
|
|
System.ScanInProgress = 0
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
System.ScanInProgress = 0
|
|
|
|
}
|
|
|
|
// Cache löschen
|
|
//Data.Cache.XMLTV = nil //make(map[string]XMLTV)
|
|
//Data.Cache.XMLTV = make(map[string]XMLTV)
|
|
|
|
return
|
|
}
|
|
|
|
// Mapping Menü für die XMLTV Dateien erstellen
|
|
func createXEPGMapping() {
|
|
|
|
Data.XMLTV.Files = getLocalProviderFiles("xmltv")
|
|
Data.XMLTV.Mapping = make(map[string]interface{})
|
|
|
|
var tmpMap = make(map[string]interface{})
|
|
|
|
var friendlyDisplayName = func(channel Channel) (displayName string) {
|
|
var dn = channel.DisplayName
|
|
displayName = dn[0].Value
|
|
|
|
switch len(dn) {
|
|
case 1:
|
|
displayName = dn[0].Value
|
|
default:
|
|
displayName = fmt.Sprintf("%s (%s)", dn[1].Value, dn[0].Value)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
if len(Data.XMLTV.Files) > 0 {
|
|
|
|
for i := len(Data.XMLTV.Files) - 1; i >= 0; i-- {
|
|
|
|
var file = Data.XMLTV.Files[i]
|
|
|
|
var err error
|
|
var fileID = strings.TrimSuffix(getFilenameFromPath(file), path.Ext(getFilenameFromPath(file)))
|
|
showInfo("XEPG:" + "Parse XMLTV file: " + getProviderParameter(fileID, "xmltv", "name"))
|
|
|
|
//xmltv, err = getLocalXMLTV(file)
|
|
var xmltv XMLTV
|
|
|
|
err = getLocalXMLTV(file, &xmltv)
|
|
if err != nil {
|
|
Data.XMLTV.Files = append(Data.XMLTV.Files, Data.XMLTV.Files[i+1:]...)
|
|
var errMsg = err.Error()
|
|
err = errors.New(getProviderParameter(fileID, "xmltv", "name") + ": " + errMsg)
|
|
ShowError(err, 000)
|
|
}
|
|
|
|
// XML Parsen (Provider Datei)
|
|
if err == nil {
|
|
|
|
// Daten aus der XML Datei in eine temporäre Map schreiben
|
|
var xmltvMap = make(map[string]interface{})
|
|
|
|
for _, c := range xmltv.Channel {
|
|
var channel = make(map[string]interface{})
|
|
|
|
channel["id"] = c.ID
|
|
channel["display-name"] = friendlyDisplayName(*c)
|
|
channel["icon"] = c.Icon.Src
|
|
|
|
xmltvMap[c.ID] = channel
|
|
|
|
}
|
|
|
|
tmpMap[getFilenameFromPath(file)] = xmltvMap
|
|
Data.XMLTV.Mapping[getFilenameFromPath(file)] = xmltvMap
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Data.XMLTV.Mapping = tmpMap
|
|
tmpMap = make(map[string]interface{})
|
|
|
|
} else {
|
|
|
|
if System.ConfigurationWizard == false {
|
|
showWarning(1007)
|
|
}
|
|
|
|
}
|
|
|
|
// Auswahl für den Dummy erstellen
|
|
var dummy = make(map[string]interface{})
|
|
var times = []string{"30", "60", "90", "120"}
|
|
|
|
for _, i := range times {
|
|
|
|
var dummyChannel = make(map[string]string)
|
|
dummyChannel["display-name"] = i + " Minutes"
|
|
dummyChannel["id"] = i + "_Minutes"
|
|
dummyChannel["icon"] = ""
|
|
|
|
dummy[dummyChannel["id"]] = dummyChannel
|
|
|
|
}
|
|
|
|
Data.XMLTV.Mapping["xTeVe Dummy"] = dummy
|
|
|
|
return
|
|
}
|
|
|
|
// XEPG Datenbank erstellen / aktualisieren
|
|
func createXEPGDatabase() (err error) {
|
|
|
|
var allChannelNumbers []float64
|
|
Data.Cache.Streams.Active = []string{}
|
|
|
|
Data.XEPG.Channels, err = loadJSONFileToMap(System.File.XEPG)
|
|
if err != nil {
|
|
ShowError(err, 1004)
|
|
return err
|
|
}
|
|
|
|
var createNewID = func() (xepg string) {
|
|
|
|
var firstID = 0 //len(Data.XEPG.Channels)
|
|
|
|
newXEPGID:
|
|
|
|
if _, ok := Data.XEPG.Channels["x-ID."+strconv.FormatInt(int64(firstID), 10)]; ok {
|
|
firstID++
|
|
goto newXEPGID
|
|
}
|
|
|
|
xepg = "x-ID." + strconv.FormatInt(int64(firstID), 10)
|
|
return
|
|
}
|
|
|
|
var getFreeChannelNumber = func() (xChannelID string) {
|
|
|
|
var firstFreeNumber float64 = Settings.MappingFirstChannel
|
|
|
|
newNumber:
|
|
|
|
if indexOfFloat64(firstFreeNumber, allChannelNumbers) == -1 {
|
|
xChannelID = fmt.Sprintf("%g", firstFreeNumber)
|
|
allChannelNumbers = append(allChannelNumbers, firstFreeNumber)
|
|
} else {
|
|
firstFreeNumber++
|
|
goto newNumber
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
showInfo("XEPG:" + "Update database")
|
|
|
|
// Kanal mit fehlenden Kanalnummern löschen
|
|
for id, dxc := range Data.XEPG.Channels {
|
|
|
|
var xepgChannel XEPGChannelStruct
|
|
err = json.Unmarshal([]byte(mapToJSON(dxc)), &xepgChannel)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
if len(xepgChannel.XChannelID) == 0 {
|
|
fmt.Println(mapToJSON(xepgChannel))
|
|
delete(Data.XEPG.Channels, id)
|
|
}
|
|
|
|
if xChannelID, err := strconv.ParseFloat(xepgChannel.XChannelID, 64); err == nil {
|
|
allChannelNumbers = append(allChannelNumbers, xChannelID)
|
|
}
|
|
|
|
}
|
|
|
|
for _, dsa := range Data.Streams.Active {
|
|
|
|
var channelExists = false // Entscheidet ob ein Kanal neu zu Datenbank hinzugefügt werden soll.
|
|
var channelHasUUID = false // Überprüft, ob der Kanal (Stream) eindeutige ID's besitzt
|
|
var currentXEPGID string // Aktuelle Datenbank ID (XEPG). Wird verwendet, um den Kanal in der Datenbank mit dem Stream der M3u zu aktualisieren
|
|
var m3uChannel M3UChannelStructXEPG
|
|
|
|
err = json.Unmarshal([]byte(mapToJSON(dsa)), &m3uChannel)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
Data.Cache.Streams.Active = append(Data.Cache.Streams.Active, m3uChannel.Name)
|
|
|
|
// XEPG Datenbank durchlaufen um nach dem Kanal zu suchen.
|
|
for xepg, dxc := range Data.XEPG.Channels {
|
|
|
|
var xepgChannel XEPGChannelStruct
|
|
err = json.Unmarshal([]byte(mapToJSON(dxc)), &xepgChannel)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
// Vergleichen des Streams anhand einer UUID in der M3U mit dem Kanal in der Databank
|
|
if len(xepgChannel.UUIDValue) > 0 && len(m3uChannel.UUIDValue) > 0 {
|
|
|
|
if xepgChannel.UUIDValue == m3uChannel.UUIDValue && xepgChannel.UUIDKey == m3uChannel.UUIDKey {
|
|
|
|
channelExists = true
|
|
channelHasUUID = true
|
|
currentXEPGID = xepg
|
|
break
|
|
|
|
}
|
|
|
|
} else {
|
|
// Vergleichen des Streams mit dem Kanal in der Databank anhand des Kanalnamens
|
|
//fmt.Println(xepgChannel.Name, xepgChannel.UUIDKey, xepgChannel.UUIDValue)
|
|
if xepgChannel.Name == m3uChannel.Name {
|
|
channelExists = true
|
|
currentXEPGID = xepg
|
|
break
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//os.Exit(0)
|
|
|
|
switch channelExists {
|
|
case true:
|
|
// Bereits vorhandener Kanal
|
|
var xepgChannel XEPGChannelStruct
|
|
err = json.Unmarshal([]byte(mapToJSON(Data.XEPG.Channels[currentXEPGID])), &xepgChannel)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
// Streaming URL aktualisieren
|
|
xepgChannel.URL = m3uChannel.URL
|
|
|
|
// Name aktualisieren, anhand des Names wird überprüft ob der Kanal noch in einer Playlist verhanden. Funktion: cleanupXEPG
|
|
xepgChannel.Name = m3uChannel.Name
|
|
|
|
// Kanalname aktualisieren, nur mit Kanal ID's möglich
|
|
if channelHasUUID == true {
|
|
if xepgChannel.XUpdateChannelName == true {
|
|
xepgChannel.XName = m3uChannel.Name
|
|
}
|
|
}
|
|
|
|
// Kanallogo aktualisieren. Wird bei vorhandenem Logo in der XMLTV Datei wieder überschrieben
|
|
if xepgChannel.XUpdateChannelIcon == true {
|
|
xepgChannel.TvgLogo = m3uChannel.TvgLogo
|
|
}
|
|
|
|
Data.XEPG.Channels[currentXEPGID] = xepgChannel
|
|
|
|
case false:
|
|
// Neuer Kanal
|
|
var xepg = createNewID()
|
|
var xChannelID = getFreeChannelNumber()
|
|
|
|
var newChannel XEPGChannelStruct
|
|
newChannel.FileM3UID = m3uChannel.FileM3UID
|
|
newChannel.FileM3UName = m3uChannel.FileM3UName
|
|
newChannel.FileM3UPath = m3uChannel.FileM3UPath
|
|
newChannel.Values = m3uChannel.Values
|
|
newChannel.GroupTitle = m3uChannel.GroupTitle
|
|
newChannel.Name = m3uChannel.Name
|
|
newChannel.TvgID = m3uChannel.TvgID
|
|
newChannel.TvgLogo = m3uChannel.TvgLogo
|
|
newChannel.TvgName = m3uChannel.TvgName
|
|
newChannel.URL = m3uChannel.URL
|
|
newChannel.XmltvFile = ""
|
|
newChannel.XMapping = ""
|
|
|
|
if len(m3uChannel.UUIDKey) > 0 {
|
|
newChannel.UUIDKey = m3uChannel.UUIDKey
|
|
newChannel.UUIDValue = m3uChannel.UUIDValue
|
|
}
|
|
|
|
newChannel.XName = m3uChannel.Name
|
|
newChannel.XGroupTitle = m3uChannel.GroupTitle
|
|
newChannel.XEPG = xepg
|
|
newChannel.XChannelID = xChannelID
|
|
|
|
Data.XEPG.Channels[xepg] = newChannel
|
|
|
|
}
|
|
|
|
}
|
|
|
|
err = saveMapToJSONFile(System.File.XEPG, Data.XEPG.Channels)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// Kanäle automatisch zuordnen und das Mapping überprüfen
|
|
func mapping() (err error) {
|
|
showInfo("XEPG:" + "Map channels")
|
|
|
|
for xepg, dxc := range Data.XEPG.Channels {
|
|
|
|
var xepgChannel XEPGChannelStruct
|
|
err = json.Unmarshal([]byte(mapToJSON(dxc)), &xepgChannel)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
// Automatische Mapping für neue Kanäle. Wird nur ausgeführt, wenn der Kanal deaktiviert ist und keine XMLTV Datei und kein XMLTV Kanal zugeordnet ist.
|
|
if xepgChannel.XActive == false {
|
|
|
|
// Werte kann "-" sein, deswegen len < 1
|
|
if len(xepgChannel.XmltvFile) < 1 && len(xepgChannel.XmltvFile) < 1 {
|
|
|
|
var tvgID = xepgChannel.TvgID
|
|
|
|
// Default für neuen Kanal setzen
|
|
xepgChannel.XmltvFile = "-"
|
|
xepgChannel.XMapping = "-"
|
|
|
|
Data.XEPG.Channels[xepg] = xepgChannel
|
|
|
|
for file, xmltvChannels := range Data.XMLTV.Mapping {
|
|
|
|
if channel, ok := xmltvChannels.(map[string]interface{})[tvgID]; ok {
|
|
|
|
if channelID, ok := channel.(map[string]interface{})["id"].(string); ok {
|
|
|
|
xepgChannel.XmltvFile = file
|
|
xepgChannel.XMapping = channelID
|
|
xepgChannel.XActive = true
|
|
|
|
// Falls in der XMLTV Datei ein Logo existiert, wird dieses verwendet. Falls nicht, dann das Logo aus der M3U Datei
|
|
if icon, ok := channel.(map[string]interface{})["icon"].(string); ok {
|
|
if len(icon) > 0 {
|
|
xepgChannel.TvgLogo = icon
|
|
}
|
|
}
|
|
|
|
Data.XEPG.Channels[xepg] = xepgChannel
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Überprüfen, ob die zugeordneten XMLTV Dateien und Kanäle noch existieren.
|
|
if xepgChannel.XActive == true {
|
|
|
|
var mapping = xepgChannel.XMapping
|
|
var file = xepgChannel.XmltvFile
|
|
|
|
if file != "xTeVe Dummy" {
|
|
|
|
if value, ok := Data.XMLTV.Mapping[file].(map[string]interface{}); ok {
|
|
|
|
if channel, ok := value[mapping].(map[string]interface{}); ok {
|
|
|
|
// Kanallogo aktualisieren
|
|
if logo, ok := channel["icon"].(string); ok {
|
|
|
|
if xepgChannel.XUpdateChannelIcon == true && len(logo) > 0 {
|
|
xepgChannel.TvgLogo = logo
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ShowError(fmt.Errorf(fmt.Sprintf("Missing EPG data: %s", xepgChannel.Name)), 0)
|
|
showWarning(2302)
|
|
xepgChannel.XActive = false
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
var fileID = strings.TrimSuffix(getFilenameFromPath(file), path.Ext(getFilenameFromPath(file)))
|
|
|
|
ShowError(fmt.Errorf("Missing XMLTV file: %s", getProviderParameter(fileID, "xmltv", "name")), 0)
|
|
showWarning(2301)
|
|
xepgChannel.XActive = false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(xepgChannel.XmltvFile) == 0 {
|
|
xepgChannel.XmltvFile = "-"
|
|
xepgChannel.XActive = false
|
|
}
|
|
|
|
if len(xepgChannel.XMapping) == 0 {
|
|
xepgChannel.XMapping = "-"
|
|
xepgChannel.XActive = false
|
|
}
|
|
|
|
Data.XEPG.Channels[xepg] = xepgChannel
|
|
|
|
}
|
|
|
|
}
|
|
|
|
err = saveMapToJSONFile(System.File.XEPG, Data.XEPG.Channels)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// XMLTV Datei erstellen
|
|
func createXMLTVFile() (err error) {
|
|
|
|
Data.Cache.ImagesFiles = []string{}
|
|
Data.Cache.ImagesURLS = []string{}
|
|
Data.Cache.ImagesCache = []string{}
|
|
|
|
files, err := ioutil.ReadDir(System.Folder.ImagesCache)
|
|
if err == nil {
|
|
|
|
for _, file := range files {
|
|
|
|
if indexOfString(file.Name(), Data.Cache.ImagesCache) == -1 {
|
|
Data.Cache.ImagesCache = append(Data.Cache.ImagesCache, file.Name())
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(Data.XMLTV.Files) == 0 && len(Data.Streams.Active) == 0 {
|
|
Data.XEPG.Channels = make(map[string]interface{})
|
|
return
|
|
}
|
|
|
|
showInfo("XEPG:" + fmt.Sprintf("Create XMLTV file (%s)", System.File.XML))
|
|
|
|
var xepgXML XMLTV
|
|
|
|
xepgXML.Generator = System.Name
|
|
|
|
if System.Branch == "master" {
|
|
xepgXML.Source = fmt.Sprintf("%s - %s", System.Name, System.Version)
|
|
} else {
|
|
xepgXML.Source = fmt.Sprintf("%s - %s.%s", System.Name, System.Version, System.Build)
|
|
}
|
|
|
|
var tmpProgram = &XMLTV{}
|
|
|
|
for _, dxc := range Data.XEPG.Channels {
|
|
|
|
var xepgChannel XEPGChannelStruct
|
|
err := json.Unmarshal([]byte(mapToJSON(dxc)), &xepgChannel)
|
|
if err == nil {
|
|
|
|
if xepgChannel.XActive == true {
|
|
|
|
// Kanäle
|
|
var channel Channel
|
|
channel.ID = xepgChannel.XChannelID
|
|
channel.Icon = Icon{Src: getCacheImageURL(xepgChannel.TvgLogo)}
|
|
channel.DisplayName = append(channel.DisplayName, DisplayName{Value: xepgChannel.XName})
|
|
|
|
xepgXML.Channel = append(xepgXML.Channel, &channel)
|
|
|
|
// Programme
|
|
|
|
*tmpProgram, err = getProgramData(xepgChannel)
|
|
if err == nil {
|
|
|
|
for _, program := range tmpProgram.Program {
|
|
xepgXML.Program = append(xepgXML.Program, program)
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
var content, _ = xml.MarshalIndent(xepgXML, " ", " ")
|
|
var xmlOutput = []byte(xml.Header + string(content))
|
|
writeByteToFile(System.File.XML, xmlOutput)
|
|
|
|
xepgXML = XMLTV{}
|
|
|
|
//saveMapToJSONFile(System.File.Images, Data.Cache.ImageCache)
|
|
|
|
return
|
|
}
|
|
|
|
// Programmdaten erstellen (createXMLTVFile)
|
|
func getProgramData(xepgChannel XEPGChannelStruct) (xepgXML XMLTV, err error) {
|
|
|
|
var xmltvFile = System.Folder.Data + xepgChannel.XmltvFile
|
|
var channelID = xepgChannel.XMapping
|
|
|
|
var xmltv XMLTV
|
|
|
|
if xmltvFile == System.Folder.Data+"xTeVe Dummy" {
|
|
xmltv = createDummyProgram(xepgChannel)
|
|
} else {
|
|
|
|
err = getLocalXMLTV(xmltvFile, &xmltv)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
}
|
|
|
|
for _, xmltvProgram := range xmltv.Program {
|
|
|
|
if xmltvProgram.Channel == channelID {
|
|
//fmt.Println(&channelID)
|
|
var program = &Program{}
|
|
|
|
// Channel ID
|
|
program.Channel = xepgChannel.XChannelID
|
|
program.Start = xmltvProgram.Start
|
|
program.Stop = xmltvProgram.Stop
|
|
|
|
// Title
|
|
program.Title = xmltvProgram.Title
|
|
|
|
// Sub title (Untertitel)
|
|
program.SubTitle = xmltvProgram.SubTitle
|
|
|
|
// Description (Beschreibung)
|
|
program.Desc = xmltvProgram.Desc
|
|
|
|
// Category (Kategorie)
|
|
getCategory(program, xmltvProgram, xepgChannel)
|
|
|
|
// Country (Länder)
|
|
program.Country = xmltvProgram.Country
|
|
|
|
// Program icon (Poster / Cover)
|
|
getPoster(program, xmltvProgram, xepgChannel)
|
|
|
|
// Language (Sprache)
|
|
program.Language = xmltvProgram.Language
|
|
|
|
// Episodes numbers (Episodennummern)
|
|
getEpisodeNum(program, xmltvProgram, xepgChannel)
|
|
|
|
// Video (Videoparameter)
|
|
getVideo(program, xmltvProgram, xepgChannel)
|
|
|
|
// Date (Datum)
|
|
program.Date = xmltvProgram.Date
|
|
|
|
// Previously shown (Wiederholung)
|
|
program.PreviouslyShown = xmltvProgram.PreviouslyShown
|
|
|
|
// New (Neu)
|
|
program.New = xmltvProgram.New
|
|
|
|
// Live
|
|
program.Live = xmltvProgram.Live
|
|
|
|
xepgXML.Program = append(xepgXML.Program, program)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// Dummy Daten erstellen (createXMLTVFile)
|
|
func createDummyProgram(xepgChannel XEPGChannelStruct) (dummyXMLTV XMLTV) {
|
|
|
|
var currentTime = time.Now()
|
|
var dateArray = strings.Fields(currentTime.String())
|
|
var offset = " " + dateArray[2]
|
|
var currentDay = currentTime.Format("20060102")
|
|
var startTime, _ = time.Parse("20060102150405", currentDay+"000000")
|
|
|
|
showInfo("Create Dummy Guide:" + "Time offset" + offset + " - " + xepgChannel.XName)
|
|
|
|
var dl = strings.Split(xepgChannel.XMapping, "_")
|
|
dummyLength, err := strconv.Atoi(dl[0])
|
|
if err != nil {
|
|
ShowError(err, 000)
|
|
return
|
|
}
|
|
|
|
for d := 0; d < 4; d++ {
|
|
|
|
var epgStartTime = startTime.Add(time.Hour * time.Duration(d*24))
|
|
|
|
for t := dummyLength; t <= 1440; t = t + dummyLength {
|
|
|
|
var epgStopTime = epgStartTime.Add(time.Minute * time.Duration(dummyLength))
|
|
|
|
var epg Program
|
|
poster := Poster{}
|
|
|
|
epg.Channel = xepgChannel.XMapping
|
|
epg.Start = epgStartTime.Format("20060102150405") + offset
|
|
epg.Stop = epgStopTime.Format("20060102150405") + offset
|
|
epg.Title = append(epg.Title, &Title{Value: xepgChannel.XName + " (" + epgStartTime.Weekday().String()[0:2] + ". " + epgStartTime.Format("15:04") + " - " + epgStopTime.Format("15:04") + ")", Lang: "en"})
|
|
epg.Desc = append(epg.Desc, &Desc{Value: "xTeVe: (" + strconv.Itoa(dummyLength) + " Minutes) " + epgStartTime.Weekday().String() + " " + epgStartTime.Format("15:04") + " - " + epgStopTime.Format("15:04"), Lang: "en"})
|
|
|
|
if Settings.XepgReplaceMissingImages == true {
|
|
poster.Src = getCacheImageURL(xepgChannel.TvgLogo)
|
|
epg.Poster = append(epg.Poster, poster)
|
|
}
|
|
|
|
if xepgChannel.XCategory != "Movie" {
|
|
epg.EpisodeNum = append(epg.EpisodeNum, &EpisodeNum{Value: epgStartTime.Format("2006-01-02 15:04:05"), System: "original-air-date"})
|
|
}
|
|
|
|
epg.New = &New{Value: ""}
|
|
|
|
dummyXMLTV.Program = append(dummyXMLTV.Program, &epg)
|
|
epgStartTime = epgStopTime
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// Kategorien erweitern (createXMLTVFile)
|
|
func getCategory(program *Program, xmltvProgram *Program, xepgChannel XEPGChannelStruct) {
|
|
|
|
for _, i := range xmltvProgram.Category {
|
|
|
|
category := &Category{}
|
|
category.Value = i.Value
|
|
category.Lang = i.Lang
|
|
program.Category = append(program.Category, category)
|
|
|
|
}
|
|
|
|
if len(xepgChannel.XCategory) > 0 {
|
|
|
|
category := &Category{}
|
|
category.Value = xepgChannel.XCategory
|
|
category.Lang = "en"
|
|
program.Category = append(program.Category, category)
|
|
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// Programm Poster Cover aus der XMLTV Datei laden
|
|
func getPoster(program *Program, xmltvProgram *Program, xepgChannel XEPGChannelStruct) {
|
|
|
|
for _, poster := range xmltvProgram.Poster {
|
|
poster.Src = getCacheImageURL(poster.Src)
|
|
program.Poster = append(program.Poster, poster)
|
|
}
|
|
|
|
if Settings.XepgReplaceMissingImages == true {
|
|
|
|
if len(xmltvProgram.Poster) == 0 {
|
|
var poster Poster
|
|
poster.Src = getCacheImageURL(xepgChannel.TvgLogo)
|
|
program.Poster = append(program.Poster, poster)
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Episodensystem übernehmen, falls keins vorhanden ist und eine Kategorie im Mapping eingestellt wurden, wird eine Episode erstellt
|
|
func getEpisodeNum(program *Program, xmltvProgram *Program, xepgChannel XEPGChannelStruct) {
|
|
|
|
program.EpisodeNum = xmltvProgram.EpisodeNum
|
|
|
|
if len(xepgChannel.XCategory) > 0 && xepgChannel.XCategory != "Movie" {
|
|
|
|
if len(xmltvProgram.EpisodeNum) == 0 {
|
|
|
|
var timeLayout = "20060102150405"
|
|
|
|
t, err := time.Parse(timeLayout, strings.Split(xmltvProgram.Start, " ")[0])
|
|
if err == nil {
|
|
program.EpisodeNum = append(program.EpisodeNum, &EpisodeNum{Value: t.Format("2006-01-02 15:04:05"), System: "original-air-date"})
|
|
} else {
|
|
ShowError(err, 0)
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// Videoparameter erstellen (createXMLTVFile)
|
|
func getVideo(program *Program, xmltvProgram *Program, xepgChannel XEPGChannelStruct) {
|
|
|
|
var video Video
|
|
video.Present = xmltvProgram.Video.Present
|
|
video.Colour = xmltvProgram.Video.Colour
|
|
video.Aspect = xmltvProgram.Video.Aspect
|
|
video.Quality = xmltvProgram.Video.Quality
|
|
|
|
if len(xmltvProgram.Video.Quality) == 0 {
|
|
|
|
if strings.Contains(strings.ToUpper(xepgChannel.XName), " HD") || strings.Contains(strings.ToUpper(xepgChannel.XName), " FHD") {
|
|
video.Quality = "HDTV"
|
|
}
|
|
|
|
if strings.Contains(strings.ToUpper(xepgChannel.XName), " UHD") || strings.Contains(strings.ToUpper(xepgChannel.XName), " 4K") {
|
|
video.Quality = "UHDTV"
|
|
}
|
|
|
|
}
|
|
|
|
program.Video = video
|
|
|
|
return
|
|
}
|
|
|
|
// Lokale Provider XMLTV Datei laden
|
|
func getLocalXMLTV(file string, xmltv *XMLTV) (err error) {
|
|
|
|
if _, ok := Data.Cache.XMLTV[file]; !ok {
|
|
|
|
// Cache initialisieren
|
|
if len(Data.Cache.XMLTV) == 0 {
|
|
Data.Cache.XMLTV = make(map[string]XMLTV)
|
|
}
|
|
|
|
// XML Daten lesen
|
|
content, err := readByteFromFile(file)
|
|
|
|
// Lokale XML Datei existiert nicht im Ordner: data
|
|
if err != nil {
|
|
ShowError(err, 1004)
|
|
err = errors.New("Local copy of the file no longer exists")
|
|
return err
|
|
}
|
|
|
|
// XML Datei parsen
|
|
err = xml.Unmarshal(content, &xmltv)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
Data.Cache.XMLTV[file] = *xmltv
|
|
|
|
} else {
|
|
*xmltv = Data.Cache.XMLTV[file]
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// M3U Datei erstellen
|
|
func createM3UFile() {
|
|
|
|
showInfo("XEPG:" + fmt.Sprintf("Create M3U file (%s)", System.File.M3U))
|
|
_, err := buildM3U([]string{})
|
|
if err != nil {
|
|
ShowError(err, 000)
|
|
}
|
|
|
|
saveMapToJSONFile(System.File.URLS, Data.Cache.StreamingURLS)
|
|
|
|
return
|
|
}
|
|
|
|
// XEPG Datenbank bereinigen
|
|
func cleanupXEPG() {
|
|
|
|
showInfo("XEPG:" + fmt.Sprintf("Cleanup database"))
|
|
Data.XEPG.XEPGCount = 0
|
|
|
|
for id, dxc := range Data.XEPG.Channels {
|
|
|
|
var xepgChannel XEPGChannelStruct
|
|
err := json.Unmarshal([]byte(mapToJSON(dxc)), &xepgChannel)
|
|
if err == nil {
|
|
|
|
if indexOfString(xepgChannel.Name, Data.Cache.Streams.Active) == -1 {
|
|
delete(Data.XEPG.Channels, id)
|
|
} else {
|
|
if xepgChannel.XActive == true {
|
|
Data.XEPG.XEPGCount++
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
err := saveMapToJSONFile(System.File.XEPG, Data.XEPG.Channels)
|
|
if err != nil {
|
|
ShowError(err, 000)
|
|
return
|
|
}
|
|
|
|
showInfo("XEPG Channels:" + fmt.Sprintf("%d", Data.XEPG.XEPGCount))
|
|
|
|
if len(Data.Streams.Active) > 0 && Data.XEPG.XEPGCount == 0 {
|
|
showWarning(2005)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// Streaming URL für die Channels App generieren
|
|
func getStreamByChannelID(channelID string) (playlistID, streamURL string, err error) {
|
|
|
|
err = errors.New("Channel not found")
|
|
|
|
for _, dxc := range Data.XEPG.Channels {
|
|
|
|
var xepgChannel XEPGChannelStruct
|
|
err := json.Unmarshal([]byte(mapToJSON(dxc)), &xepgChannel)
|
|
|
|
fmt.Println(xepgChannel.XChannelID)
|
|
|
|
if err == nil {
|
|
|
|
if channelID == xepgChannel.XChannelID {
|
|
|
|
playlistID = xepgChannel.FileM3UID
|
|
streamURL = xepgChannel.URL
|
|
|
|
return playlistID, streamURL, nil
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
}
|