v2.0.0.0007-beta

Add CLI: info
Add CLI: restore
Improves UPnP support for Plex
CSS: Add min-width for Ch.No.
Fixed missing images on cache for localhost
This commit is contained in:
marmei
2019-08-07 12:35:56 +02:00
parent 2a06bf6b01
commit 3730d1187d
16 changed files with 406 additions and 140 deletions

View File

@@ -81,6 +81,11 @@ Including:
- Crond: Daemon to execute scheduled commands
- Perl: Programming language
## Beta version
New features are first available in the beta version and will be added to the master branch after successful testing
If you prefer to use the beta version, you can always switch between master and beta branch.
---
## Build from source code [Go / Golang]

View File

@@ -299,6 +299,8 @@ tbody {
#content_table input[type=text]{
width: 80%;
min-width: 35px;
max-width: 60px;
border: 0px;
background-color: #333;
margin-left: 5px;

View File

@@ -1,3 +0,0 @@
h1 {
color: green;
}

View File

@@ -23,7 +23,7 @@
}
},
"confirm":{
"restore": "All data will be replaced with those from the backup.Should the files be restored?"
"restore": "All data will be replaced with those from the backup. Should the files be restored?"
},
"alert": {
"fileLoadingError": "File couldn't be loaded",
@@ -320,7 +320,7 @@
},
"streamBuffering": {
"title": "Stream Buffer",
"description": "- The stream is passed from xTeVe to Plex / Emby / M3U Player<br>- Small jerking of the streams can be compensated<br>- HLS / M3U8 support"
"description": "- The stream is passed from xTeVe to Plex / Emby / M3U Player<br>- Small jerking of the streams can be compensated<br>- HLS / M3U8 support<br>- Re-streaming<br>- Separate tuner limit for each playlist"
},
"bufferSize": {
"title": "Buffer Size",

View File

@@ -2,6 +2,7 @@ package src
import (
b64 "encoding/base64"
"errors"
"fmt"
"io/ioutil"
"os"
@@ -124,35 +125,44 @@ func xteveBackup() (archiv string, err error) {
return
}
func xteveRestore(input string) (newWebURL string, err error) {
func xteveRestore(archive string) (newWebURL string, err error) {
var newPort, oldPort string
var newPort, oldPort, backupVersion, tmpRestore string
// Base64 Json String in base64 umwandeln
b64data := input[strings.IndexByte(input, ',')+1:]
// Base64 in bytes umwandeln und speichern
sDec, err := b64.StdEncoding.DecodeString(b64data)
tmpRestore = System.Folder.Temp + "restore" + string(os.PathSeparator)
err = checkFolder(tmpRestore)
if err != nil {
return
}
var archive = System.Folder.Temp + "restore.zip"
err = writeByteToFile(archive, sDec)
// Zip Archiv in tmp entpacken
err = extractZIP(archive, tmpRestore)
if err != nil {
return
}
// Zip Archiv entpacken
// Neue Config laden um den Port und die Version zu überprüfen
newConfig, err := loadJSONFileToMap(tmpRestore + "settings.json")
if err != nil {
ShowError(err, 0)
return
}
backupVersion = newConfig["version"].(string)
if backupVersion < System.Compatibility {
err = errors.New(getErrMsg(1013))
return
}
// Zip Archiv in den Config Ordner entpacken
err = extractZIP(archive, System.Folder.Config)
if err != nil {
return
}
// Neue Config laden um den Port zu überprüfen
newConfig, err := loadJSONFileToMap(System.Folder.Config + "settings.json")
// Neue Config laden um den Port und die Version zu überprüfen
newConfig, err = loadJSONFileToMap(System.Folder.Config + "settings.json")
if err != nil {
ShowError(err, 0)
return
@@ -187,5 +197,78 @@ func xteveRestore(input string) (newWebURL string, err error) {
var url = System.URLBase + "/web/"
newWebURL = strings.Replace(url, ":"+oldPort, ":"+newPort, 1)
os.RemoveAll(tmpRestore)
return
}
func xteveRestoreFromWeb(input string) (newWebURL string, err error) {
// Base64 Json String in base64 umwandeln
b64data := input[strings.IndexByte(input, ',')+1:]
// Base64 in bytes umwandeln und speichern
sDec, err := b64.StdEncoding.DecodeString(b64data)
if err != nil {
return
}
var archive = System.Folder.Temp + "restore.zip"
err = writeByteToFile(archive, sDec)
if err != nil {
return
}
newWebURL, err = xteveRestore(archive)
return
}
// XteveRestoreFromCLI : Wiederherstellung über die Kommandozeile
func XteveRestoreFromCLI(archive string) (err error) {
var confirm string
println()
showInfo(fmt.Sprintf("Version:%s Build: %s", System.Version, System.Build))
showInfo(fmt.Sprintf("Backup File:%s", archive))
showInfo(fmt.Sprintf("System Folder:%s", getPlatformPath(System.Folder.Config)))
println()
fmt.Print("All data will be replaced with those from the backup. Should the files be restored? [yes|no]:")
fmt.Scanln(&confirm)
switch strings.ToLower(confirm) {
case "yes":
break
case "no":
return
default:
fmt.Println("Invalid input")
return
}
if len(System.Folder.Config) > 0 {
err = checkFilePermission(System.Folder.Config)
if err != nil {
return
}
_, err = xteveRestore(archive)
if err != nil {
return
}
showHighlight(fmt.Sprintf("Restor:Backup was successfully restored. %s can now be started normally", System.Name))
}
return
}

View File

@@ -709,7 +709,7 @@ InitBuffer:
if resp.StatusCode != http.StatusOK {
showInfo("Content type:" + contentType)
showInfo("Content Type:" + contentType)
showInfo("Streaming Status:" + httpStatusInfo)
showInfo("Error with this URL:" + currentURL)
@@ -754,7 +754,7 @@ InitBuffer:
showDebug(debug, 1)
showInfo("Streaming Status:" + "HTTP Response Status [" + strconv.Itoa(resp.StatusCode) + "] " + http.StatusText(resp.StatusCode))
showInfo("Content type:" + contentType)
showInfo("Content Type:" + contentType)
} else {
@@ -790,7 +790,7 @@ InitBuffer:
}
// Video Stream (TS)
case "video/mpeg", "video/mp4", "video/mp2t", "application/octet-stream", "binary/octet-stream":
case "video/mpeg", "video/mp4", "video/mp2t", "application/octet-stream", "binary/octet-stream", "application/mp2t":
var fileSize int
@@ -931,7 +931,7 @@ InitBuffer:
// Umbekanntes Format
default:
showInfo("Content type:" + resp.Header.Get("Content-Type"))
showInfo("Content Type:" + resp.Header.Get("Content-Type"))
err = errors.New("Streaming error")
ShowError(err, 4003)

View File

@@ -83,6 +83,11 @@ func Init() (err error) {
return
}
if len(System.Flag.Restore) > 0 {
// Einstellungen werden über CLI wiederhergestellt. Weitere Initialisierung ist nicht notwendig.
return
}
System.File.XML = getPlatformFile(fmt.Sprintf("%s%s.xml", System.Folder.Data, System.AppName))
System.File.M3U = getPlatformFile(fmt.Sprintf("%s%s.m3u", System.Folder.Data, System.AppName))
@@ -161,9 +166,6 @@ func Init() (err error) {
return
}
// DLNA Server starten
go SSDP()
// Branch festlegen
System.Branch = Settings.Branch
@@ -194,6 +196,13 @@ func Init() (err error) {
}
// DLNA Server starten
err = SSDP()
if err != nil {
return
}
// HTML Datein laden
loadHTMLMap()
return

99
src/info.go Normal file
View File

@@ -0,0 +1,99 @@
package src
import (
"fmt"
"strings"
)
// ShowSystemInfo : Systeminformationen anzeigen
func ShowSystemInfo() {
fmt.Print("Creating the information takes a moment...")
err := buildDatabaseDVR()
if err != nil {
ShowError(err, 0)
return
}
buildXEPG(false)
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("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:"))
for i, ipv4 := range System.IPAddressesV4 {
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
}
}
println()
fmt.Println(fmt.Sprintf("IPv6 Addresses:"))
for i, ipv4 := range System.IPAddressesV6 {
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
}
}
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))
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))
println("---")
fmt.Println("Settings [Streaming]")
fmt.Println(fmt.Sprintf("Buffer: %t", Settings.Buffer))
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("---")
fmt.Println("Settings [Backup]")
fmt.Println(fmt.Sprintf("Folder (backup): %s", Settings.BackupPath))
fmt.Println(fmt.Sprintf("Backup Keep: %d", Settings.BackupKeep))
}

View File

@@ -12,6 +12,10 @@ import (
func showInfo(str string) {
if System.Flag.Info == true {
return
}
var max = 22
var msg = strings.SplitN(str, ":", 2)
var length = len(msg[0])
@@ -240,7 +244,7 @@ func getErrMsg(errCode int) (errMsg string) {
case 1012:
errMsg = fmt.Sprintf("Invalid formatting of the time")
case 1013:
errMsg = fmt.Sprintf("Invalid settings file (%s), file must be at least version %s", System.File.Settings, System.Compatibility)
errMsg = fmt.Sprintf("Invalid settings file (settings.json), file must be at least version %s", System.Compatibility)
case 1020:
errMsg = fmt.Sprintf("Data could not be saved, invalid keyword")

View File

@@ -11,24 +11,26 @@ import (
)
// SSDP : SSPD / DLNA Server
func SSDP() {
func SSDP() (err error) {
showInfo(fmt.Sprintf("SSDP / DLNA:%t", Settings.SSDP))
if Settings.SSDP == false {
if Settings.SSDP == false || System.Flag.Info == true {
return
}
time.Sleep(10 * time.Second)
showInfo(fmt.Sprintf("SSDP / DLNA:%t", Settings.SSDP))
quit := make(chan os.Signal, 1)
signal.Notify(quit, os.Interrupt)
ad, err := ssdp.Advertise(
"upnp:"+System.AppName, // send as "ST"
System.DeviceID+"::upnp:"+System.AppName, // send as "USN"
System.URLBase+"/device.xml", // send as "LOCATION"
System.AppName, // send as "SERVER"
1800) // send as "maxAge" in "CACHE-CONTROL"
fmt.Sprintf("upnp:rootdevice"), // send as "ST"
fmt.Sprintf("uuid:%s::upnp:rootdevice", System.DeviceID), // send as "USN"
fmt.Sprintf("%s/device.xml", System.URLBase), // send as "LOCATION"
System.AppName, // send as "SERVER"
1800) // send as "maxAge" in "CACHE-CONTROL"
if err != nil {
ShowError(err, 000)
return
}
// Debug SSDP
@@ -36,34 +38,35 @@ func SSDP() {
ssdp.Logger = log.New(os.Stderr, "[SSDP] ", log.LstdFlags)
}
var aliveTick <-chan time.Time
var ai = 10
go func(adv *ssdp.Advertiser) {
if ai > 0 {
aliveTick = time.Tick(time.Duration(ai) * time.Second)
} else {
aliveTick = make(chan time.Time)
}
aliveTick := time.Tick(300 * time.Second)
quit := make(chan os.Signal, 1)
signal.Notify(quit, os.Interrupt)
loop:
for {
loop:
select {
for {
case <-aliveTick:
err = adv.Alive()
if err != nil {
ShowError(err, 0)
adv.Bye()
adv.Close()
break loop
}
select {
case <-quit:
adv.Bye()
adv.Close()
os.Exit(0)
break loop
case <-aliveTick:
ad.Alive()
case <-quit:
os.Exit(0)
break loop
}
}
}
}(ad)
ad.Bye()
ad.Close()
return
}

View File

@@ -32,10 +32,12 @@ type SystemStruct struct {
}
Flag struct {
Branch string
Debug int
Port string
SSDP bool
Branch string
Debug int
Info bool
Port string
Restore string
SSDP bool
}
Folder struct {

View File

@@ -117,7 +117,7 @@ func loadSettings() (settings SettingsStrcut, err error) {
defaults["buffer.size.kb"] = 1024
defaults["buffer.timeout"] = 500
defaults["cache.images"] = false
defaults["epgSource"] = "XEPG"
defaults["epgSource"] = "PMS"
defaults["files"] = dataMap
defaults["files.update"] = true
defaults["filter"] = make(map[string]interface{})

File diff suppressed because one or more lines are too long

View File

@@ -34,7 +34,19 @@ func StartWebserver() (err error) {
showInfo("DVR IP:" + System.IPAddress + ":" + Settings.Port)
showHighlight(fmt.Sprintf("Web Interface:%s://%s:%s/web/", System.ServerProtocol.WEB, System.IPAddress, Settings.Port))
var ips = len(System.IPAddressesV4) + len(System.IPAddressesV6) - 1
switch ips {
case 0:
showHighlight(fmt.Sprintf("Web Interface:%s://%s:%s/web/", System.ServerProtocol.WEB, System.IPAddress, Settings.Port))
case 1:
showHighlight(fmt.Sprintf("Web Interface:%s://%s:%s/web/ | xTeVe is also available via the other %d IP", System.ServerProtocol.WEB, System.IPAddress, Settings.Port, ips))
default:
showHighlight(fmt.Sprintf("Web Interface:%s://%s:%s/web/ | xTeVe is also available via the other %d IP's", System.ServerProtocol.WEB, System.IPAddress, Settings.Port, len(System.IPAddressesV4)+len(System.IPAddressesV6)-1))
}
if err = http.ListenAndServe(":"+port, nil); err != nil {
ShowError(err, 1001)
@@ -147,7 +159,8 @@ func Stream(w http.ResponseWriter, r *http.Request) {
showInfo("Streaming URL:" + streamInfo.URL)
http.Redirect(w, r, streamInfo.URL, 302)
showInfo("Streaming Info:URL was passed to the client")
showInfo("Streaming Info:URL was passed to the client.")
showInfo("Streaming Info:xTeVe is no longer involved, the client connects directly to the streaming server.")
}
@@ -302,6 +315,8 @@ func WS(w http.ResponseWriter, r *http.Request) {
http.Error(w, "Could not open websocket connection", http.StatusBadRequest)
}
setGlobalDomain(r.Host)
for {
err = conn.ReadJSON(&request)
@@ -447,7 +462,7 @@ func WS(w http.ResponseWriter, r *http.Request) {
file, errNew := xteveBackup()
err = errNew
if err == nil {
response.OpenLink = System.URLBase + "/download/" + file
response.OpenLink = fmt.Sprintf("%s://%s/download/%s", System.ServerProtocol.WEB, System.Domain, file)
}
case "xteveRestore":
@@ -457,9 +472,10 @@ func WS(w http.ResponseWriter, r *http.Request) {
if len(request.Base64) > 0 {
newWebURL, err := xteveRestore(request.Base64)
newWebURL, err := xteveRestoreFromWeb(request.Base64)
if err != nil {
ShowError(err, 000)
response.Alert = err.Error()
}
if err == nil {
@@ -528,7 +544,6 @@ func WS(w http.ResponseWriter, r *http.Request) {
response.Settings = Settings
}
setGlobalDomain(r.Host)
response = setDefaultResponseData(response, true)
if System.ConfigurationWizard == true {
response.ConfigurationWizard = System.ConfigurationWizard
@@ -867,8 +882,6 @@ func API(w http.ResponseWriter, r *http.Request) {
case "status":
fmt.Println("-----------------------------")
os.Exit(0)
response.VersionXteve = System.Version
response.VersionAPI = System.APIVersion
response.StreamsActive = int64(len(Data.Streams.Active))

View File

@@ -174,49 +174,58 @@ func createXEPGMapping() {
return
}
if len(Data.XMLTV.Files) == 0 {
return
}
if len(Data.XMLTV.Files) > 0 {
for i := len(Data.XMLTV.Files) - 1; i >= 0; i-- {
for i := len(Data.XMLTV.Files) - 1; i >= 0; i-- {
var file = Data.XMLTV.Files[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"))
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
//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)
}
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 {
// XML Parsen (Provider Datei)
if err == nil {
// Daten aus der XML Datei in eine temporäre Map schreiben
var xmltvMap = make(map[string]interface{})
// 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{})
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
channel["id"] = c.ID
channel["display-name"] = friendlyDisplayName(*c)
channel["icon"] = c.Icon.Src
xmltvMap[c.ID] = channel
xmltvMap[c.ID] = channel
}
tmpMap[getFilenameFromPath(file)] = xmltvMap
Data.XMLTV.Mapping[getFilenameFromPath(file)] = xmltvMap
}
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)
}
}
@@ -236,11 +245,8 @@ func createXEPGMapping() {
}
Data.XMLTV.Mapping = tmpMap
Data.XMLTV.Mapping["xTeVe Dummy"] = dummy
tmpMap = make(map[string]interface{})
return
}
@@ -583,7 +589,12 @@ func createXMLTVFile() (err error) {
var xepgXML XMLTV
xepgXML.Generator = System.Name
xepgXML.Source = fmt.Sprintf("%s - %s", System.Name, System.Version)
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{}

View File

@@ -39,7 +39,7 @@ var GitHub = GitHubStruct{Branch: "master", User: "xteve-project", Repo: "xTeVe-
const Name = "xTeVe"
// Version : Version, die Build Nummer wird in der main func geparst.
const Version = "2.0.0.0002"
const Version = "2.0.0.0007"
// DBVersion : Datanbank Version
const DBVersion = "2.0.0"
@@ -52,12 +52,15 @@ const Dev = false
var homeDirectory = fmt.Sprintf("%s%s.%s%s", src.GetUserHomeDirectory(), string(os.PathSeparator), strings.ToLower(Name), string(os.PathSeparator))
var samplePath = fmt.Sprintf("%spath%sto%sxteve%s", string(os.PathSeparator), string(os.PathSeparator), string(os.PathSeparator), string(os.PathSeparator))
var sampleRestore = fmt.Sprintf("%spath%sto%sfile%s", string(os.PathSeparator), string(os.PathSeparator), string(os.PathSeparator), string(os.PathSeparator))
var configFolder = flag.String("config", "", ": Config Folder ["+samplePath+"] (default: "+homeDirectory+")")
var port = flag.String("port", "", ": Server port [34400] (default: 34400)")
var restore = flag.String("restore", "", ": Restore from backup ["+sampleRestore+"xteve_backup.zip]")
var gitBranch = flag.String("branch", "", ": Git Branch [master|beta] (default: master)")
var debug = flag.Int("debug", 0, ": Debug level [0 - 3] (default: 0)")
var info = flag.Bool("info", false, ": Show system info")
var h = flag.Bool("h", false, ": Show help")
func main() {
@@ -119,6 +122,22 @@ func main() {
return
}
// Systeminformationen anzeigen
if *info {
system.Flag.Info = true
err := src.Init()
if err != nil {
src.ShowError(err, 0)
os.Exit(0)
}
src.ShowSystemInfo()
return
}
// Webserver Port
if len(*port) > 0 {
system.Flag.Port = *port
@@ -142,6 +161,25 @@ func main() {
system.Folder.Config = *configFolder
}
// Backup wiederherstellen
if len(*restore) > 0 {
system.Flag.Restore = *restore
err := src.Init()
if err != nil {
src.ShowError(err, 0)
os.Exit(0)
}
err = src.XteveRestoreFromCLI(*restore)
if err != nil {
src.ShowError(err, 0)
}
os.Exit(0)
}
err := src.Init()
if err != nil {
src.ShowError(err, 0)