Phase 3: Go hygiene pass, runtime PUID/PGID, fork README
continuous-integration/drone/push Build encountered an error

Go:
- staticcheck 399 -> 0 with staticcheck.conf (style checks ST1000/1003/
  1005/1016/1020/1021/1022 excluded; error strings are shown in the UI).
- io/ioutil and rand.Seed removed; CloseNotifier kept with a lint-ignore
  until the Phase 2 context rewrite.
- Dead code deleted: Auto handler, getStreamByChannelID, updateXEPG,
  indexOfInt, jsonToMapInt64, removeOldSystemData, randomTime, and the
  commented-out blocks in struct-buffer.go and internal/authentication.
- Duplicates folded: cacheImagesInBackground(), one addErrorToStream().
- Bugs found by SA4006/SA5001: os.Create handle leaked per ffmpeg segment
  (buffer.go), http.NewRequest error unchecked (buffer.go), xepg.json
  migration wrote null on read error (migrate.go), WriteUserData errors
  silently dropped (authentication.go), defer Close before error check
  (buffer.go, toolchain.go). checkFilePermission results were discarded;
  an unwritable config or temp dir is now fatal at start-up.
- gofmt applied repo-wide; Drone runs gofmt check and staticcheck.

Docker:
- Entrypoint starts as root, applies PUID/PGID (falls back to XTEVE_UID/
  XTEVE_GID, then image defaults), fixes config ownership only when it
  differs, then drops to xteve via su-exec. --user starts skip all of it.
- /xteve removed from LEGACY_CONFIG_DIRS (it is the parent of the default).
- mwader/static-ffmpeg pinned to 7.1.1; VOLUME /xteve/config.
- Compose files pull registry.coadcorp.com/nathan/xteve:latest, use
  PUID/PGID/TZ, and explain that SSDP needs host networking.
- .dockerignore excludes the npm toolchain (bundle stays in html/js).

Docs: README rewritten for the fork (about, registry, compose, env vars,
security notes); README-DEV gains a container section.
This commit is contained in:
2026-09-26 12:57:43 +10:00
parent ddc70e170a
commit 504ea3f9f4
38 changed files with 1034 additions and 1382 deletions
+10 -6
View File
@@ -63,7 +63,7 @@ func createFirstUserForAuthentication(username, password string) (token string,
func tokenAuthentication(token string) (newToken string, err error) {
if System.ConfigurationWizard == true {
if System.ConfigurationWizard {
return
}
@@ -109,7 +109,7 @@ func urlAuth(r *http.Request, requestType string) (err error) {
case "m3u":
level = "authentication.m3u"
if Settings.AuthenticationM3U == true {
if Settings.AuthenticationM3U {
token, err = authentication.UserAuthentication(username, password)
if err != nil {
return
@@ -119,7 +119,7 @@ func urlAuth(r *http.Request, requestType string) (err error) {
case "xml":
level = "authentication.xml"
if Settings.AuthenticationXML == true {
if Settings.AuthenticationXML {
token, err = authentication.UserAuthentication(username, password)
if err != nil {
return
@@ -150,18 +150,22 @@ func checkAuthorizationLevel(token, level string) (err error) {
if v, ok := userData[level].(bool); ok {
if v == false {
if !v {
err = errors.New("No authorization")
}
} else {
userData[level] = false
err = authentication.WriteUserData(userID, userData)
if err = authentication.WriteUserData(userID, userData); err != nil {
ShowError(err, 0)
}
err = errors.New("No authorization")
}
} else {
err = authentication.WriteUserData(userID, userData)
if err = authentication.WriteUserData(userID, userData); err != nil {
ShowError(err, 0)
}
err = errors.New("No authorization")
}
+179 -180
View File
@@ -1,274 +1,273 @@
package src
import (
b64 "encoding/base64"
"errors"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"time"
b64 "encoding/base64"
"errors"
"fmt"
"os"
"path/filepath"
"strings"
"time"
)
func xTeVeAutoBackup() (err error) {
var archiv = "xteve_auto_backup_" + time.Now().Format("20060102_1504") + ".zip"
var target string
var sourceFiles = make([]string, 0)
var oldBackupFiles = make([]string, 0)
var debug string
var archiv = "xteve_auto_backup_" + time.Now().Format("20060102_1504") + ".zip"
var target string
var sourceFiles = make([]string, 0)
var oldBackupFiles = make([]string, 0)
var debug string
if len(Settings.BackupPath) > 0 {
System.Folder.Backup = Settings.BackupPath
}
if len(Settings.BackupPath) > 0 {
System.Folder.Backup = Settings.BackupPath
}
showInfo("Backup Path:" + System.Folder.Backup)
showInfo("Backup Path:" + System.Folder.Backup)
err = checkFolder(System.Folder.Backup)
if err != nil {
ShowError(err, 1070)
return
}
err = checkFolder(System.Folder.Backup)
if err != nil {
ShowError(err, 1070)
return
}
// Alte Backups löschen
files, err := ioutil.ReadDir(System.Folder.Backup)
// Alte Backups löschen
files, err := os.ReadDir(System.Folder.Backup)
if err == nil {
if err == nil {
for _, file := range files {
for _, file := range files {
if filepath.Ext(file.Name()) == ".zip" && strings.Contains(file.Name(), "xteve_auto_backup") {
oldBackupFiles = append(oldBackupFiles, file.Name())
}
if filepath.Ext(file.Name()) == ".zip" && strings.Contains(file.Name(), "xteve_auto_backup") {
oldBackupFiles = append(oldBackupFiles, file.Name())
}
}
}
// Alle Backups löschen
var end int
switch Settings.BackupKeep {
case 0:
end = 0
default:
end = Settings.BackupKeep - 1
}
// Alle Backups löschen
var end int
switch Settings.BackupKeep {
case 0:
end = 0
default:
end = Settings.BackupKeep - 1
}
for i := 0; i < len(oldBackupFiles)-end; i++ {
for i := 0; i < len(oldBackupFiles)-end; i++ {
os.RemoveAll(System.Folder.Backup + oldBackupFiles[i])
debug = fmt.Sprintf("Delete backup file:%s", oldBackupFiles[i])
showDebug(debug, 1)
os.RemoveAll(System.Folder.Backup + oldBackupFiles[i])
debug = fmt.Sprintf("Delete backup file:%s", oldBackupFiles[i])
showDebug(debug, 1)
}
}
if Settings.BackupKeep == 0 {
return
}
if Settings.BackupKeep == 0 {
return
}
} else {
} else {
return
return
}
}
// Backup erstellen
if err == nil {
// Backup erstellen
if err == nil {
target = System.Folder.Backup + archiv
target = System.Folder.Backup + archiv
for _, i := range SystemFiles {
sourceFiles = append(sourceFiles, System.Folder.Config+i)
}
for _, i := range SystemFiles {
sourceFiles = append(sourceFiles, System.Folder.Config+i)
}
sourceFiles = append(sourceFiles, System.Folder.ImagesUpload)
sourceFiles = append(sourceFiles, System.Folder.ImagesUpload)
err = zipFiles(sourceFiles, target)
err = zipFiles(sourceFiles, target)
if err == nil {
if err == nil {
debug = fmt.Sprintf("Create backup file:%s", target)
showDebug(debug, 1)
debug = fmt.Sprintf("Create backup file:%s", target)
showDebug(debug, 1)
showInfo("Backup file:" + target)
showInfo("Backup file:" + target)
}
}
}
}
return
return
}
func xteveBackup() (archiv string, err error) {
err = checkFolder(System.Folder.Temp)
if err != nil {
return
}
err = checkFolder(System.Folder.Temp)
if err != nil {
return
}
archiv = "xteve_backup_" + time.Now().Format("20060102_1504") + ".zip"
archiv = "xteve_backup_" + time.Now().Format("20060102_1504") + ".zip"
var target = System.Folder.Temp + archiv
var sourceFiles = make([]string, 0)
var target = System.Folder.Temp + archiv
var sourceFiles = make([]string, 0)
for _, i := range SystemFiles {
sourceFiles = append(sourceFiles, System.Folder.Config+i)
}
for _, i := range SystemFiles {
sourceFiles = append(sourceFiles, System.Folder.Config+i)
}
sourceFiles = append(sourceFiles, System.Folder.Data)
sourceFiles = append(sourceFiles, System.Folder.Data)
err = zipFiles(sourceFiles, target)
if err != nil {
ShowError(err, 0)
return
}
err = zipFiles(sourceFiles, target)
if err != nil {
ShowError(err, 0)
return
}
return
return
}
func xteveRestore(archive string) (newWebURL string, err error) {
var newPort, oldPort, backupVersion, tmpRestore string
var newPort, oldPort, backupVersion, tmpRestore string
tmpRestore = System.Folder.Temp + "restore" + string(os.PathSeparator)
tmpRestore = System.Folder.Temp + "restore" + string(os.PathSeparator)
err = checkFolder(tmpRestore)
if err != nil {
return
}
err = checkFolder(tmpRestore)
if err != nil {
return
}
// Zip Archiv in tmp entpacken
err = extractZIP(archive, tmpRestore)
if err != nil {
return
}
// Zip Archiv in tmp entpacken
err = extractZIP(archive, tmpRestore)
if err != nil {
return
}
// 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
}
// 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
}
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
}
// Zip Archiv in den Config Ordner entpacken
err = extractZIP(archive, System.Folder.Config)
if err != nil {
return
}
// 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
}
// 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
}
newPort = newConfig["port"].(string)
oldPort = Settings.Port
newPort = newConfig["port"].(string)
oldPort = Settings.Port
if newPort == oldPort {
if newPort == oldPort {
if err != nil {
ShowError(err, 0)
}
if err != nil {
ShowError(err, 0)
}
loadSettings()
loadSettings()
err := Init()
if err != nil {
ShowError(err, 0)
return "", err
}
err := Init()
if err != nil {
ShowError(err, 0)
return "", err
}
err = StartSystem(true)
if err != nil {
ShowError(err, 0)
return "", err
}
err = StartSystem(true)
if err != nil {
ShowError(err, 0)
return "", err
}
return "", err
}
return "", err
}
var url = System.URLBase + "/web/"
newWebURL = strings.Replace(url, ":"+oldPort, ":"+newPort, 1)
var url = System.URLBase + "/web/"
newWebURL = strings.Replace(url, ":"+oldPort, ":"+newPort, 1)
os.RemoveAll(tmpRestore)
os.RemoveAll(tmpRestore)
return
return
}
func xteveRestoreFromWeb(input string) (newWebURL string, err error) {
// Base64 Json String in base64 umwandeln
b64data := input[strings.IndexByte(input, ',')+1:]
// Base64 Json String in base64 umwandeln
b64data := input[strings.IndexByte(input, ',')+1:]
// Base64 in bytes umwandeln und speichern
sDec, err := b64.StdEncoding.DecodeString(b64data)
// Base64 in bytes umwandeln und speichern
sDec, err := b64.StdEncoding.DecodeString(b64data)
if err != nil {
return
}
if err != nil {
return
}
var archive = System.Folder.Temp + "restore.zip"
var archive = System.Folder.Temp + "restore.zip"
err = writeByteToFile(archive, sDec)
if err != nil {
return
}
err = writeByteToFile(archive, sDec)
if err != nil {
return
}
newWebURL, err = xteveRestore(archive)
newWebURL, err = xteveRestore(archive)
return
return
}
// XteveRestoreFromCLI : Wiederherstellung über die Kommandozeile
func XteveRestoreFromCLI(archive string) (err error) {
var confirm string
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()
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.Print("All data will be replaced with those from the backup. Should the files be restored? [yes|no]:")
fmt.Scanln(&confirm)
fmt.Scanln(&confirm)
switch strings.ToLower(confirm) {
switch strings.ToLower(confirm) {
case "yes":
break
case "yes":
break
case "no":
return
case "no":
return
default:
fmt.Println("Invalid input")
return
default:
fmt.Println("Invalid input")
return
}
}
if len(System.Folder.Config) > 0 {
if len(System.Folder.Config) > 0 {
err = checkFilePermission(System.Folder.Config)
if err != nil {
return
}
err = checkFilePermission(System.Folder.Config)
if err != nil {
return
}
_, err = xteveRestore(archive)
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))
showHighlight(fmt.Sprintf("Restor:Backup was successfully restored. %s can now be started normally", System.Name))
}
return
}
return
}
+77 -86
View File
@@ -11,7 +11,6 @@ import (
"errors"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"os"
@@ -146,7 +145,7 @@ func bufferingStream(playlistID, streamingURL, channelName string, w http.Respon
}
// Neuer Stream bei einer bereits aktiven Playlist
if newStream == true {
if newStream {
// Prüfen ob die Playlist noch einen weiteren Stream erlaubt (Tuner)
if len(playlist.Streams) >= playlist.Tuner {
@@ -193,7 +192,7 @@ func bufferingStream(playlistID, streamingURL, channelName string, w http.Respon
}
// Überprüfen ob der Stream breits von einem anderen Client abgespielt wird
if playlist.Streams[streamID].Status == false && newStream == true {
if !playlist.Streams[streamID].Status && newStream {
// Neuer Buffer wird benötigt
stream = playlist.Streams[streamID]
@@ -235,7 +234,7 @@ func bufferingStream(playlistID, streamingURL, channelName string, w http.Respon
if stream, ok := playlist.Streams[streamID]; ok {
if stream.Status == false {
if !stream.Status {
timeOut++
@@ -261,6 +260,7 @@ func bufferingStream(playlistID, streamingURL, channelName string, w http.Respon
// HTTP Clientverbindung überwachen
//lint:ignore SA1019 replaced by request context in Phase 2
cn, ok := w.(http.CloseNotifier)
if ok {
@@ -310,10 +310,11 @@ func bufferingStream(playlistID, streamingURL, channelName string, w http.Respon
var fileName = stream.Folder + f
file, err := os.Open(fileName)
defer file.Close()
if err == nil {
defer file.Close()
l, err := file.Stat()
if err == nil {
@@ -327,7 +328,7 @@ func bufferingStream(playlistID, streamingURL, channelName string, w http.Respon
file.Seek(0, 0)
if streaming == false {
if !streaming {
contentType := http.DetectContentType(buffer)
_ = contentType
@@ -406,7 +407,7 @@ func getTmpFiles(stream *ThisStream) (tmpFiles []string) {
if _, err := os.Stat(tmpFolder); !os.IsNotExist(err) {
files, err := ioutil.ReadDir(getPlatformPath(tmpFolder))
files, err := os.ReadDir(getPlatformPath(tmpFolder))
if err != nil {
ShowError(err, 000)
return
@@ -455,7 +456,7 @@ func killClientConnection(streamID int, playlistID string, force bool) {
var playlist = p.(Playlist)
if force == true {
if force {
delete(playlist.Streams, streamID)
showInfo(fmt.Sprintf("Streaming Status:Playlist: %s - Tuner: %d / %d", playlist.PlaylistName, len(playlist.Streams), playlist.Tuner))
return
@@ -531,6 +532,21 @@ func clientConnection(stream ThisStream) (status bool) {
return
}
// addErrorToStream : Fehler an die Clientverbindung des Streams weitergeben
func addErrorToStream(playlist Playlist, streamID int, playlistID string, err error) {
var stream = playlist.Streams[streamID]
if c, ok := BufferClients.Load(playlistID + stream.MD5); ok {
var clients = c.(ClientConnection)
clients.Error = err
BufferClients.Store(playlistID+stream.MD5, clients)
}
}
func connectToStreamingServer(streamID int, playlistID string) {
if p, ok := BufferInformation.Load(playlistID); ok {
@@ -575,26 +591,12 @@ func connectToStreamingServer(streamID int, playlistID string) {
}
var addErrorToStream = func(err error) {
var stream = playlist.Streams[streamID]
if c, ok := BufferClients.Load(playlistID + stream.MD5); ok {
var clients = c.(ClientConnection)
clients.Error = err
BufferClients.Store(playlistID+stream.MD5, clients)
}
}
os.RemoveAll(getPlatformPath(tmpFolder))
err := checkFolder(tmpFolder)
if err != nil {
ShowError(err, 0)
addErrorToStream(err)
addErrorToStream(playlist, streamID, playlistID, err)
return
}
@@ -609,11 +611,11 @@ func connectToStreamingServer(streamID int, playlistID string) {
return
}
var stream ThisStream = playlist.Streams[streamID]
var stream = playlist.Streams[streamID]
if stream.Status == false {
if !stream.Status {
if strings.Index(stream.URL, ".m3u8") != -1 {
if strings.Contains(stream.URL, ".m3u8") {
showInfo("Streaming Type:" + "[HLS / M3U8]")
} else {
showInfo("Streaming Type:" + "[TS]")
@@ -631,7 +633,7 @@ func connectToStreamingServer(streamID int, playlistID string) {
for {
if clientConnection(stream) == false {
if !clientConnection(stream) {
return
}
@@ -654,6 +656,14 @@ func connectToStreamingServer(streamID int, playlistID string) {
Redirect:
req, err := http.NewRequest("GET", currentURL, nil)
if err != nil {
ShowError(err, 0)
addErrorToStream(playlist, streamID, playlistID, err)
killClientConnection(streamID, playlistID, true)
clientConnection(stream)
return
}
req.Header.Set("User-Agent", getUserAgent())
req.Header.Set("Connection", "close")
//req.Header.Set("Range", "bytes=0-")
@@ -679,7 +689,7 @@ func connectToStreamingServer(streamID int, playlistID string) {
fmt.Println("Current URL:", currentURL)
ShowError(err, 0)
addErrorToStream(err)
addErrorToStream(playlist, streamID, playlistID, err)
killClientConnection(streamID, playlistID, true)
clientConnection(stream)
@@ -708,7 +718,7 @@ func connectToStreamingServer(streamID int, playlistID string) {
err = errors.New("Streaming server")
ShowError(err, 4002)
addErrorToStream(err)
addErrorToStream(playlist, streamID, playlistID, err)
defer resp.Body.Close()
@@ -719,7 +729,7 @@ func connectToStreamingServer(streamID int, playlistID string) {
} else {
ShowError(err, 0)
addErrorToStream(err)
addErrorToStream(playlist, streamID, playlistID, err)
defer resp.Body.Close()
@@ -749,7 +759,7 @@ func connectToStreamingServer(streamID int, playlistID string) {
showDebug(debug, 1)
BufferInformation.Store(playlist.PlaylistID, playlist)
addErrorToStream(err)
addErrorToStream(playlist, streamID, playlistID, err)
killClientConnection(streamID, playlistID, true)
clientConnection(stream)
@@ -759,7 +769,7 @@ func connectToStreamingServer(streamID int, playlistID string) {
}
// Informationen über den Streamingserver auslesen
if stream.Status == false {
if !stream.Status {
if len(stream.URLStreamingServer) == 0 {
@@ -802,10 +812,10 @@ func connectToStreamingServer(streamID int, playlistID string) {
// M3U8 Playlist
case "application/x-mpegurl", "application/vnd.apple.mpegurl", "audio/mpegurl", "audio/x-mpegurl":
body, err := ioutil.ReadAll(resp.Body)
body, err := io.ReadAll(resp.Body)
if err != nil {
ShowError(err, 0)
addErrorToStream(err)
addErrorToStream(playlist, streamID, playlistID, err)
}
stream.Body = string(body)
@@ -815,7 +825,7 @@ func connectToStreamingServer(streamID int, playlistID string) {
err = parseM3U8(&stream)
if err != nil {
ShowError(err, 4050)
addErrorToStream(err)
addErrorToStream(playlist, streamID, playlistID, err)
}
// Video Stream (TS)
@@ -835,7 +845,7 @@ func connectToStreamingServer(streamID int, playlistID string) {
var tmpFile = fmt.Sprintf("%s%d.ts", tmpFolder, tmpSegment)
if clientConnection(stream) == false {
if !clientConnection(stream) {
resp.Body.Close()
return
}
@@ -843,7 +853,7 @@ func connectToStreamingServer(streamID int, playlistID string) {
bufferFile, err := os.Create(tmpFile)
if err != nil {
addErrorToStream(err)
addErrorToStream(playlist, streamID, playlistID, err)
bufferFile.Close()
resp.Body.Close()
return
@@ -866,7 +876,7 @@ func connectToStreamingServer(streamID int, playlistID string) {
if err != nil && err != io.EOF {
ShowError(err, 0)
addErrorToStream(err)
addErrorToStream(playlist, streamID, playlistID, err)
resp.Body.Close()
return
@@ -877,7 +887,7 @@ func connectToStreamingServer(streamID int, playlistID string) {
if _, err := bufferFile.Write(buffer[:n]); err != nil {
ShowError(err, 0)
addErrorToStream(err)
addErrorToStream(playlist, streamID, playlistID, err)
resp.Body.Close()
return
@@ -887,7 +897,7 @@ func connectToStreamingServer(streamID int, playlistID string) {
fileSize = fileSize + n
if clientConnection(stream) == false {
if !clientConnection(stream) {
resp.Body.Close()
bufferFile.Close()
@@ -929,7 +939,7 @@ func connectToStreamingServer(streamID int, playlistID string) {
tmpFile = fmt.Sprintf("%s%d.ts", tmpFolder, tmpSegment)
if clientConnection(stream) == false {
if !clientConnection(stream) {
bufferFile.Close()
resp.Body.Close()
@@ -944,7 +954,7 @@ func connectToStreamingServer(streamID int, playlistID string) {
bufferFile, err = os.Create(tmpFile)
if err != nil {
addErrorToStream(err)
addErrorToStream(playlist, streamID, playlistID, err)
resp.Body.Close()
return
}
@@ -970,7 +980,7 @@ func connectToStreamingServer(streamID int, playlistID string) {
err = errors.New("Streaming error")
ShowError(err, 4003)
addErrorToStream(err)
addErrorToStream(playlist, streamID, playlistID, err)
resp.Body.Close()
return
}
@@ -978,7 +988,7 @@ func connectToStreamingServer(streamID int, playlistID string) {
s++
// Wartezeit für den Download das nächste Segments berechnen
if stream.HLS == true {
if stream.HLS {
var sleep float64
@@ -1143,14 +1153,13 @@ func parseM3U8(stream *ThisStream) (err error) {
}
return
}
if strings.Contains(stream.Body, "#EXTM3U") {
var lines = strings.Split(strings.Replace(stream.Body, "\r\n", "\n", -1), "\n")
if stream.DynamicBandwidth == false {
if !stream.DynamicBandwidth {
stream.DynamicStream = make(map[int]DynamicStream)
}
@@ -1173,7 +1182,7 @@ func parseM3U8(stream *ThisStream) (err error) {
}
// M3U8 enthällt mehrere Links zu weiteren M3U8 Wiedergabelisten (Bandbreitenoption)
if segment.Info == true && len(line) > 0 && line[0:1] != "#" {
if segment.Info && len(line) > 0 && line[0:1] != "#" {
var dynamicStream DynamicStream
@@ -1217,7 +1226,7 @@ func parseM3U8(stream *ThisStream) (err error) {
noNewSegment = true
if stream.Status == false {
if !stream.Status {
if len(m3u8Segments) >= 2 {
m3u8Segments = m3u8Segments[0 : len(m3u8Segments)-1]
@@ -1229,7 +1238,7 @@ func parseM3U8(stream *ThisStream) (err error) {
segment = s
if stream.Status == false {
if !stream.Status {
noNewSegment = false
stream.LastSequence = segment.Sequence
@@ -1255,9 +1264,9 @@ func parseM3U8(stream *ThisStream) (err error) {
}
if noNewSegment == false {
if !noNewSegment {
if stream.DynamicBandwidth == true {
if stream.DynamicBandwidth {
switchBandwidth(stream)
} else {
stream.Segment = append(stream.Segment, segment)
@@ -1265,7 +1274,7 @@ func parseM3U8(stream *ThisStream) (err error) {
}
if noNewSegment == true {
if noNewSegment {
var sleep = lastSegmentDuration * 0.5
@@ -1371,33 +1380,19 @@ func thirdPartyBuffer(streamID int, playlistID string) {
return
}
var addErrorToStream = func(err error) {
var stream = playlist.Streams[streamID]
if c, ok := BufferClients.Load(playlistID + stream.MD5); ok {
var clients = c.(ClientConnection)
clients.Error = err
BufferClients.Store(playlistID+stream.MD5, clients)
}
}
os.RemoveAll(getPlatformPath(tmpFolder))
err := checkFolder(tmpFolder)
if err != nil {
ShowError(err, 0)
addErrorToStream(err)
addErrorToStream(playlist, streamID, playlistID, err)
return
}
err = checkFile(path)
if err != nil {
ShowError(err, 0)
addErrorToStream(err)
addErrorToStream(playlist, streamID, playlistID, err)
return
}
@@ -1409,7 +1404,7 @@ func thirdPartyBuffer(streamID int, playlistID string) {
f, err := os.Create(tmpFile)
f.Close()
if err != nil {
addErrorToStream(err)
addErrorToStream(playlist, streamID, playlistID, err)
return
}
@@ -1461,7 +1456,7 @@ func thirdPartyBuffer(streamID int, playlistID string) {
ShowError(err, 0)
cmd.Process.Kill()
cmd.Wait()
addErrorToStream(err)
addErrorToStream(playlist, streamID, playlistID, err)
return
}
@@ -1471,11 +1466,11 @@ func thirdPartyBuffer(streamID int, playlistID string) {
ShowError(err, 0)
cmd.Process.Kill()
cmd.Wait()
addErrorToStream(err)
addErrorToStream(playlist, streamID, playlistID, err)
return
}
if len(buf.Bytes()) == 0 && stream.Status == false {
if len(buf.Bytes()) == 0 && !stream.Status {
showInfo(bufferType + ":Processing data")
}
@@ -1543,7 +1538,7 @@ func thirdPartyBuffer(streamID int, playlistID string) {
cmd.Process.Kill()
err = errors.New("Timout")
ShowError(err, 4006)
addErrorToStream(err)
addErrorToStream(playlist, streamID, playlistID, err)
cmd.Wait()
f.Close()
return
@@ -1553,11 +1548,11 @@ func thirdPartyBuffer(streamID int, playlistID string) {
}
if fileSize == 0 && stream.Status == false {
if fileSize == 0 && !stream.Status {
showInfo("Streaming Status:Receive data from " + bufferType)
}
if clientConnection(stream) == false {
if !clientConnection(stream) {
cmd.Process.Kill()
f.Close()
cmd.Wait()
@@ -1574,7 +1569,7 @@ func thirdPartyBuffer(streamID int, playlistID string) {
if _, err := f.Write(buffer[:n]); err != nil {
cmd.Process.Kill()
ShowError(err, 0)
addErrorToStream(err)
addErrorToStream(playlist, streamID, playlistID, err)
cmd.Wait()
f.Close()
return
@@ -1582,7 +1577,7 @@ func thirdPartyBuffer(streamID int, playlistID string) {
if fileSize >= bufferSize/2 {
if tmpSegment == 1 && stream.Status == false {
if tmpSegment == 1 && !stream.Status {
close(t)
close(streamStatus)
showInfo(fmt.Sprintf("Streaming Status:Buffering data from %s", bufferType))
@@ -1591,7 +1586,7 @@ func thirdPartyBuffer(streamID int, playlistID string) {
f.Close()
tmpSegment++
if stream.Status == false {
if !stream.Status {
Lock.Lock()
stream.Status = true
playlist.Streams[streamID] = stream
@@ -1603,13 +1598,11 @@ func thirdPartyBuffer(streamID int, playlistID string) {
fileSize = 0
var errCreate, errOpen error
f, errCreate = os.Create(tmpFile)
f, errOpen = os.OpenFile(tmpFile, os.O_APPEND|os.O_WRONLY, 0600)
if errCreate != nil || errOpen != nil {
f, err = os.OpenFile(tmpFile, os.O_CREATE|os.O_TRUNC|os.O_APPEND|os.O_WRONLY, 0666)
if err != nil {
cmd.Process.Kill()
ShowError(err, 0)
addErrorToStream(err)
addErrorToStream(playlist, streamID, playlistID, err)
cmd.Wait()
f.Close()
return
@@ -1623,7 +1616,7 @@ func thirdPartyBuffer(streamID int, playlistID string) {
cmd.Wait()
err = errors.New(bufferType + " error")
addErrorToStream(err)
addErrorToStream(playlist, streamID, playlistID, err)
ShowError(err, 1204)
time.Sleep(time.Duration(500) * time.Millisecond)
@@ -1694,7 +1687,6 @@ func debugRequest(req *http.Request) {
debug = "Request:* * * * * * END HTTP(S) REQUEST * * * * * *"
showDebug(debug, debugLevel)
return
}
func debugResponse(resp *http.Response) {
@@ -1739,5 +1731,4 @@ func debugResponse(resp *http.Response) {
debug = "Pesponse:* * * * * * END RESPONSE * * * * * * "
showDebug(debug, debugLevel)
return
}
+11 -4
View File
@@ -152,8 +152,9 @@ func Init() (err error) {
// Berechtigung aller Ordner überprüfen
err = checkFilePermission(System.Folder.Config)
if err == nil {
err = checkFilePermission(System.Folder.Temp)
if err != nil {
ShowError(err, 0)
return
}
// Separaten tmp Ordner für jede Instanz
@@ -165,6 +166,12 @@ func Init() (err error) {
return
}
err = checkFilePermission(System.Folder.Temp)
if err != nil {
ShowError(err, 0)
return
}
err = removeChildItems(getPlatformPath(System.Folder.Temp))
if err != nil {
return
@@ -173,7 +180,7 @@ func Init() (err error) {
// Branch festlegen
System.Branch = Settings.Branch
if System.Dev == true {
if System.Dev {
System.Branch = "Development"
}
@@ -217,7 +224,7 @@ func StartSystem(updateProviderFiles bool) (err error) {
showInfo(fmt.Sprintf("Unfiltered Chan. Limit:%d", System.UnfilteredChannelLimit))
// Providerdaten aktualisieren
if len(Settings.Files.M3U) > 0 && Settings.FilesUpdate == true || updateProviderFiles == true {
if len(Settings.Files.M3U) > 0 && Settings.FilesUpdate || updateProviderFiles {
err = xTeVeAutoBackup()
if err != nil {
+13 -22
View File
@@ -73,10 +73,6 @@ func updateServerSettings(request RequestStruct) (settings SettingsStruct, err e
}
if len(newUpdateTimes) == 0 {
//newUpdateTimes = append(newUpdateTimes, "0000")
}
value = newUpdateTimes
case "cache.images":
@@ -179,7 +175,7 @@ func updateServerSettings(request RequestStruct) (settings SettingsStruct, err e
return
}
if Settings.AuthenticationWEB == false {
if !Settings.AuthenticationWEB {
Settings.AuthenticationAPI = false
Settings.AuthenticationM3U = false
@@ -221,7 +217,7 @@ func updateServerSettings(request RequestStruct) (settings SettingsStruct, err e
settings = Settings
if reloadData == true {
if reloadData {
err = buildDatabaseDVR()
if err != nil {
@@ -232,7 +228,7 @@ func updateServerSettings(request RequestStruct) (settings SettingsStruct, err e
}
if cacheImages == true {
if cacheImages {
if Settings.EpgSource == "XEPG" && System.ImageCachingInProgress == 0 {
@@ -271,7 +267,7 @@ func updateServerSettings(request RequestStruct) (settings SettingsStruct, err e
}
if createXEPGFiles == true {
if createXEPGFiles {
go func() {
createXMLTVFile()
@@ -280,7 +276,7 @@ func updateServerSettings(request RequestStruct) (settings SettingsStruct, err e
}
if triggerPlexGuideReload == true {
if triggerPlexGuideReload {
queuePlexGuideRefresh("settings change")
}
@@ -378,7 +374,7 @@ func saveFiles(request RequestStruct, fileType string) (err error) {
return
}
if reloadData == true {
if reloadData {
err = buildDatabaseDVR()
if err != nil {
@@ -452,7 +448,6 @@ func deleteLocalProviderFiles(dataID, fileType string) {
os.RemoveAll(System.Folder.Data + dataID + fileExtension)
}
return
}
// Filtereinstellungen speichern (WebUI)
@@ -505,7 +500,7 @@ func saveFilter(request RequestStruct) (settings SettingsStruct, err error) {
if len(filter) == 0 {
err = errors.New(getErrMsg(1014))
if newFilter == true {
if newFilter {
delete(filterMap, dataID)
}
@@ -573,7 +568,7 @@ func saveXEpgMapping(request RequestStruct) (err error) {
// Wenn während des erstellen der Datanbank das Mapping erneut gespeichert wird, wird die Datenbank erst später erneut aktualisiert.
go func() {
if System.BackgroundProcess == true {
if System.BackgroundProcess {
return
}
@@ -591,7 +586,7 @@ func saveXEpgMapping(request RequestStruct) (err error) {
cleanupXEPG()
System.ScanInProgress = 0
buildXEPG(false)
showInfo("XEPG:" + fmt.Sprintf("Ready to use"))
showInfo("XEPG:" + "Ready to use")
System.BackgroundProcess = false
@@ -632,7 +627,7 @@ func saveUserData(request RequestStruct) (err error) {
return
}
if request.DeleteUser == true {
if request.DeleteUser {
err = authentication.RemoveUser(userID)
return
}
@@ -859,7 +854,7 @@ func buildDatabaseDVR() (err error) {
for n, i := range playlistFile {
var channels []any
var groupTitle, tvgID, uuid int = 0, 0, 0
var groupTitle, tvgID, uuid = 0, 0, 0
var keys = []string{"group-title", "tvg-id", "uuid"}
var compatibility = make(map[string]int)
@@ -905,11 +900,7 @@ func buildDatabaseDVR() (err error) {
if value, ok := s[key]; ok {
if len(value) > 0 {
if _, ok := tmpGroupsM3U[value]; ok {
tmpGroupsM3U[value]++
} else {
tmpGroupsM3U[value] = 1
}
tmpGroupsM3U[value]++
groupTitle++
}
@@ -985,7 +976,7 @@ func buildDatabaseDVR() (err error) {
for group, count := range tmpGroupsM3U {
var text = fmt.Sprintf("%s (%d)", group, count)
var value = fmt.Sprintf("%s", group)
var value = 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)
}
+1 -1
View File
@@ -154,7 +154,7 @@ func getLineup() (jsonContent []byte, err error) {
return
}
if xepgChannel.XActive == true {
if xepgChannel.XActive {
var stream LineupStream
stream.GuideName = xepgChannel.XName
stream.GuideNumber = xepgChannel.XChannelID
+16 -16
View File
@@ -1,30 +1,30 @@
package src
import (
b64 "encoding/base64"
"fmt"
"strings"
b64 "encoding/base64"
"fmt"
"strings"
)
func uploadLogo(input, filename string) (logoURL string, err error) {
b64data := input[strings.IndexByte(input, ',')+1:]
b64data := input[strings.IndexByte(input, ',')+1:]
// BAse64 in bytes umwandeln un speichern
sDec, err := b64.StdEncoding.DecodeString(b64data)
if err != nil {
return
}
// BAse64 in bytes umwandeln un speichern
sDec, err := b64.StdEncoding.DecodeString(b64data)
if err != nil {
return
}
var file = fmt.Sprintf("%s%s", System.Folder.ImagesUpload, filename)
var file = fmt.Sprintf("%s%s", System.Folder.ImagesUpload, filename)
err = writeByteToFile(file, sDec)
if err != nil {
return
}
err = writeByteToFile(file, sDec)
if err != nil {
return
}
logoURL = fmt.Sprintf("%s://%s/data_images/%s", System.ServerProtocol.XML, System.Domain, filename)
logoURL = fmt.Sprintf("%s://%s/data_images/%s", System.ServerProtocol.XML, System.Domain, filename)
return
return
}
+28 -32
View File
@@ -20,45 +20,41 @@ func ShowSystemInfo() {
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("Folder (config): %s", System.Folder.Config))
fmt.Printf("Version: %s %s.%s\n", System.Name, System.Version, System.Build)
fmt.Printf("Branch: %s\n", System.Branch)
fmt.Printf("Folder (config): %s\n", 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.Printf("Streams: %d / %d\n", len(Data.Streams.Active), len(Data.Streams.All))
fmt.Printf("Filter: %d\n", len(Data.Filter))
fmt.Printf("XEPG Chanels: %d\n", int(Data.XEPG.XEPGCount))
println()
fmt.Println(fmt.Sprintf("IPv4 Addresses:"))
fmt.Println("IPv4 Addresses:")
for i, ipv4 := range System.IPAddressesV4 {
switch count := i; {
case count < 10:
fmt.Println(fmt.Sprintf(" %d. %s", count, ipv4))
break
fmt.Printf(" %d. %s\n", count, ipv4)
case count < 100:
fmt.Println(fmt.Sprintf(" %d. %s", count, ipv4))
break
fmt.Printf(" %d. %s\n", count, ipv4)
}
}
println()
fmt.Println(fmt.Sprintf("IPv6 Addresses:"))
fmt.Println("IPv6 Addresses:")
for i, ipv4 := range System.IPAddressesV6 {
switch count := i; {
case count < 10:
fmt.Println(fmt.Sprintf(" %d. %s", count, ipv4))
break
fmt.Printf(" %d. %s\n", count, ipv4)
case count < 100:
fmt.Println(fmt.Sprintf(" %d. %s", count, ipv4))
break
fmt.Printf(" %d. %s\n", count, ipv4)
}
@@ -67,33 +63,33 @@ func ShowSystemInfo() {
println("---")
fmt.Println("Settings [General]")
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.Printf("UUID: %s\n", Settings.UUID)
fmt.Printf("Tuner (Plex / Emby): %d\n", Settings.Tuner)
fmt.Printf("EPG Source: %s\n", 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("Missing EPG Mode: %s", Settings.XepgMissingEPGMode))
fmt.Println(fmt.Sprintf("Replace EPG Image: %t", Settings.XepgReplaceMissingImages))
fmt.Printf("Schedule: %s\n", strings.Join(Settings.Update, ","))
fmt.Printf("Files Update: %t\n", Settings.FilesUpdate)
fmt.Printf("Folder (tmp): %s\n", Settings.TempPath)
fmt.Printf("Image Chaching: %t\n", Settings.CacheImages)
fmt.Printf("Missing EPG Mode: %s\n", Settings.XepgMissingEPGMode)
fmt.Printf("Replace EPG Image: %t\n", Settings.XepgReplaceMissingImages)
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.Printf("Buffer: %s\n", Settings.Buffer)
fmt.Printf("UDPxy: %s\n", Settings.UDPxy)
fmt.Printf("Buffer Size: %d KB\n", Settings.BufferSize)
fmt.Printf("Timeout: %d ms\n", int(Settings.BufferTimeout))
fmt.Printf("User Agent: %s\n", 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))
fmt.Printf("Folder (backup): %s\n", Settings.BackupPath)
fmt.Printf("Backup Keep: %d\n", Settings.BackupKeep)
}
+3 -96
View File
@@ -3,7 +3,6 @@ package authentication
import (
"encoding/json"
"errors"
"io/ioutil"
"net/http"
"os"
"path/filepath"
@@ -44,90 +43,6 @@ type Cookie struct {
RawExpires string
}
// Framework examples
/*
func main() {
var err error
var checkErr = func(err error) {
log.Println(err)
os.Exit(0)
}
err = Init("", 10) // Path to save the data, Validity of tokens in minutes | (error)
if err != nil {
checkErr(err)
}
err = CreateDefaultUser("admin", "123")
if err != nil {
checkErr(err)
}
err = CreateNewUser("xteve", "xteve") // Username, Password | (error)
if err != nil {
checkErr(err)
}
err, token := UserAuthentication("xteve", "xteve") // Username, Password | (error, token)
if err != nil {
checkErr(err)
} else {
fmt.Println("UserAuthentication()")
fmt.Println("Token:", token)
fmt.Println("---")
}
err, newToken := CheckTheValidityOfTheToken(token) // Current token | (error, new token)
if err != nil {
checkErr(err)
} else {
fmt.Println("CheckTheValidityOfTheToken()")
fmt.Println("New Token:", newToken)
fmt.Println("---")
}
err, userID := GetUserID(newToken) // Current token | (error, user id)
if err != nil {
checkErr(err)
} else {
fmt.Println("GetUserID()")
fmt.Println("User ID:", userID)
fmt.Println("---")
}
var userData = make(map[string]interface{})
userData["type"] = "Administrator"
err = WriteUserData(userID, userData) // User id, user data | (error)
if err != nil {
checkErr(err)
}
err, userData = ReadUserData(userID) // User id | (error, userData)
if err != nil {
checkErr(err)
} else {
fmt.Println("ReadUserData()")
fmt.Println("User data:", userData)
fmt.Println("---")
}
err = RemoveUser(userID)
if err != nil {
checkErr(err)
}
}
*/
// Init : databasePath = Path to authentication.json
func Init(databasePath string, validity int) (err error) {
database = filepath.Dir(databasePath) + string(os.PathSeparator) + databaseFile
@@ -457,7 +372,7 @@ func CheckTheValidityOfTheTokenFromHTTPHeader(w http.ResponseWriter, r *http.Req
// Framework tools
func checkInit() (err error) {
if initAuthentication == false {
if !initAuthentication {
err = createError(000)
}
@@ -472,7 +387,7 @@ func saveDatabase(tmpMap any) (err error) {
return
}
err = ioutil.WriteFile(database, []byte(jsonString), 0600)
err = os.WriteFile(database, []byte(jsonString), 0600)
if err != nil {
return
}
@@ -481,7 +396,7 @@ func saveDatabase(tmpMap any) (err error) {
}
func loadDatabase() (err error) {
jsonString, err := ioutil.ReadFile(database)
jsonString, err := os.ReadFile(database)
if err != nil {
return
}
@@ -587,14 +502,6 @@ loopToken:
return
}
func mapToJSON(tmpMap any) string {
jsonString, err := json.MarshalIndent(tmpMap, "", " ")
if err != nil {
return "{}"
}
return string(jsonString)
}
// SetCookieToken : set cookie
func SetCookieToken(w http.ResponseWriter, token string) http.ResponseWriter {
expiration := time.Now().Add(time.Minute * time.Duration(tokenValidity))
+121 -122
View File
@@ -1,178 +1,177 @@
package imgcache
import (
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"os"
"path/filepath"
"strings"
"sync"
"fmt"
"io"
"net/http"
"net/url"
"os"
"path/filepath"
"strings"
"sync"
)
// Cache : Cache strcut
type Cache struct {
path string
cacheURL string
caching bool
images map[string]string
Queue []string
Cache []string
Image imageFunc
sync.RWMutex
path string
cacheURL string
caching bool
images map[string]string
Queue []string
Cache []string
Image imageFunc
sync.RWMutex
}
type imageFunc struct {
GetURL func(string) string
Caching func()
Remove func()
GetURL func(string) string
Caching func()
Remove func()
}
// New : New cahce
func New(path, chacheURL string, caching bool) (c *Cache, err error) {
c = &Cache{}
c = &Cache{}
c.images = make(map[string]string)
c.path = path
c.cacheURL = chacheURL
c.caching = caching
c.Queue = []string{}
c.Cache = []string{}
c.images = make(map[string]string)
c.path = path
c.cacheURL = chacheURL
c.caching = caching
c.Queue = []string{}
c.Cache = []string{}
var queue []string
var queue []string
c.Image.GetURL = func(src string) (cacheURL string) {
c.Image.GetURL = func(src string) (cacheURL string) {
c.Lock()
defer c.Unlock()
c.Lock()
defer c.Unlock()
src = strings.Trim(src, "\r\n")
src = strings.Trim(src, "\r\n")
if c.caching == false {
return src
}
if !c.caching {
return src
}
u, err := url.Parse(src)
if err != nil || len(filepath.Ext(u.Path)) == 0 {
return src
}
u, err := url.Parse(src)
if err != nil || len(filepath.Ext(u.Path)) == 0 {
return src
}
var filename = fmt.Sprintf("%s%s", strToMD5(src), filepath.Ext(u.Path))
if cacheURL, ok := c.images[fmt.Sprintf("%s%s", strToMD5(src), filepath.Ext(u.Path))]; ok {
return cacheURL
}
var filename = fmt.Sprintf("%s%s", strToMD5(src), filepath.Ext(u.Path))
if cacheURL, ok := c.images[fmt.Sprintf("%s%s", strToMD5(src), filepath.Ext(u.Path))]; ok {
return cacheURL
}
if indexOfString(filename, c.Cache) == -1 {
if indexOfString(filename, c.Cache) == -1 {
if indexOfString(src, c.Queue) == -1 {
c.Queue = append(c.Queue, src)
}
if indexOfString(src, c.Queue) == -1 {
c.Queue = append(c.Queue, src)
}
} else {
c.images[filename] = c.cacheURL + filename
src = c.cacheURL + filename
}
} else {
c.images[filename] = c.cacheURL + filename
src = c.cacheURL + filename
}
/*
if _, err := os.Stat(c.path + filename); err != nil {
//c.images[filename] = c.cacheURL + filename
if indexOfString(src, c.Queue) == -1 {
c.Queue = append(c.Queue, src)
}
} else {
c.images[filename] = c.cacheURL + filename
}
*/
/*
if _, err := os.Stat(c.path + filename); err != nil {
//c.images[filename] = c.cacheURL + filename
if indexOfString(src, c.Queue) == -1 {
c.Queue = append(c.Queue, src)
}
} else {
c.images[filename] = c.cacheURL + filename
}
*/
return src
}
return src
}
c.Image.Caching = func() {
c.Image.Caching = func() {
c.Lock()
defer c.Unlock()
c.Lock()
defer c.Unlock()
var filename string
var filename string
for _, src := range c.Queue {
for _, src := range c.Queue {
resp, err := http.Get(src)
if err != nil {
continue
}
defer resp.Body.Close()
resp, err := http.Get(src)
if err != nil {
continue
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
continue
}
if resp.StatusCode != http.StatusOK {
continue
}
filename = fmt.Sprintf("%s%s%s%s", c.path, string(os.PathSeparator), strToMD5(src), filepath.Ext(src))
filename = fmt.Sprintf("%s%s%s%s", c.path, string(os.PathSeparator), strToMD5(src), filepath.Ext(src))
file, err := os.Create(filename)
if err != nil {
continue
}
file, err := os.Create(filename)
if err != nil {
continue
}
defer file.Close()
defer file.Close()
_, err = io.Copy(file, resp.Body)
if err != nil {
continue
}
_, err = io.Copy(file, resp.Body)
if err != nil {
continue
}
u, err := url.Parse(src)
if err == nil {
c.images[fmt.Sprintf("%s%s", strToMD5(src), filepath.Ext(u.Path))] = c.cacheURL + filename
}
u, err := url.Parse(src)
if err == nil {
c.images[fmt.Sprintf("%s%s", strToMD5(src), filepath.Ext(u.Path))] = c.cacheURL + filename
}
queue = append(queue, src)
queue = append(queue, src)
}
}
for _, q := range queue {
c.Queue = removeStringFromSlice(q, c.Queue)
}
for _, q := range queue {
c.Queue = removeStringFromSlice(q, c.Queue)
}
}
}
c.Image.Remove = func() {
c.Image.Remove = func() {
c.Lock()
defer c.Unlock()
c.Lock()
defer c.Unlock()
files, err := ioutil.ReadDir(c.path)
if err != nil {
return
}
files, err := os.ReadDir(c.path)
if err != nil {
return
}
for _, file := range files {
for _, file := range files {
switch c.caching {
switch c.caching {
case true:
if _, ok := c.images[file.Name()]; !ok {
os.RemoveAll(c.path + file.Name())
}
case true:
if _, ok := c.images[file.Name()]; !ok {
os.RemoveAll(c.path + file.Name())
}
case false:
os.RemoveAll(c.path + file.Name())
}
case false:
os.RemoveAll(c.path + file.Name())
}
}
}
}
}
files, err := ioutil.ReadDir(c.path)
if err != nil {
return
}
files, err := os.ReadDir(c.path)
if err != nil {
return
}
for _, file := range files {
c.Cache = append(c.Cache, file.Name())
}
for _, file := range files {
c.Cache = append(c.Cache, file.Name())
}
return
return
}
+16 -16
View File
@@ -1,34 +1,34 @@
package imgcache
import (
"crypto/md5"
"encoding/hex"
"crypto/md5"
"encoding/hex"
)
func strToMD5(str string) string {
md5Hasher := md5.New()
md5Hasher.Write([]byte(str))
return hex.EncodeToString(md5Hasher.Sum(nil))
md5Hasher := md5.New()
md5Hasher.Write([]byte(str))
return hex.EncodeToString(md5Hasher.Sum(nil))
}
func indexOfString(str string, slice []string) int {
for i, v := range slice {
if str == v {
return i
}
}
for i, v := range slice {
if str == v {
return i
}
}
return -1
return -1
}
func removeStringFromSlice(str string, slice []string) []string {
var i = indexOfString(str, slice)
var i = indexOfString(str, slice)
if i != -1 {
slice = append(slice[:i], slice[i+1:]...)
}
if i != -1 {
slice = append(slice[:i], slice[i+1:]...)
}
return slice
return slice
}
+19 -19
View File
@@ -3,18 +3,18 @@ package m3u
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"os"
"testing"
)
type M3UStream struct {
GroupTitle string `json:"group-title,required"`
Name string `json:"name,required"`
TvgID string `json:"tvg-id,required"`
TvgLogo string `json:"tvg-logo,required"`
TvgName string `json:"tvg-name,required"`
URL string `json:"url,required"`
GroupTitle string `json:"group-title"`
Name string `json:"name"`
TvgID string `json:"tvg-id"`
TvgLogo string `json:"tvg-logo"`
TvgName string `json:"tvg-name"`
URL string `json:"url"`
UUIDKey string `json:"_uuid.key,omitempty"`
UUIDValue string `json:"_uuid.value,omitempty"`
}
@@ -22,7 +22,7 @@ type M3UStream struct {
func TestStream1(t *testing.T) {
var file = "test_list_1.m3u"
var content, err = ioutil.ReadFile(file)
var content, err = os.ReadFile(file)
if err != nil {
t.Error(err)
return
@@ -58,26 +58,26 @@ func checkStream(streamInterface []any) (err error) {
err = json.Unmarshal(jsonString, &m3uStream)
if err == nil {
log.Print(fmt.Sprintf("Stream: %d", i))
log.Print(fmt.Sprintf("Name*: %s", m3uStream.Name))
log.Print(fmt.Sprintf("URL*: %s", m3uStream.URL))
log.Print(fmt.Sprintf("tvg-name: %s", m3uStream.TvgName))
log.Print(fmt.Sprintf("tvg-id**: %s", m3uStream.TvgID))
log.Print(fmt.Sprintf("tvg-logo: %s", m3uStream.TvgLogo))
log.Print(fmt.Sprintf("group-title**: %s", m3uStream.GroupTitle))
log.Printf("Stream: %d", i)
log.Printf("Name*: %s", m3uStream.Name)
log.Printf("URL*: %s", m3uStream.URL)
log.Printf("tvg-name: %s", m3uStream.TvgName)
log.Printf("tvg-id**: %s", m3uStream.TvgID)
log.Printf("tvg-logo: %s", m3uStream.TvgLogo)
log.Printf("group-title**: %s", m3uStream.GroupTitle)
if len(m3uStream.UUIDKey) > 0 {
log.Print(fmt.Sprintf("UUID key***: %s", m3uStream.UUIDKey))
log.Print(fmt.Sprintf("UUID value: %s", m3uStream.UUIDValue))
log.Printf("UUID key***: %s", m3uStream.UUIDKey)
log.Printf("UUID value: %s", m3uStream.UUIDValue)
} else {
log.Print(fmt.Sprintf("UUID key: false"))
log.Print("UUID key: false")
}
}
}
log.Println(fmt.Sprintf("- - - - - (*: Required) | (**: Nice to have) | (***: Love it) - - - - -"))
log.Println("- - - - - (*: Required) | (**: Nice to have) | (***: Love it) - - - - -")
}
return
+1 -2
View File
@@ -2,7 +2,6 @@ package m3u
import (
"errors"
"fmt"
"log"
"net/url"
"regexp"
@@ -124,7 +123,7 @@ func MakeInterfaceFromM3U(byteStream []byte) (allChannels []any, err error) {
if strings.Contains(strings.ToLower(key), "id") {
if indexOfString(value, uuids) != -1 {
log.Println(fmt.Sprintf("Channel: %s - %s = %s ", stream["name"], key, value))
log.Printf("Channel: %s - %s = %s ", stream["name"], key, value)
break
}
+4 -4
View File
@@ -114,18 +114,18 @@ func filterThisStream(s any) (status bool) {
}
}
if match == true {
if match {
if len(exclude) > 0 {
var status = checkConditions(search, exclude, "exclude")
if status == false {
if !status {
return false
}
}
if len(include) > 0 {
var status = checkConditions(search, include, "include")
if status == false {
if !status {
return false
}
}
@@ -191,7 +191,7 @@ func buildM3U(groups []string) (m3u string, err error) {
err := json.Unmarshal([]byte(mapToJSON(dxc)), &xepgChannel)
if err == nil {
if xepgChannel.XActive == true {
if xepgChannel.XActive {
if len(groups) > 0 {
+1 -9
View File
@@ -1,15 +1,12 @@
package src
import (
"math/rand"
"time"
)
// InitMaintenance : Wartungsprozess initialisieren
func InitMaintenance() (err error) {
rand.Seed(time.Now().Unix())
go maintenance()
return
@@ -50,7 +47,7 @@ func maintenance() {
ShowError(err, 000)
}
if Settings.CacheImages == false && System.ImageCachingInProgress == 0 {
if !Settings.CacheImages && System.ImageCachingInProgress == 0 {
removeChildItems(System.Folder.ImagesCache)
}
@@ -68,8 +65,3 @@ func maintenance() {
}
}
func randomTime(min, max int) int {
rand.Seed(time.Now().Unix())
return rand.Intn(max-min) + min
}
+3
View File
@@ -131,6 +131,9 @@ func convertToNewFilter(oldFilter []any) (newFilterMap map[int]any) {
func setValueForUUID() (err error) {
xepg, err := loadJSONFileToMap(System.File.XEPG)
if err != nil {
return
}
for _, c := range xepg {
+18 -18
View File
@@ -4,7 +4,7 @@ import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"io"
"net/http"
"net/url"
"strconv"
@@ -26,7 +26,7 @@ var plexRefreshState = struct {
// queuePlexGuideRefresh schedules a debounced Plex DVR guide refresh.
func queuePlexGuideRefresh(reason string) {
if Settings.UsePlexAPI == false {
if !Settings.UsePlexAPI {
return
}
@@ -36,7 +36,7 @@ func queuePlexGuideRefresh(reason string) {
plexRefreshState.reason = reason
}
if plexRefreshState.scheduled == true || plexRefreshState.inProgress == true {
if plexRefreshState.scheduled || plexRefreshState.inProgress {
plexRefreshState.queued = true
plexRefreshState.Unlock()
return
@@ -78,13 +78,13 @@ func runPlexGuideRefresh(initialDelay time.Duration) {
plexRefreshState.inProgress = false
nextDelay := time.Duration(0)
if runAgain == true && Settings.UsePlexAPI == true {
if runAgain && Settings.UsePlexAPI {
plexRefreshState.scheduled = true
nextDelay = plexRefreshDelayLocked()
}
plexRefreshState.Unlock()
if runAgain == false || Settings.UsePlexAPI == false {
if !runAgain || !Settings.UsePlexAPI {
return
}
@@ -98,7 +98,7 @@ func plexRefreshDelayLocked() (delay time.Duration) {
const minDelay = 2 * time.Second
const cooldown = 20 * time.Second
if plexRefreshState.lastRun.IsZero() == true {
if plexRefreshState.lastRun.IsZero() {
return minDelay
}
@@ -113,7 +113,7 @@ func plexRefreshDelayLocked() (delay time.Duration) {
func refreshPlexGuide(reason string) (err error) {
baseURL, token, ready := getPlexConfig()
if ready == false {
if !ready {
return nil
}
@@ -178,7 +178,7 @@ func getPlexConfig() (baseURL, token string, ready bool) {
baseURL = strings.TrimSpace(Settings.PlexURL)
token = strings.TrimSpace(Settings.PlexToken)
if strings.HasPrefix(baseURL, "http://") == false && strings.HasPrefix(baseURL, "https://") == false && len(baseURL) > 0 {
if !strings.HasPrefix(baseURL, "http://") && !strings.HasPrefix(baseURL, "https://") && len(baseURL) > 0 {
baseURL = "http://" + baseURL
}
@@ -189,7 +189,7 @@ func getPlexConfig() (baseURL, token string, ready bool) {
if len(baseURL) == 0 || len(token) == 0 {
if plexRefreshState.missingConfigLogged == false {
if !plexRefreshState.missingConfigLogged {
showInfo("Plex API:Skipped refresh because plex.url or plex.token is empty")
plexRefreshState.missingConfigLogged = true
}
@@ -221,14 +221,14 @@ func discoverPlexDVRs(baseURL, token string) (dvrs []map[string]any, err error)
}
mediaContainer, ok := payload["MediaContainer"].(map[string]any)
if ok == false {
if !ok {
err = errors.New("Plex API response missing MediaContainer")
return
}
for _, key := range []string{"Dvr", "DVR", "Directory", "Metadata"} {
if raw, found := mediaContainer[key]; found == true {
if list, ok := raw.([]any); ok == true {
if raw, found := mediaContainer[key]; found {
if list, ok := raw.([]any); ok {
dvrs = convertToPlexMapSlice(list)
if len(dvrs) > 0 {
return
@@ -246,7 +246,7 @@ func convertToPlexMapSlice(list []any) (dvrs []map[string]any) {
dvrs = make([]map[string]any, 0, len(list))
for _, item := range list {
if dvr, ok := item.(map[string]any); ok == true {
if dvr, ok := item.(map[string]any); ok {
dvrs = append(dvrs, dvr)
}
}
@@ -264,10 +264,10 @@ func getPlexReloadEndpoints(dvr map[string]any) (endpoints []string) {
if len(endpoint) == 0 {
return
}
if strings.HasPrefix(endpoint, "/") == false {
if !strings.HasPrefix(endpoint, "/") {
endpoint = "/" + endpoint
}
if added[endpoint] == true {
if added[endpoint] {
return
}
added[endpoint] = true
@@ -275,7 +275,7 @@ func getPlexReloadEndpoints(dvr map[string]any) (endpoints []string) {
}
if key := getPlexMapString(dvr, "key"); len(key) > 0 {
if strings.HasPrefix(key, "/") == true {
if strings.HasPrefix(key, "/") {
add(strings.TrimRight(key, "/") + "/reloadGuide")
} else {
add("/livetv/dvrs/" + url.PathEscape(key) + "/reloadGuide")
@@ -294,7 +294,7 @@ func getPlexReloadEndpoints(dvr map[string]any) (endpoints []string) {
func getPlexMapString(data map[string]any, field string) string {
for key, value := range data {
if strings.EqualFold(key, field) == false {
if !strings.EqualFold(key, field) {
continue
}
@@ -354,7 +354,7 @@ func doPlexRequest(method, baseURL, endpoint, token string) (status int, body []
defer resp.Body.Close()
status = resp.StatusCode
body, err = ioutil.ReadAll(resp.Body)
body, err = io.ReadAll(resp.Body)
if err != nil {
return
}
+5 -5
View File
@@ -2,7 +2,7 @@ package src
import (
"fmt"
"io/ioutil"
"io"
"net/http"
"net/url"
"strings"
@@ -152,7 +152,7 @@ func getProviderData(fileType, fileID string) (err error) {
}
// Wenn eine ID vorhanden ist und nicht mit der aus der Datanbank übereinstimmt, wird die Aktualisierung übersprungen (goto)
if len(fileID) > 0 && newProvider == false {
if len(fileID) > 0 && !newProvider {
if dataID != fileID {
goto Done
}
@@ -204,7 +204,7 @@ func getProviderData(fileType, fileID string) (err error) {
ShowError(err, 000)
var downloadErr = err
if newProvider == false {
if !newProvider {
// Prüfen ob ältere Datei vorhanden ist
var file = System.Folder.Data + dataID + fileExtension
@@ -236,7 +236,7 @@ func getProviderData(fileType, fileID string) (err error) {
}
// Berechnen der Fehlerquote
if newProvider == false {
if !newProvider {
if value, ok := dataMap[dataID].(map[string]any); ok {
@@ -322,7 +322,7 @@ func downloadFileFromServer(providerURL string) (filename string, body []byte, e
}
body, err = ioutil.ReadAll(resp.Body)
body, err = io.ReadAll(resp.Body)
if err != nil {
return
}
+58 -64
View File
@@ -12,7 +12,7 @@ import (
func showInfo(str string) {
if System.Flag.Info == true {
if System.Flag.Info {
return
}
@@ -39,7 +39,6 @@ func showInfo(str string) {
}
return
}
func showDebug(str string, level int) {
@@ -73,7 +72,6 @@ func showDebug(str string, level int) {
}
return
}
func showHighlight(str string) {
@@ -105,7 +103,6 @@ func showHighlight(str string) {
addNotification(notification)
return
}
func showWarning(errCode int) {
@@ -121,7 +118,6 @@ func showWarning(errCode int) {
WebScreenLog.Warnings++
mutex.Unlock()
return
}
// ShowError : Zeigt die Fehlermeldungen in der Konsole
@@ -139,7 +135,6 @@ func ShowError(err error, errCode int) {
WebScreenLog.Errors++
mutex.Unlock()
return
}
func printLogOnScreen(logMsg string, logType string) {
@@ -211,7 +206,6 @@ func logCleanUp() {
WebScreenLog.Log = logs
return
}
// Fehlercodes
@@ -224,79 +218,79 @@ func getErrMsg(errCode int) (errMsg string) {
// Errors
case 1001:
errMsg = fmt.Sprintf("Web server could not be started.")
errMsg = "Web server could not be started."
case 1002:
errMsg = fmt.Sprintf("No local IP address found.")
errMsg = "No local IP address found."
case 1003:
errMsg = fmt.Sprintf("Invalid xml")
errMsg = "Invalid xml"
case 1004:
errMsg = fmt.Sprintf("File not found")
errMsg = "File not found"
case 1005:
errMsg = fmt.Sprintf("Invalid M3U file, an extended M3U file is required.")
errMsg = "Invalid M3U file, an extended M3U file is required."
case 1006:
errMsg = fmt.Sprintf("No playlist!")
errMsg = "No playlist!"
case 1007:
errMsg = fmt.Sprintf("XEPG requires an XMLTV file.")
errMsg = "XEPG requires an XMLTV file."
case 1010:
errMsg = fmt.Sprintf("Invalid file compression")
errMsg = "Invalid file compression"
case 1011:
errMsg = fmt.Sprintf("Data is corrupt or unavailable, %s now uses an older version of this file", System.Name)
case 1012:
errMsg = fmt.Sprintf("Invalid formatting of the time")
errMsg = "Invalid formatting of the time"
case 1013:
errMsg = fmt.Sprintf("Invalid settings file (settings.json), file must be at least version %s", System.Compatibility)
case 1014:
errMsg = fmt.Sprintf("Invalid filter rule")
errMsg = "Invalid filter rule"
case 1020:
errMsg = fmt.Sprintf("Data could not be saved, invalid keyword")
errMsg = "Data could not be saved, invalid keyword"
// Datenbank Update
case 1030:
errMsg = fmt.Sprintf("Invalid settings file (%s)", System.File.Settings)
case 1031:
errMsg = fmt.Sprintf("Database error. The database version of your settings is not compatible with this version.")
errMsg = "Database error. The database version of your settings is not compatible with this version."
// M3U Parser
case 1050:
errMsg = fmt.Sprintf("Invalid duration specification in the M3U8 playlist.")
errMsg = "Invalid duration specification in the M3U8 playlist."
case 1060:
errMsg = fmt.Sprintf("Invalid characters found in the tvg parameters, streams with invalid parameters were skipped.")
errMsg = "Invalid characters found in the tvg parameters, streams with invalid parameters were skipped."
// Dateisystem
case 1070:
errMsg = fmt.Sprintf("Folder could not be created.")
errMsg = "Folder could not be created."
case 1071:
errMsg = fmt.Sprintf("File could not be created")
errMsg = "File could not be created"
case 1072:
errMsg = fmt.Sprintf("File not found")
errMsg = "File not found"
// Backup
case 1090:
errMsg = fmt.Sprintf("Automatic backup failed")
errMsg = "Automatic backup failed"
// Websockets
case 1100:
errMsg = fmt.Sprintf("WebUI build error")
errMsg = "WebUI build error"
case 1101:
errMsg = fmt.Sprintf("WebUI request error")
errMsg = "WebUI request error"
case 1102:
errMsg = fmt.Sprintf("WebUI response error")
errMsg = "WebUI response error"
// PMS Guide Numbers
case 1200:
errMsg = fmt.Sprintf("Could not create file")
errMsg = "Could not create file"
// Stream URL Fehler
case 1201:
errMsg = fmt.Sprintf("Plex stream error")
errMsg = "Plex stream error"
case 1202:
errMsg = fmt.Sprintf("Steaming URL could not be found in any playlist")
errMsg = "Steaming URL could not be found in any playlist"
case 1203:
errMsg = fmt.Sprintf("Steaming URL could not be found in any playlist")
errMsg = "Steaming URL could not be found in any playlist"
case 1204:
errMsg = fmt.Sprintf("Streaming was stopped by third party transcoder (FFmpeg / VLC)")
errMsg = "Streaming was stopped by third party transcoder (FFmpeg / VLC)"
// Warnings
case 2000:
@@ -304,86 +298,86 @@ func getErrMsg(errCode int) (errMsg string) {
case 2001:
errMsg = fmt.Sprintf("%s has loaded more than %d streams. Use the filter to reduce the number of streams.", System.Name, System.UnfilteredChannelLimit)
case 2002:
errMsg = fmt.Sprintf("PMS can not play m3u8 streams")
errMsg = "PMS can not play m3u8 streams"
case 2003:
errMsg = fmt.Sprintf("PMS can not play streams over RTSP.")
errMsg = "PMS can not play streams over RTSP."
case 2004:
errMsg = fmt.Sprintf("Buffer is disabled for this stream.")
errMsg = "Buffer is disabled for this stream."
case 2005:
errMsg = fmt.Sprintf("There are no channels mapped, use the mapping menu to assign EPG data to the channels.")
errMsg = "There are no channels mapped, use the mapping menu to assign EPG data to the channels."
case 2010:
errMsg = fmt.Sprintf("No valid streaming URL")
errMsg = "No valid streaming URL"
case 2020:
errMsg = fmt.Sprintf("FFmpeg binary was not found. Check the FFmpeg binary path in the xTeVe settings.")
errMsg = "FFmpeg binary was not found. Check the FFmpeg binary path in the xTeVe settings."
case 2021:
errMsg = fmt.Sprintf("VLC binary was not found. Check the VLC path binary in the xTeVe settings.")
errMsg = "VLC binary was not found. Check the VLC path binary in the xTeVe settings."
case 2099:
errMsg = fmt.Sprintf("Updates have been disabled by the developer")
errMsg = "Updates have been disabled by the developer"
// Tuner
case 2105:
errMsg = "The number of tuners has changed, you have to delete " + System.Name + " in Plex / Emby HDHR and set it up again."
case 2106:
errMsg = fmt.Sprintf("This function is only available with XEPG as EPG source")
errMsg = "This function is only available with XEPG as EPG source"
case 2110:
errMsg = fmt.Sprintf("Don't run this as Root!")
errMsg = "Don't run this as Root!"
case 2300:
errMsg = fmt.Sprintf("No channel logo found in the XMLTV or M3U file.")
errMsg = "No channel logo found in the XMLTV or M3U file."
case 2301:
errMsg = fmt.Sprintf("XMLTV file no longer available, channel has been deactivated.")
errMsg = "XMLTV file no longer available, channel has been deactivated."
case 2302:
errMsg = fmt.Sprintf("Channel ID in the XMLTV file has changed. Channel has been deactivated.")
errMsg = "Channel ID in the XMLTV file has changed. Channel has been deactivated."
// Benutzerauthentifizierung
case 3000:
errMsg = fmt.Sprintf("Database for user authentication could not be initialized.")
errMsg = "Database for user authentication could not be initialized."
case 3001:
errMsg = fmt.Sprintf("The user has no authorization to load the channels.")
errMsg = "The user has no authorization to load the channels."
// Buffer
case 4000:
errMsg = fmt.Sprintf("Connection to streaming source was interrupted.")
errMsg = "Connection to streaming source was interrupted."
case 4001:
errMsg = fmt.Sprintf("Too many errors connecting to the provider. Streaming is canceled.")
errMsg = "Too many errors connecting to the provider. Streaming is canceled."
case 4002:
errMsg = fmt.Sprintf("New URL for the redirect to the streaming server is missing")
errMsg = "New URL for the redirect to the streaming server is missing"
case 4003:
errMsg = fmt.Sprintf("Server sends an incompatible content-type")
errMsg = "Server sends an incompatible content-type"
case 4004:
errMsg = fmt.Sprintf("This error message comes from the provider")
errMsg = "This error message comes from the provider"
case 4005:
errMsg = fmt.Sprintf("Temporary buffer files could not be deleted")
errMsg = "Temporary buffer files could not be deleted"
case 4006:
errMsg = fmt.Sprintf("Server connection timeout")
errMsg = "Server connection timeout"
// Buffer (M3U8)
case 4050:
errMsg = fmt.Sprintf("Invalid M3U8 file")
errMsg = "Invalid M3U8 file"
case 4051:
errMsg = fmt.Sprintf("#EXTM3U header is missing")
errMsg = "#EXTM3U header is missing"
// Caching
case 4100:
errMsg = fmt.Sprintf("Unknown content type for downloaded image")
errMsg = "Unknown content type for downloaded image"
case 4101:
errMsg = fmt.Sprintf("Invalid URL, original URL is used for this image")
errMsg = "Invalid URL, original URL is used for this image"
// API
case 5000:
errMsg = fmt.Sprintf("Invalid API command")
errMsg = "Invalid API command"
// Update Server
case 6001:
errMsg = fmt.Sprintf("Ivalid key")
errMsg = "Ivalid key"
case 6002:
errMsg = fmt.Sprintf("Update failed")
errMsg = "Update failed"
case 6003:
errMsg = fmt.Sprintf("Update server not available")
errMsg = "Update server not available"
case 6004:
errMsg = fmt.Sprintf("xTeVe update available")
errMsg = "xTeVe update available"
default:
errMsg = fmt.Sprintf("Unknown error / warning (%d)", errCode)
+47 -47
View File
@@ -1,72 +1,72 @@
package src
import (
"fmt"
"log"
"os"
"os/signal"
"time"
"fmt"
"log"
"os"
"os/signal"
"time"
"github.com/koron/go-ssdp"
"github.com/koron/go-ssdp"
)
// SSDP : SSPD / DLNA Server
func SSDP() (err error) {
if Settings.SSDP == false || System.Flag.Info == true {
return
}
if !Settings.SSDP || System.Flag.Info {
return
}
showInfo(fmt.Sprintf("SSDP / DLNA:%t", Settings.SSDP))
showInfo(fmt.Sprintf("SSDP / DLNA:%t", Settings.SSDP))
quit := make(chan os.Signal, 1)
signal.Notify(quit, os.Interrupt)
quit := make(chan os.Signal, 1)
signal.Notify(quit, os.Interrupt)
ad, err := ssdp.Advertise(
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"
ad, err := ssdp.Advertise(
"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 {
return
}
if err != nil {
return
}
// Debug SSDP
if System.Flag.Debug == 3 {
ssdp.Logger = log.New(os.Stderr, "[SSDP] ", log.LstdFlags)
}
// Debug SSDP
if System.Flag.Debug == 3 {
ssdp.Logger = log.New(os.Stderr, "[SSDP] ", log.LstdFlags)
}
go func(adv *ssdp.Advertiser) {
go func(adv *ssdp.Advertiser) {
aliveTick := time.Tick(300 * time.Second)
aliveTick := time.Tick(300 * time.Second)
loop:
for {
loop:
for {
select {
select {
case <-aliveTick:
err = adv.Alive()
if err != nil {
ShowError(err, 0)
adv.Bye()
adv.Close()
break loop
}
case <-aliveTick:
err = adv.Alive()
if err != nil {
ShowError(err, 0)
adv.Bye()
adv.Close()
break loop
}
case <-quit:
adv.Bye()
adv.Close()
os.Exit(0)
break loop
case <-quit:
adv.Bye()
adv.Close()
os.Exit(0)
break loop
}
}
}
}
}(ad)
}(ad)
return
return
}
-144
View File
@@ -108,147 +108,3 @@ type BandwidthCalculation struct {
Stop time.Time
TimeDiff float64
}
/*
var args = "-hide_banner -loglevel panic -re -i " + url + " -codec copy -f mpegts pipe:1"
//var args = "-re -i " + url + " -codec copy -f mpegts pipe:1"
cmd := exec.Command("/usr/local/bin/ffmpeg", strings.Split(args, " ")...)
//run := exec.Command("/usr/local/bin/ffmpeg", "-hide_banner", "-loglevel", "panic", "-re", "-i", url, "-codec", "copy", "-f", "mpegts", "pipe:1")
//run := exec.Command("/usr/local/bin/ffmpeg", "-re", "-i", url, "-codec", "copy", "-f", "mpegts", "pipe:1")
stderr, _ := cmd.StderrPipe()
cmd.Start()
scanner := bufio.NewScanner(stderr)
scanner.Split(bufio.ScanLines)
for scanner.Scan() {
m := scanner.Text()
fmt.Println(m)
}
cmd.Wait()
os.Exit(0)
*/
/*
ffmpegOut, _ := run.StderrPipe()
//run.Start()
scanner = bufio.NewScanner(ffmpegOut)
scanner.Split(bufio.ScanLines)
for scanner.Scan() {
m := scanner.Text()
fmt.Println(m)
}
ffmpegOut, err = run.StdoutPipe()
if err != nil {
ShowError(err, 0)
return
}
stderr, stderrErr := run.StderrPipe()
if stderrErr != nil {
fmt.Println(stderrErr)
}
_ = stderr
if startErr := run.Start(); startErr != nil {
fmt.Println(startErr)
return
}
n, err := ffmpegOut.Read(buffer)
_ = n
_ = stream
_ = fileSize
if err != nil && err != io.EOF {
ShowError(err, 0)
addErrorToStream(err)
return
}
defer bufferFile.Close()
scanner = bufio.NewScanner(ffmpegOut)
for scanner.Scan() {
//fmt.Printf("%s\n", scanner.Text())
//fmt.Println(scanner)
thisLine := scanner.Text()
line := make([]byte, len(thisLine))
buffer = append(buffer, line...)
fmt.Println(len(buffer))
if len(buffer) > tmpFileSize {
if _, err := bufferFile.Write(buffer[:]); err != nil {
ShowError(err, 0)
addErrorToStream(err)
run.Process.Kill()
return
}
buffer = make([]byte, 1024*Settings.BufferSize*2)
debug = fmt.Sprintf("Buffer Status:Done (%s)", tmpFile)
showDebug(debug, 2)
bufferFile.Close()
stream.Status = true
playlist.Streams[streamID] = stream
BufferInformation.Store(playlistID, playlist)
tmpSegment++
tmpFile = fmt.Sprintf("%s%d.ts", tmpFolder, tmpSegment)
if clientConnection(stream) == false {
bufferFile.Close()
run.Process.Kill()
err = os.RemoveAll(stream.Folder)
if err != nil {
ShowError(err, 4005)
}
return
}
bufferFile, err = os.Create(tmpFile)
if err != nil {
addErrorToStream(err)
run.Process.Kill()
return
}
fileSize = 0
if n == 0 {
bufferFile.Close()
run.Process.Kill()
break
}
os.Exit(0)
}
}
*/
+53 -54
View File
@@ -155,81 +155,81 @@ type Filter struct {
// XEPGChannelStruct : XEPG Struktur
type XEPGChannelStruct struct {
FileM3UID string `json:"_file.m3u.id,required"`
FileM3UName string `json:"_file.m3u.name,required"`
FileM3UPath string `json:"_file.m3u.path,required"`
GroupTitle string `json:"group-title,required"`
Name string `json:"name,required"`
TvgID string `json:"tvg-id,required"`
TvgLogo string `json:"tvg-logo,required"`
TvgName string `json:"tvg-name,required"`
URL string `json:"url,required"`
UUIDKey string `json:"_uuid.key,required"`
FileM3UID string `json:"_file.m3u.id"`
FileM3UName string `json:"_file.m3u.name"`
FileM3UPath string `json:"_file.m3u.path"`
GroupTitle string `json:"group-title"`
Name string `json:"name"`
TvgID string `json:"tvg-id"`
TvgLogo string `json:"tvg-logo"`
TvgName string `json:"tvg-name"`
URL string `json:"url"`
UUIDKey string `json:"_uuid.key"`
UUIDValue string `json:"_uuid.value,omitempty"`
Values string `json:"_values,required"`
XActive bool `json:"x-active,required"`
XCategory string `json:"x-category,required"`
XChannelID string `json:"x-channelID,required"`
XEPG string `json:"x-epg,required"`
XGroupTitle string `json:"x-group-title,required"`
XMapping string `json:"x-mapping,required"`
XmltvFile string `json:"x-xmltv-file,required"`
XName string `json:"x-name,required"`
XUpdateChannelIcon bool `json:"x-update-channel-icon,required"`
XUpdateChannelName bool `json:"x-update-channel-name,required"`
XDescription string `json:"x-description,required"`
Values string `json:"_values"`
XActive bool `json:"x-active"`
XCategory string `json:"x-category"`
XChannelID string `json:"x-channelID"`
XEPG string `json:"x-epg"`
XGroupTitle string `json:"x-group-title"`
XMapping string `json:"x-mapping"`
XmltvFile string `json:"x-xmltv-file"`
XName string `json:"x-name"`
XUpdateChannelIcon bool `json:"x-update-channel-icon"`
XUpdateChannelName bool `json:"x-update-channel-name"`
XDescription string `json:"x-description"`
}
// M3UChannelStructXEPG : M3U Struktur für XEPG
type M3UChannelStructXEPG struct {
FileM3UID string `json:"_file.m3u.id,required"`
FileM3UName string `json:"_file.m3u.name,required"`
FileM3UPath string `json:"_file.m3u.path,required"`
GroupTitle string `json:"group-title,required"`
Name string `json:"name,required"`
TvgID string `json:"tvg-id,required"`
TvgLogo string `json:"tvg-logo,required"`
TvgName string `json:"tvg-name,required"`
URL string `json:"url,required"`
UUIDKey string `json:"_uuid.key,required"`
UUIDValue string `json:"_uuid.value,required"`
Values string `json:"_values,required"`
FileM3UID string `json:"_file.m3u.id"`
FileM3UName string `json:"_file.m3u.name"`
FileM3UPath string `json:"_file.m3u.path"`
GroupTitle string `json:"group-title"`
Name string `json:"name"`
TvgID string `json:"tvg-id"`
TvgLogo string `json:"tvg-logo"`
TvgName string `json:"tvg-name"`
URL string `json:"url"`
UUIDKey string `json:"_uuid.key"`
UUIDValue string `json:"_uuid.value"`
Values string `json:"_values"`
}
// FilterStruct : Filter Struktur
type FilterStruct struct {
Active bool `json:"active,required"`
CaseSensitive bool `json:"caseSensitive,required"`
Description string `json:"description,required"`
Exclude string `json:"exclude,required"`
Filter string `json:"filter,required"`
Include string `json:"include,required"`
Name string `json:"name,required"`
Active bool `json:"active"`
CaseSensitive bool `json:"caseSensitive"`
Description string `json:"description"`
Exclude string `json:"exclude"`
Filter string `json:"filter"`
Include string `json:"include"`
Name string `json:"name"`
Rule string `json:"rule,omitempty"`
Type string `json:"type,required"`
Type string `json:"type"`
}
// StreamingURLS : Informationen zu allen streaming URL's
type StreamingURLS struct {
Streams map[string]StreamInfo `json:"channels,required"`
Streams map[string]StreamInfo `json:"channels"`
}
// StreamInfo : Informationen zum Kanal für die streaming URL
type StreamInfo struct {
ChannelNumber string `json:"channelNumber,required"`
Name string `json:"name,required"`
PlaylistID string `json:"playlistID,required"`
URL string `json:"url,required"`
URLid string `json:"urlID,required"`
ChannelNumber string `json:"channelNumber"`
Name string `json:"name"`
PlaylistID string `json:"playlistID"`
URL string `json:"url"`
URLid string `json:"urlID"`
}
// Notification : Notifikationen im Webinterface
type Notification struct {
Headline string `json:"headline,required"`
Message string `json:"message,required"`
New bool `json:"new,required"`
Time string `json:"time,required"`
Type string `json:"type,required"`
Headline string `json:"headline"`
Message string `json:"message"`
New bool `json:"new"`
Time string `json:"time"`
Type string `json:"type"`
}
// SettingsStruct : Inhalt der settings.json
@@ -283,4 +283,3 @@ type SettingsStruct struct {
XepgReplaceMissingImages bool `json:"xepg.replace.missing.images"`
WizardCompleted bool `json:"wizard.completed"`
}
+20 -20
View File
@@ -3,7 +3,7 @@ package src
// RequestStruct : Anfragen über die Websocket Schnittstelle
type RequestStruct struct {
// Befehle an xTeVe
Cmd string `json:"cmd,required"`
Cmd string `json:"cmd"`
// Benutzer
DeleteUser bool `json:"deleteUser,omitempty"`
@@ -79,46 +79,46 @@ type ResponseStruct struct {
DVR string `json:"DVR"`
EpgSource string `json:"epgSource"`
Errors int `json:"errors"`
M3U string `json:"m3u-url,required"`
M3U string `json:"m3u-url"`
OS string `json:"os"`
Streams string `json:"streams"`
UUID string `json:"uuid"`
Version string `json:"version"`
Warnings int `json:"warnings"`
XEPGCount int64 `json:"xepg"`
XML string `json:"xepg-url,required"`
XML string `json:"xepg-url"`
} `json:"clientInfo"`
Data struct {
Playlist struct {
M3U struct {
Groups struct {
Text []string `json:"text,required"`
Value []string `json:"value,required"`
} `json:"groups,required"`
} `json:"m3u,required"`
} `json:"playlist,required"`
Text []string `json:"text"`
Value []string `json:"value"`
} `json:"groups"`
} `json:"m3u"`
} `json:"playlist"`
StreamPreviewUI struct {
Active []string `json:"activeStreams,required"`
Inactive []string `json:"inactiveStreams,required"`
Active []string `json:"activeStreams"`
Inactive []string `json:"inactiveStreams"`
}
} `json:"data,required"`
} `json:"data"`
Alert string `json:"alert,omitempty"`
ConfigurationWizard bool `json:"configurationWizard,required"`
ConfigurationWizard bool `json:"configurationWizard"`
Error string `json:"err,omitempty"`
Log WebScreenLogStruct `json:"log,required"`
Log WebScreenLogStruct `json:"log"`
LogoURL string `json:"logoURL,omitempty"`
OpenLink string `json:"openLink,omitempty"`
OpenMenu string `json:"openMenu,omitempty"`
Reload bool `json:"reload,omitempty"`
Settings SettingsStruct `json:"settings,required"`
Status bool `json:"status,required"`
Settings SettingsStruct `json:"settings"`
Status bool `json:"status"`
Token string `json:"token,omitempty"`
Users map[string]any `json:"users,omitempty"`
Wizard int `json:"wizard,omitempty"`
XEPG map[string]any `json:"xepg,required"`
XEPG map[string]any `json:"xepg"`
Notification map[string]Notification `json:"notification,omitempty"`
}
@@ -135,7 +135,7 @@ type APIRequestStruct struct {
type APIResponseStruct struct {
EpgSource string `json:"epg.source,omitempty"`
Error string `json:"err,omitempty"`
Status bool `json:"status,required"`
Status bool `json:"status"`
StreamsActive int64 `json:"streams.active,omitempty"`
StreamsAll int64 `json:"streams.all,omitempty"`
StreamsXepg int64 `json:"streams.xepg,omitempty"`
@@ -149,7 +149,7 @@ type APIResponseStruct struct {
// WebScreenLogStruct : Logs werden im RAM gespeichert und für das Webinterface bereitgestellt
type WebScreenLogStruct struct {
Errors int `json:"errors,required"`
Log []string `json:"log,required"`
Warnings int `json:"warnings,required"`
Errors int `json:"errors"`
Log []string `json:"log"`
Warnings int `json:"warnings"`
}
+1 -1
View File
@@ -66,7 +66,7 @@ type SubTitle struct {
Value string `xml:",chardata"`
}
//Desc : Programmbeschreibung
// Desc : Programmbeschreibung
type Desc struct {
Lang string `xml:"lang,attr"`
Value string `xml:",chardata"`
+2 -5
View File
@@ -13,7 +13,7 @@ import (
// Entwicklerinfos anzeigen
func showDevInfo() {
if System.Dev == true {
if System.Dev {
fmt.Print("\033[31m")
fmt.Println("* * * * * D E V M O D E * * * * *")
@@ -25,7 +25,6 @@ func showDevInfo() {
}
return
}
// Alle Systemordner erstellen
@@ -261,7 +260,7 @@ func saveSettings(settings SettingsStruct) (err error) {
Settings = settings
if System.Dev == true {
if System.Dev {
Settings.UUID = "2019-01-DEV-xTeVe!"
}
@@ -301,7 +300,6 @@ func setGlobalDomain(domain string) {
System.Addresses.XML = getErrMsg(2106)
}
return
}
// UUID generieren
@@ -323,7 +321,6 @@ func setDeviceID() {
System.DeviceID = fmt.Sprintf("%s:%d", id, Settings.Tuner)
}
return
}
// Provider Streaming-URL zu xTeVe Streaming-URL konvertieren
+14 -39
View File
@@ -7,7 +7,7 @@ import (
"encoding/hex"
"encoding/json"
"fmt"
"io/ioutil"
"io"
"net"
"os"
"os/exec"
@@ -59,11 +59,8 @@ func checkFile(filename string) (err error) {
return err
}
switch mode := fi.Mode(); {
case mode.IsDir():
if fi.Mode().IsDir() {
err = fmt.Errorf("%s: %s", file, getErrMsg(1072))
case mode.IsRegular():
break
}
return
@@ -97,7 +94,7 @@ func checkFilePermission(dir string) (err error) {
var filename = dir + "permission.test"
err = ioutil.WriteFile(filename, []byte(""), 0644)
err = os.WriteFile(filename, []byte(""), 0644)
if err == nil {
err = os.RemoveAll(filename)
}
@@ -125,12 +122,6 @@ func getFilenameFromPath(path string) (file string) {
return filepath.Base(path)
}
// Nicht mehr verwendete Systemdaten löschen
func removeOldSystemData() {
// Temporären Ordner löschen
os.RemoveAll(System.Folder.Temp)
}
// Sucht eine Datei im OS
func searchFileInOS(file string) (path string) {
@@ -197,14 +188,6 @@ func jsonToMap(content string) map[string]any {
return (tmpMap)
}
func jsonToMapInt64(content string) map[int64]any {
var tmpMap = make(map[int64]any)
json.Unmarshal([]byte(content), &tmpMap)
return (tmpMap)
}
func jsonToInterface(content string) (tmpMap any, err error) {
err = json.Unmarshal([]byte(content), &tmpMap)
@@ -221,7 +204,7 @@ func saveMapToJSONFile(file string, tmpMap any) error {
return err
}
err = ioutil.WriteFile(filename, []byte(jsonString), 0644)
err = os.WriteFile(filename, []byte(jsonString), 0644)
if err != nil {
return err
}
@@ -232,16 +215,17 @@ func saveMapToJSONFile(file string, tmpMap any) error {
func loadJSONFileToMap(file string) (tmpMap map[string]any, err error) {
f, err := os.Open(getPlatformFile(file))
if err != nil {
return
}
defer f.Close()
content, err := ioutil.ReadAll(f)
content, err := io.ReadAll(f)
if err == nil {
err = json.Unmarshal([]byte(content), &tmpMap)
}
f.Close()
return
}
@@ -249,10 +233,12 @@ func loadJSONFileToMap(file string) (tmpMap map[string]any, err error) {
func readByteFromFile(file string) (content []byte, err error) {
f, err := os.Open(getPlatformFile(file))
if err != nil {
return
}
defer f.Close()
content, err = ioutil.ReadAll(f)
f.Close()
content, err = io.ReadAll(f)
return
}
@@ -260,7 +246,7 @@ func readByteFromFile(file string) (content []byte, err error) {
func writeByteToFile(file string, data []byte) (err error) {
var filename = getPlatformFile(file)
err = ioutil.WriteFile(filename, data, 0644)
err = os.WriteFile(filename, data, 0644)
return
}
@@ -275,7 +261,7 @@ func readStringFromFile(file string) (str string, err error) {
return
}
content, err = ioutil.ReadFile(filename)
content, err = os.ReadFile(filename)
if err != nil {
ShowError(err, 0)
return
@@ -395,17 +381,6 @@ func indexOfFloat64(element float64, data []float64) int {
return -1
}
func indexOfInt(element int, data []int) int {
for k, v := range data {
if element == v {
return (k)
}
}
return -1
}
func getMD5(str string) string {
md5Hasher := md5.New()
+20 -57
View File
@@ -4,7 +4,7 @@ import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"io"
"net/http"
"os"
"strconv"
@@ -40,8 +40,6 @@ func StartWebserver() (err error) {
http.HandleFunc("/data_images/", DataImages)
http.HandleFunc("/healthz", Healthz)
//http.HandleFunc("/auto/", Auto)
showInfo("DVR IP:" + System.IPAddress + ":" + Settings.Port)
var ips = len(System.IPAddressesV4) + len(System.IPAddressesV6) - 1
@@ -90,7 +88,7 @@ func Index(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
case "/lineup.json":
if Settings.AuthenticationPMS == true {
if Settings.AuthenticationPMS {
_, err := basicAuth(r, "authentication.pms")
if err != nil {
@@ -123,7 +121,6 @@ func Index(w http.ResponseWriter, r *http.Request) {
httpStatusError(w, r, 500)
return
}
// Stream : Web Server /stream/
@@ -151,12 +148,12 @@ func Stream(w http.ResponseWriter, r *http.Request) {
showInfo(fmt.Sprintf("Buffer:false [%s]", Settings.Buffer))
case "xteve":
if strings.Index(streamInfo.URL, "rtsp://") != -1 || strings.Index(streamInfo.URL, "rtp://") != -1 {
if strings.Contains(streamInfo.URL, "rtsp://") || strings.Contains(streamInfo.URL, "rtp://") {
err = errors.New("RTSP and RTP streams are not supported")
ShowError(err, 2004)
showInfo("Streaming URL:" + streamInfo.URL)
http.Redirect(w, r, streamInfo.URL, 302)
http.Redirect(w, r, streamInfo.URL, http.StatusFound)
showInfo("Streaming Info:URL was passed to the client")
return
@@ -181,7 +178,7 @@ func Stream(w http.ResponseWriter, r *http.Request) {
case "-":
showInfo("Streaming URL:" + streamInfo.URL)
http.Redirect(w, r, streamInfo.URL, 302)
http.Redirect(w, r, streamInfo.URL, http.StatusFound)
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.")
@@ -191,32 +188,6 @@ func Stream(w http.ResponseWriter, r *http.Request) {
}
return
}
// Auto : HDHR routing (wird derzeit nicht benutzt)
func Auto(w http.ResponseWriter, r *http.Request) {
var channelID = strings.Replace(r.RequestURI, "/auto/v", "", 1)
fmt.Println(channelID)
/*
switch Settings.Buffer {
case true:
var playlistID, streamURL, err = getStreamByChannelID(channelID)
if err == nil {
bufferingStream(playlistID, streamURL, w, r)
} else {
httpStatusError(w, r, 404)
}
case false:
httpStatusError(w, r, 423)
}
*/
return
}
// xTeVe : Web Server /xmltv/ und /m3u/
@@ -250,7 +221,7 @@ func xTeVe(w http.ResponseWriter, r *http.Request) {
requestType = "m3u"
groupTitle = r.URL.Query().Get("group-title")
if System.Dev == false {
if !System.Dev {
// false: Dateiname wird im Header gesetzt
// true: M3U wird direkt im Browser angezeigt
w.Header().Set("Content-Disposition", "attachment; filename="+getFilenameFromPath(path))
@@ -286,7 +257,6 @@ func xTeVe(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(content))
}
return
}
// Images : Image Cache /images/
@@ -306,7 +276,6 @@ func Images(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(200)
w.Write(content)
return
}
// DataImages : Image Pfad für Logos / Bilder die hochgeladen wurden /data_images/
@@ -326,7 +295,6 @@ func DataImages(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(200)
w.Write(content)
return
}
// WS : Web Sockets /ws/
@@ -362,7 +330,7 @@ func WS(w http.ResponseWriter, r *http.Request) {
return
}
if System.ConfigurationWizard == false {
if !System.ConfigurationWizard {
switch Settings.AuthenticationWEB {
@@ -425,7 +393,7 @@ func WS(w http.ResponseWriter, r *http.Request) {
response.OpenMenu = strconv.Itoa(indexOfString("settings", System.WEB.Menu))
if Settings.AuthenticationWEB == true && authenticationUpdate == false {
if Settings.AuthenticationWEB && !authenticationUpdate {
response.Reload = true
}
@@ -568,7 +536,7 @@ func WS(w http.ResponseWriter, r *http.Request) {
var requestMap = make(map[string]any) // Debug
_ = requestMap
if System.Dev == true {
if System.Dev {
fmt.Println(mapToJSON(requestMap))
}
@@ -581,7 +549,7 @@ func WS(w http.ResponseWriter, r *http.Request) {
}
response = setDefaultResponseData(response, true)
if System.ConfigurationWizard == true {
if System.ConfigurationWizard {
response.ConfigurationWizard = System.ConfigurationWizard
}
@@ -593,7 +561,6 @@ func WS(w http.ResponseWriter, r *http.Request) {
}
return
}
// Healthz : Liveness endpoint for container healthchecks and monitoring.
@@ -629,7 +596,7 @@ func Web(w http.ResponseWriter, r *http.Request) {
if System.ScanInProgress == 0 {
if Settings.WizardCompleted == false && len(Settings.Files.M3U) == 0 && len(Settings.Files.HDHR) == 0 {
if !Settings.WizardCompleted && len(Settings.Files.M3U) == 0 && len(Settings.Files.HDHR) == 0 {
System.ConfigurationWizard = true
}
@@ -675,7 +642,7 @@ func Web(w http.ResponseWriter, r *http.Request) {
}
// Redirect, damit die Daten aus dem Browser gelöscht werden.
w = authentication.SetCookieToken(w, token)
http.Redirect(w, r, "/web", 301)
http.Redirect(w, r, "/web", http.StatusMovedPermanently)
return
}
@@ -691,11 +658,11 @@ func Web(w http.ResponseWriter, r *http.Request) {
}
w = authentication.SetCookieToken(w, token)
http.Redirect(w, r, "/web", 301) // Redirect, damit die Daten aus dem Browser gelöscht werden.
http.Redirect(w, r, "/web", http.StatusMovedPermanently) // Redirect, damit die Daten aus dem Browser gelöscht werden.
} else {
w = authentication.SetCookieToken(w, "-")
http.Redirect(w, r, "/web", 301) // Redirect, damit die Daten aus dem Browser gelöscht werden.
http.Redirect(w, r, "/web", http.StatusMovedPermanently) // Redirect, damit die Daten aus dem Browser gelöscht werden.
}
return
@@ -723,7 +690,7 @@ func Web(w http.ResponseWriter, r *http.Request) {
return
}
if len(allUserData) == 0 && Settings.AuthenticationWEB == true {
if len(allUserData) == 0 && Settings.AuthenticationWEB {
file = "create-first-user.html"
}
@@ -828,13 +795,12 @@ func API(w http.ResponseWriter, r *http.Request) {
response.Status = false
response.Error = err.Error()
w.Write([]byte(mapToJSON(response)))
return
}
response.Status = true
if Settings.API == false {
if !Settings.API {
httpStatusError(w, r, 423)
return
}
@@ -844,7 +810,7 @@ func API(w http.ResponseWriter, r *http.Request) {
return
}
b, err := ioutil.ReadAll(r.Body)
b, err := io.ReadAll(r.Body)
defer r.Body.Close()
if err != nil {
httpStatusError(w, r, 400)
@@ -860,7 +826,7 @@ func API(w http.ResponseWriter, r *http.Request) {
w.Header().Set("content-type", "application/json")
if Settings.AuthenticationAPI == true {
if Settings.AuthenticationAPI {
var token string
switch len(request.Token) {
case 0:
@@ -957,7 +923,6 @@ func API(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(mapToJSON(response)))
return
}
// Download : Datei Download
@@ -975,7 +940,6 @@ func Download(w http.ResponseWriter, r *http.Request) {
os.RemoveAll(System.Folder.Temp + getFilenameFromPath(path))
w.Write([]byte(content))
return
}
func setDefaultResponseData(response ResponseStruct, data bool) (defaults ResponseStruct) {
@@ -999,7 +963,7 @@ func setDefaultResponseData(response ResponseStruct, data bool) (defaults Respon
switch System.Branch {
case "master":
defaults.ClientInfo.Version = fmt.Sprintf("%s", System.Version)
defaults.ClientInfo.Version = System.Version
default:
defaults.ClientInfo.Version = fmt.Sprintf("%s (%s)", System.Version, System.Build)
@@ -1007,7 +971,7 @@ func setDefaultResponseData(response ResponseStruct, data bool) (defaults Respon
}
if data == true {
if data {
defaults.Users, _ = authentication.GetAllUserData()
//defaults.DVR = System.DVRAddress
@@ -1048,7 +1012,6 @@ func setDefaultResponseData(response ResponseStruct, data bool) (defaults Respon
func httpStatusError(w http.ResponseWriter, r *http.Request, httpStatusCode int) {
http.Error(w, fmt.Sprintf("%s [%d]", http.StatusText(httpStatusCode), httpStatusCode), httpStatusCode)
return
}
func getContentType(filename string) (contentType string) {
+41 -140
View File
@@ -5,7 +5,7 @@ import (
"encoding/xml"
"errors"
"fmt"
"io/ioutil"
"os"
"path"
"runtime"
"sort"
@@ -71,28 +71,9 @@ func buildXEPG(background bool) {
createM3UFile()
queuePlexGuideRefresh("xepg rebuild")
showInfo("XEPG:" + fmt.Sprintf("Ready to use"))
showInfo("XEPG:" + "Ready to use")
if Settings.CacheImages == true && System.ImageCachingInProgress == 0 {
go func() {
System.ImageCachingInProgress = 1
showInfo(fmt.Sprintf("Image Caching:Images are cached (%d)", len(Data.Cache.Images.Queue)))
Data.Cache.Images.Image.Caching()
Data.Cache.Images.Image.Remove()
showInfo("Image Caching:Done")
createXMLTVFile()
createM3UFile()
queuePlexGuideRefresh("xepg image cache refresh")
System.ImageCachingInProgress = 0
}()
}
cacheImagesInBackground()
System.ScanInProgress = 0
@@ -118,28 +99,9 @@ func buildXEPG(background bool) {
createM3UFile()
queuePlexGuideRefresh("xepg rebuild")
if Settings.CacheImages == true && System.ImageCachingInProgress == 0 {
cacheImagesInBackground()
go func() {
System.ImageCachingInProgress = 1
showInfo(fmt.Sprintf("Image Caching:Images are cached (%d)", len(Data.Cache.Images.Queue)))
Data.Cache.Images.Image.Caching()
Data.Cache.Images.Image.Remove()
showInfo("Image Caching:Done")
createXMLTVFile()
createM3UFile()
queuePlexGuideRefresh("xepg image cache refresh")
System.ImageCachingInProgress = 0
}()
}
showInfo("XEPG:" + fmt.Sprintf("Ready to use"))
showInfo("XEPG:" + "Ready to use")
System.ScanInProgress = 0
@@ -161,52 +123,30 @@ func buildXEPG(background bool) {
}
// XEPG Daten aktualisieren
func updateXEPG(background bool) {
// cacheImagesInBackground : Bilder im Hintergrund cachen, danach XMLTV- und M3U-Datei neu erstellen
func cacheImagesInBackground() {
if System.ScanInProgress == 1 {
if !Settings.CacheImages || System.ImageCachingInProgress != 0 {
return
}
System.ScanInProgress = 1
go func() {
if Settings.EpgSource == "XEPG" {
System.ImageCachingInProgress = 1
showInfo(fmt.Sprintf("Image Caching:Images are cached (%d)", len(Data.Cache.Images.Queue)))
switch background {
Data.Cache.Images.Image.Caching()
Data.Cache.Images.Image.Remove()
showInfo("Image Caching:Done")
case false:
createXMLTVFile()
createM3UFile()
queuePlexGuideRefresh("xepg image cache refresh")
createXEPGDatabase()
mapping()
cleanupXEPG()
System.ImageCachingInProgress = 0
go func() {
}()
createXMLTVFile()
createM3UFile()
queuePlexGuideRefresh("xepg update")
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
@@ -219,7 +159,6 @@ func createXEPGMapping() {
var friendlyDisplayName = func(channel Channel) (displayName string) {
var dn = channel.DisplayName
displayName = dn[0].Value
switch len(dn) {
case 1:
@@ -277,11 +216,10 @@ func createXEPGMapping() {
}
Data.XMLTV.Mapping = tmpMap
tmpMap = make(map[string]any)
} else {
if System.ConfigurationWizard == false {
if !System.ConfigurationWizard {
showWarning(1007)
}
@@ -304,7 +242,6 @@ func createXEPGMapping() {
Data.XMLTV.Mapping["xTeVe Dummy"] = dummy
return
}
// XEPG Datenbank erstellen / aktualisieren
@@ -339,7 +276,7 @@ func createXEPGDatabase() (err error) {
sort.Float64s(allChannelNumbers)
var firstFreeNumber float64 = Settings.MappingFirstChannel
var firstFreeNumber = Settings.MappingFirstChannel
for {
@@ -469,14 +406,14 @@ func createXEPGDatabase() (err error) {
xepgChannel.Name = m3uChannel.Name
// Kanalname aktualisieren, nur mit Kanal ID's möglich
if channelHasUUID == true {
if xepgChannel.XUpdateChannelName == true {
if channelHasUUID {
if xepgChannel.XUpdateChannelName {
xepgChannel.XName = m3uChannel.Name
}
}
// Kanallogo aktualisieren. Wird bei vorhandenem Logo in der XMLTV Datei wieder überschrieben
if xepgChannel.XUpdateChannelIcon == true {
if xepgChannel.XUpdateChannelIcon {
xepgChannel.TvgLogo = m3uChannel.TvgLogo
}
@@ -602,7 +539,7 @@ func findXEPGReplacementChannel(xmltvChannels map[string]any, xepgChannel XEPGCh
for id, data := range xmltvChannels {
xmltvChannel, castOK := data.(map[string]any)
if castOK == false {
if !castOK {
continue
}
@@ -615,7 +552,7 @@ func findXEPGReplacementChannel(xmltvChannels map[string]any, xepgChannel XEPGCh
}
displayName, hasDisplayName := xmltvChannel["display-name"].(string)
if hasDisplayName == false {
if !hasDisplayName {
continue
}
@@ -660,7 +597,7 @@ func mapping() (err error) {
}
// 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 {
if !xepgChannel.XActive {
// Werte kann "-" sein, deswegen len < 1
if len(xepgChannel.XmltvFile) < 1 && len(xepgChannel.XMapping) < 1 {
@@ -704,7 +641,7 @@ func mapping() (err error) {
}
// Überprüfen, ob die zugeordneten XMLTV Dateien und Kanäle noch existieren.
if xepgChannel.XActive == true {
if xepgChannel.XActive {
var mapping = xepgChannel.XMapping
var file = xepgChannel.XmltvFile
@@ -718,7 +655,7 @@ func mapping() (err error) {
// Kanallogo aktualisieren
if logo, ok := channel["icon"].(string); ok {
if xepgChannel.XUpdateChannelIcon == true && len(logo) > 0 {
if xepgChannel.XUpdateChannelIcon && len(logo) > 0 {
xepgChannel.TvgLogo = logo
}
@@ -729,7 +666,7 @@ func mapping() (err error) {
xepgChannel.XMapping = channelID
if logo, ok := replacementChannel["icon"].(string); ok {
if xepgChannel.XUpdateChannelIcon == true && len(logo) > 0 {
if xepgChannel.XUpdateChannelIcon && len(logo) > 0 {
xepgChannel.TvgLogo = logo
}
}
@@ -752,7 +689,7 @@ func mapping() (err error) {
name = xepg
}
if strictMissingEPGMode == true {
if strictMissingEPGMode {
missingEPGCount++
appendXEPGIssueSample(missingEPGSamples, name)
xepgChannel.XActive = false
@@ -772,7 +709,7 @@ func mapping() (err error) {
providerName = file
}
if strictMissingEPGMode == true {
if strictMissingEPGMode {
missingXMLTVCount++
appendXEPGIssueSample(missingXMLTVSamples, providerName)
xepgChannel.XActive = false
@@ -849,7 +786,7 @@ func createXMLTVFile() (err error) {
Data.Cache.ImagesURLS = []string{}
Data.Cache.ImagesCache = []string{}
files, err := ioutil.ReadDir(System.Folder.ImagesCache)
files, err := os.ReadDir(System.Folder.ImagesCache)
if err == nil {
for _, file := range files {
@@ -887,7 +824,7 @@ func createXMLTVFile() (err error) {
err := json.Unmarshal([]byte(mapToJSON(dxc)), &xepgChannel)
if err == nil {
if xepgChannel.XActive == true {
if xepgChannel.XActive {
// Kanäle
var channel Channel
@@ -902,9 +839,7 @@ func createXMLTVFile() (err error) {
*tmpProgram, err = getProgramData(xepgChannel)
if err == nil {
for _, program := range tmpProgram.Program {
xepgXML.Program = append(xepgXML.Program, program)
}
xepgXML.Program = append(xepgXML.Program, tmpProgram.Program...)
}
@@ -947,7 +882,7 @@ func getProgramData(xepgChannel XEPGChannelStruct) (xepgXML XMLTV, err error) {
err = getLocalXMLTV(xmltvFile, &xmltv)
if err != nil {
if relaxedMissingEPGMode == true {
if relaxedMissingEPGMode {
fallbackToDummy()
err = nil
}
@@ -1024,7 +959,7 @@ func getProgramData(xepgChannel XEPGChannelStruct) (xepgXML XMLTV, err error) {
}
if len(xepgXML.Program) == 0 && relaxedMissingEPGMode == true && xmltvFile != System.Folder.Data+"xTeVe Dummy" {
if len(xepgXML.Program) == 0 && relaxedMissingEPGMode && xmltvFile != System.Folder.Data+"xTeVe Dummy" {
fallbackToDummy()
}
@@ -1072,7 +1007,7 @@ func createDummyProgram(xepgChannel XEPGChannelStruct) (dummyXMLTV XMLTV) {
epg.Desc = append(epg.Desc, &Desc{Value: xepgChannel.XDescription, Lang: "en"})
}
if Settings.XepgReplaceMissingImages == true {
if Settings.XepgReplaceMissingImages {
poster.Src = imgc.Image.GetURL(xepgChannel.TvgLogo)
epg.Poster = append(epg.Poster, poster)
}
@@ -1114,7 +1049,6 @@ func getCategory(program *Program, xmltvProgram *Program, xepgChannel XEPGChanne
}
return
}
// Programm Poster Cover aus der XMLTV Datei laden
@@ -1127,7 +1061,7 @@ func getPoster(program *Program, xmltvProgram *Program, xepgChannel XEPGChannelS
program.Poster = append(program.Poster, poster)
}
if Settings.XepgReplaceMissingImages == true {
if Settings.XepgReplaceMissingImages {
if len(xmltvProgram.Poster) == 0 {
var poster Poster
@@ -1161,7 +1095,6 @@ func getEpisodeNum(program *Program, xmltvProgram *Program, xepgChannel XEPGChan
}
return
}
// Videoparameter erstellen (createXMLTVFile)
@@ -1187,7 +1120,6 @@ func getVideo(program *Program, xmltvProgram *Program, xepgChannel XEPGChannelSt
program.Video = video
return
}
// Lokale Provider XMLTV Datei laden
@@ -1236,7 +1168,6 @@ func createM3UFile() {
saveMapToJSONFile(System.File.URLS, Data.Cache.StreamingURLS)
return
}
// XEPG Datenbank bereinigen
@@ -1254,7 +1185,7 @@ func cleanupXEPG() {
sourceIDs = append(sourceIDs, source)
}
showInfo("XEPG:" + fmt.Sprintf("Cleanup database"))
showInfo("XEPG:" + "Cleanup database")
Data.XEPG.XEPGCount = 0
for id, dxc := range Data.XEPG.Channels {
@@ -1266,7 +1197,7 @@ func cleanupXEPG() {
if indexOfString(xepgChannel.Name+xepgChannel.FileM3UID, Data.Cache.Streams.Active) == -1 {
delete(Data.XEPG.Channels, id)
} else {
if xepgChannel.XActive == true {
if xepgChannel.XActive {
Data.XEPG.XEPGCount++
}
}
@@ -1291,34 +1222,4 @@ func cleanupXEPG() {
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
}