Files
xTeVe/src/data.go
marmei 50e6ed274e Update schedule:
Removes all spaces from the values [ 1000] -> [1000]
2019-08-04 09:49:51 +02:00

962 lines
18 KiB
Go

package src
import (
"encoding/json"
"errors"
"fmt"
"os"
"path"
"sort"
"strconv"
"strings"
"time"
"../src/internal/authentication"
)
// Einstellungen ändern (WebUI)
func updateServerSettings(request RequestStruct) (settings SettingsStrcut, err error) {
var oldSettings = jsonToMap(mapToJSON(Settings))
var newSettings = jsonToMap(mapToJSON(request.Settings))
var reloadData = false
var cacheImages = false
var createXEPGFiles = false
var debug string
for key, value := range newSettings {
if _, ok := oldSettings[key]; ok {
switch key {
case "tuner":
showWarning(2105)
case "epgSource":
reloadData = true
case "update":
// Leerzeichen aus den Werten entfernen und Formatierung der Uhrzeit überprüfen (0000 - 2359)
var newUpdateTimes []string
for _, v := range value.([]interface{}) {
v = strings.Replace(v.(string), " ", "", -1)
_, err := time.Parse("1504", v.(string))
if err != nil {
ShowError(err, 1012)
return Settings, err
}
newUpdateTimes = append(newUpdateTimes, v.(string))
}
value = newUpdateTimes
case "cache.images":
cacheImages = true
case "xepg.replace.missing.images":
createXEPGFiles = true
case "backup.path":
value = strings.TrimRight(value.(string), string(os.PathSeparator)) + string(os.PathSeparator)
err = checkFolder(value.(string))
if err == nil {
err = checkFilePermission(value.(string))
if err != nil {
return
}
}
if err != nil {
return
}
case "temp.path":
value = strings.TrimRight(value.(string), string(os.PathSeparator)) + string(os.PathSeparator)
err = checkFolder(value.(string))
if err == nil {
err = checkFilePermission(value.(string))
if err != nil {
return
}
}
if err != nil {
return
}
}
oldSettings[key] = value
switch fmt.Sprintf("%T", value) {
case "bool":
debug = fmt.Sprintf("Save Setting:Key: %s | Value: %t (%T)", key, value, value)
case "string":
debug = fmt.Sprintf("Save Setting:Key: %s | Value: %s (%T)", key, value, value)
case "[]interface {}":
debug = fmt.Sprintf("Save Setting:Key: %s | Value: %v (%T)", key, value, value)
case "float64":
debug = fmt.Sprintf("Save Setting:Key: %s | Value: %d (%T)", key, int(value.(float64)), value)
default:
debug = fmt.Sprintf("%T", value)
}
showDebug(debug, 1)
}
}
// Einstellungen aktualisieren
err = json.Unmarshal([]byte(mapToJSON(oldSettings)), &Settings)
if err != nil {
return
}
if Settings.AuthenticationWEB == false {
Settings.AuthenticationAPI = false
Settings.AuthenticationM3U = false
Settings.AuthenticationPMS = false
Settings.AuthenticationWEB = false
Settings.AuthenticationXML = false
}
err = saveSettings(Settings)
if err == nil {
settings = Settings
if reloadData == true {
err = buildDatabaseDVR()
if err != nil {
return
}
buildXEPG(false)
}
if cacheImages == true {
if Settings.EpgSource == "XEPG" {
go func() {
if Settings.CacheImages == true {
createXMLTVFile()
cachingImages()
createXMLTVFile()
createM3UFile()
} else {
createXMLTVFile()
createM3UFile()
}
}()
}
}
if createXEPGFiles == true {
go func() {
createXMLTVFile()
createM3UFile()
}()
}
}
return
}
// Providerdaten speichern (WebUI)
func saveFiles(request RequestStruct, fileType string) (err error) {
var filesMap = make(map[string]interface{})
var newData = make(map[string]interface{})
var indicator string
var reloadData = false
switch fileType {
case "m3u":
filesMap = Settings.Files.M3U
newData = request.Files.M3U
indicator = "M"
case "hdhr":
filesMap = Settings.Files.HDHR
newData = request.Files.HDHR
indicator = "H"
case "xmltv":
filesMap = Settings.Files.XMLTV
newData = request.Files.XMLTV
indicator = "X"
}
if len(filesMap) == 0 {
filesMap = make(map[string]interface{})
}
for dataID, data := range newData {
if dataID == "-" {
// Neue Providerdatei
dataID = indicator + randomString(19)
data.(map[string]interface{})["new"] = true
filesMap[dataID] = data
} else {
// Bereits vorhandene Providerdatei
for key, value := range data.(map[string]interface{}) {
var oldData = filesMap[dataID].(map[string]interface{})
oldData[key] = value
}
}
switch fileType {
case "m3u":
Settings.Files.M3U = filesMap
case "hdhr":
Settings.Files.HDHR = filesMap
case "xmltv":
Settings.Files.XMLTV = filesMap
}
// Neue Providerdatei
if _, ok := data.(map[string]interface{})["new"]; ok {
reloadData = true
err = getProviderData(fileType, dataID)
delete(data.(map[string]interface{}), "new")
if err != nil {
delete(filesMap, dataID)
return
}
}
if _, ok := data.(map[string]interface{})["delete"]; ok {
deleteLocalProviderFiles(dataID, fileType)
reloadData = true
}
err = saveSettings(Settings)
if err != nil {
return
}
if reloadData == true {
err = buildDatabaseDVR()
if err != nil {
return err
}
buildXEPG(false)
}
Settings, _ = loadSettings()
}
return
}
// Providerdaten manuell aktualisieren (WebUI)
func updateFile(request RequestStruct, fileType string) (err error) {
var updateData = make(map[string]interface{})
switch fileType {
case "m3u":
updateData = request.Files.M3U
case "hdhr":
updateData = request.Files.HDHR
case "xmltv":
updateData = request.Files.XMLTV
}
for dataID := range updateData {
err = getProviderData(fileType, dataID)
if err == nil {
err = buildDatabaseDVR()
buildXEPG(false)
}
}
return
}
// Providerdaten löschen (WebUI)
func deleteLocalProviderFiles(dataID, fileType string) {
var removeData = make(map[string]interface{})
var fileExtension string
switch fileType {
case "m3u":
removeData = Settings.Files.M3U
fileExtension = ".m3u"
case "hdhr":
removeData = Settings.Files.HDHR
fileExtension = ".json"
case "xmltv":
removeData = Settings.Files.XMLTV
fileExtension = ".xml"
}
if _, ok := removeData[dataID]; ok {
delete(removeData, dataID)
os.RemoveAll(System.Folder.Data + dataID + fileExtension)
}
return
}
// Filtereinstellungen speichern (WebUI)
func saveFilter(request RequestStruct) (settings SettingsStrcut, err error) {
var filterMap = make(map[int64]interface{})
var newData = make(map[int64]interface{})
var defaultFilter FilterStruct
defaultFilter.Active = true
defaultFilter.CaseSensitive = false
filterMap = Settings.Filter
newData = request.Filter
var createNewID = func() (id int64) {
newID:
if _, ok := filterMap[id]; ok {
id++
goto newID
}
return id
}
for dataID, data := range newData {
if dataID == -1 {
// Neuer Filter
dataID = createNewID()
filterMap[dataID] = jsonToMap(mapToJSON(defaultFilter))
}
// Filter aktualisieren / löschen
for key, value := range data.(map[string]interface{}) {
var oldData = filterMap[dataID].(map[string]interface{})
oldData[key] = value
// Filter löschen
if _, ok := data.(map[string]interface{})["delete"]; ok {
delete(filterMap, dataID)
break
}
}
}
err = saveSettings(Settings)
if err != nil {
return
}
settings = Settings
err = buildDatabaseDVR()
if err != nil {
return
}
buildXEPG(false)
return
}
// XEPG Mapping speichern
func saveXEpgMapping(request RequestStruct) (err error) {
var tmp = Data.XEPG
err = json.Unmarshal([]byte(mapToJSON(request.EpgMapping)), &tmp)
if err != nil {
return
}
err = saveMapToJSONFile(System.File.XEPG, request.EpgMapping)
if err != nil {
return err
}
Data.XEPG.Channels = request.EpgMapping
cleanupXEPG()
buildXEPG(true)
return
}
// Benutzerdaten speichern (WebUI)
func saveUserData(request RequestStruct) (err error) {
var userData = request.UserData
var newCredentials = func(userID string, newUserData map[string]interface{}) (err error) {
var newUsername, newPassword string
if username, ok := newUserData["username"].(string); ok {
newUsername = username
}
if password, ok := newUserData["password"].(string); ok {
newPassword = password
}
if len(newUsername) > 0 {
err = authentication.ChangeCredentials(userID, newUsername, newPassword)
}
return
}
for userID, newUserData := range userData {
err = newCredentials(userID, newUserData.(map[string]interface{}))
if err != nil {
return
}
if request.DeleteUser == true {
err = authentication.RemoveUser(userID)
return
}
delete(newUserData.(map[string]interface{}), "password")
delete(newUserData.(map[string]interface{}), "confirm")
if _, ok := newUserData.(map[string]interface{})["delete"]; ok {
authentication.RemoveUser(userID)
} else {
err = authentication.WriteUserData(userID, newUserData.(map[string]interface{}))
if err != nil {
return
}
}
}
return
}
// Neuen Benutzer anlegen (WebUI)
func saveNewUser(request RequestStruct) (err error) {
var data = request.UserData
var username = data["username"].(string)
var password = data["password"].(string)
delete(data, "password")
delete(data, "confirm")
userID, err := authentication.CreateNewUser(username, password)
if err != nil {
return
}
err = authentication.WriteUserData(userID, data)
return
}
// Wizard (WebUI)
func saveWizard(request RequestStruct) (nextStep int, err error) {
var wizard = jsonToMap(mapToJSON(request.Wizard))
for key, value := range wizard {
switch key {
case "tuner":
Settings.Tuner = int(value.(float64))
nextStep = 1
case "epgSource":
Settings.EpgSource = value.(string)
nextStep = 2
case "m3u", "xmltv":
var filesMap = make(map[string]interface{})
var data = make(map[string]interface{})
var indicator, dataID string
filesMap = make(map[string]interface{})
data["type"] = key
data["new"] = true
switch key {
case "m3u":
filesMap = Settings.Files.M3U
data["name"] = "M3U"
indicator = "M"
case "xmltv":
filesMap = Settings.Files.XMLTV
data["name"] = "XMLTV"
indicator = "X"
}
dataID = indicator + randomString(19)
data["file.source"] = value.(string)
filesMap[dataID] = data
switch key {
case "m3u":
Settings.Files.M3U = filesMap
nextStep = 3
err = getProviderData(key, dataID)
if err != nil {
ShowError(err, 000)
delete(filesMap, dataID)
return
}
err = buildDatabaseDVR()
if err != nil {
ShowError(err, 000)
delete(filesMap, dataID)
return
}
if Settings.EpgSource == "PMS" {
nextStep = 10
}
case "xmltv":
Settings.Files.XMLTV = filesMap
nextStep = 10
err = getProviderData(key, dataID)
if err != nil {
ShowError(err, 000)
delete(filesMap, dataID)
return
}
buildXEPG(false)
}
}
}
err = saveSettings(Settings)
if err != nil {
return
}
return
}
// Filterregeln erstellen
func createFilterRules() (err error) {
Data.Filter = nil
var dataFilter Filter
for _, f := range Settings.Filter {
var filter FilterStruct
var exclude, include string
err = json.Unmarshal([]byte(mapToJSON(f)), &filter)
if err != nil {
return
}
switch filter.Type {
case "custom-filter":
dataFilter.CaseSensitive = filter.CaseSensitive
dataFilter.Rule = filter.Filter
dataFilter.Type = filter.Type
Data.Filter = append(Data.Filter, dataFilter)
case "group-title":
if len(filter.Include) > 0 {
include = fmt.Sprintf(" {%s}", filter.Include)
}
if len(filter.Exclude) > 0 {
exclude = fmt.Sprintf(" !{%s}", filter.Exclude)
}
dataFilter.CaseSensitive = filter.CaseSensitive
dataFilter.Rule = fmt.Sprintf("%s%s%s", filter.Filter, include, exclude)
dataFilter.Type = filter.Type
Data.Filter = append(Data.Filter, dataFilter)
}
}
return
}
// Datenbank für das DVR System erstellen
func buildDatabaseDVR() (err error) {
System.ScanInProgress = 1
Data.Streams.All = make([]interface{}, 0)
Data.Streams.Active = make([]interface{}, 0)
Data.Streams.Inactive = make([]interface{}, 0)
Data.Playlist.M3U.Groups.Text = []string{}
Data.Playlist.M3U.Groups.Value = []string{}
Data.StreamPreviewUI.Active = []string{}
Data.StreamPreviewUI.Inactive = []string{}
var availableFileTypes = []string{"m3u", "hdhr"}
var tmpGroupsM3U = make(map[string]int64)
err = createFilterRules()
if err != nil {
return
}
for _, fileType := range availableFileTypes {
var playlistFile = getLocalProviderFiles(fileType)
for n, i := range playlistFile {
var channels []interface{}
var groupTitle, tvgID, uuid int = 0, 0, 0
var keys = []string{"group-title", "tvg-id", "uuid"}
var compatibility = make(map[string]int)
var id = strings.TrimSuffix(getFilenameFromPath(i), path.Ext(getFilenameFromPath(i)))
var playlistName = getProviderParameter(id, fileType, "name")
switch fileType {
case "m3u":
channels, err = parsePlaylist(i, fileType)
case "hdhr":
channels, err = parsePlaylist(i, fileType)
}
if err != nil {
ShowError(err, 1005)
err = errors.New(playlistName + ": Local copy of the file no longer exists")
ShowError(err, 0)
playlistFile = append(playlistFile[:n], playlistFile[n+1:]...)
}
// Streams analysieren
for _, stream := range channels {
var s = stream.(map[string]string)
s["_file.m3u.path"] = i
s["_file.m3u.name"] = playlistName
s["_file.m3u.id"] = id
// Kompatibilität berechnen
for _, key := range keys {
switch key {
case "uuid":
if value, ok := s["_uuid.key"]; ok {
if len(value) > 0 {
uuid++
}
}
case "group-title":
if value, ok := s[key]; ok {
if len(value) > 0 {
if _, ok := tmpGroupsM3U[value]; ok {
tmpGroupsM3U[value]++
} else {
tmpGroupsM3U[value] = 1
}
groupTitle++
}
}
case "tvg-id":
if value, ok := s[key]; ok {
if len(value) > 0 {
tvgID++
}
}
}
}
Data.Streams.All = append(Data.Streams.All, stream)
// Neuer Filter ab Version 1.3.0
var preview string
var status = filterThisStream(stream)
if name, ok := s["name"]; ok {
var group string
if v, ok := s["group-title"]; ok {
group = v
}
preview = fmt.Sprintf("%s [%s]", name, group)
}
switch status {
case true:
Data.StreamPreviewUI.Active = append(Data.StreamPreviewUI.Active, preview)
Data.Streams.Active = append(Data.Streams.Active, stream)
case false:
Data.StreamPreviewUI.Inactive = append(Data.StreamPreviewUI.Inactive, preview)
Data.Streams.Inactive = append(Data.Streams.Inactive, stream)
}
}
if tvgID == 0 {
compatibility["tvg.id"] = 0
} else {
compatibility["tvg.id"] = int(tvgID * 100 / len(channels))
}
if groupTitle == 0 {
compatibility["group.title"] = 0
} else {
compatibility["group.title"] = int(groupTitle * 100 / len(channels))
}
if uuid == 0 {
compatibility["stream.id"] = 0
} else {
compatibility["stream.id"] = int(uuid * 100 / len(channels))
}
compatibility["streams"] = len(channels)
setProviderCompatibility(id, fileType, compatibility)
}
}
for group, count := range tmpGroupsM3U {
var text = fmt.Sprintf("%s (%d)", group, count)
var value = fmt.Sprintf("%s", group)
Data.Playlist.M3U.Groups.Text = append(Data.Playlist.M3U.Groups.Text, text)
Data.Playlist.M3U.Groups.Value = append(Data.Playlist.M3U.Groups.Value, value)
}
sort.Strings(Data.Playlist.M3U.Groups.Text)
sort.Strings(Data.Playlist.M3U.Groups.Value)
if len(Data.Streams.Active) == 0 && len(Data.Streams.All) <= System.DVRLimit && len(Settings.Filter) == 0 {
Data.Streams.Active = Data.Streams.All
Data.Streams.Inactive = make([]interface{}, 0)
Data.StreamPreviewUI.Active = Data.StreamPreviewUI.Inactive
Data.StreamPreviewUI.Inactive = []string{}
}
if len(Data.Streams.Active) > System.DVRLimit {
showWarning(2000)
}
if len(Settings.Filter) == 0 && len(Data.Streams.All) > System.DVRLimit {
showWarning(2001)
}
System.ScanInProgress = 0
showInfo(fmt.Sprintf("All streams:%d", len(Data.Streams.All)))
showInfo(fmt.Sprintf("Active streams:%d", len(Data.Streams.Active)))
showInfo(fmt.Sprintf("Filter:%d", len(Data.Filter)))
sort.Strings(Data.StreamPreviewUI.Active)
sort.Strings(Data.StreamPreviewUI.Inactive)
return
}
// Speicherort aller lokalen Providerdateien laden, immer für eine Dateityp (M3U, XMLTV usw.)
func getLocalProviderFiles(fileType string) (localFiles []string) {
var fileExtension string
var dataMap = make(map[string]interface{})
switch fileType {
case "m3u":
fileExtension = ".m3u"
dataMap = Settings.Files.M3U
case "hdhr":
fileExtension = ".json"
dataMap = Settings.Files.HDHR
case "xmltv":
fileExtension = ".xml"
dataMap = Settings.Files.XMLTV
}
for dataID := range dataMap {
localFiles = append(localFiles, System.Folder.Data+dataID+fileExtension)
}
return
}
// Providerparameter anhand von dem Key ausgeben
func getProviderParameter(id, fileType, key string) (s string) {
var dataMap = make(map[string]interface{})
switch fileType {
case "m3u":
dataMap = Settings.Files.M3U
case "hdhr":
dataMap = Settings.Files.HDHR
case "xmltv":
dataMap = Settings.Files.XMLTV
}
if data, ok := dataMap[id].(map[string]interface{}); ok {
if v, ok := data[key].(string); ok {
s = v
}
if v, ok := data[key].(float64); ok {
s = strconv.Itoa(int(v))
}
}
return
}
// Provider Statistiken Kompatibilität aktualisieren
func setProviderCompatibility(id, fileType string, compatibility map[string]int) {
var dataMap = make(map[string]interface{})
switch fileType {
case "m3u":
dataMap = Settings.Files.M3U
case "hdhr":
dataMap = Settings.Files.HDHR
case "xmltv":
dataMap = Settings.Files.XMLTV
}
if data, ok := dataMap[id].(map[string]interface{}); ok {
data["compatibility"] = compatibility
switch fileType {
case "m3u":
Settings.Files.M3U = dataMap
case "hdhr":
Settings.Files.HDHR = dataMap
case "xmltv":
Settings.Files.XMLTV = dataMap
}
saveSettings(Settings)
}
}