Code refactoring for app module

This commit is contained in:
Alex X
2024-06-01 19:18:26 +03:00
parent 2ab1d9d774
commit 756be9801e
15 changed files with 182 additions and 217 deletions

View File

@@ -1,29 +1,20 @@
package app package app
import ( import (
"errors"
"flag" "flag"
"fmt" "fmt"
"os" "os"
"os/exec" "os/exec"
"path/filepath"
"runtime" "runtime"
"runtime/debug" "runtime/debug"
"strings"
"github.com/AlexxIT/go2rtc/pkg/shell"
"github.com/AlexxIT/go2rtc/pkg/yaml"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
) )
var Version = "1.9.2" var (
var UserAgent = "go2rtc/" + Version Version string
UserAgent string
var ConfigPath string ConfigPath string
var Info = map[string]any{ Info = make(map[string]any)
"version": Version, )
}
const usage = `Usage of go2rtc: const usage = `Usage of go2rtc:
@@ -33,12 +24,12 @@ const usage = `Usage of go2rtc:
` `
func Init() { func Init() {
var confs Config var config flagConfig
var daemon bool var daemon bool
var version bool var version bool
flag.Var(&confs, "config", "") flag.Var(&config, "config", "")
flag.Var(&confs, "c", "") flag.Var(&config, "c", "")
flag.BoolVar(&daemon, "daemon", false, "") flag.BoolVar(&daemon, "daemon", false, "")
flag.BoolVar(&daemon, "d", false, "") flag.BoolVar(&daemon, "d", false, "")
flag.BoolVar(&version, "version", false, "") flag.BoolVar(&version, "version", false, "")
@@ -69,118 +60,30 @@ func Init() {
// Re-run the program in background and exit // Re-run the program in background and exit
cmd := exec.Command(os.Args[0], args...) cmd := exec.Command(os.Args[0], args...)
if err := cmd.Start(); err != nil { if err := cmd.Start(); err != nil {
log.Fatal().Err(err).Send() fmt.Println(err)
os.Exit(1)
} }
fmt.Println("Running in daemon mode with PID:", cmd.Process.Pid) fmt.Println("Running in daemon mode with PID:", cmd.Process.Pid)
os.Exit(0) os.Exit(0)
} }
if confs == nil { UserAgent = "go2rtc/" + Version
confs = []string{"go2rtc.yaml"}
}
for _, conf := range confs {
if len(conf) == 0 {
continue
}
if conf[0] == '{' {
// config as raw YAML or JSON
configs = append(configs, []byte(conf))
} else if data := parseConfString(conf); data != nil {
configs = append(configs, data)
} else {
// config as file
if ConfigPath == "" {
ConfigPath = conf
}
if data, _ = os.ReadFile(conf); data == nil {
continue
}
data = []byte(shell.ReplaceEnvVars(string(data)))
configs = append(configs, data)
}
}
if ConfigPath != "" {
if !filepath.IsAbs(ConfigPath) {
if cwd, err := os.Getwd(); err == nil {
ConfigPath = filepath.Join(cwd, ConfigPath)
}
}
Info["config_path"] = ConfigPath
}
Info["version"] = Version
Info["revision"] = revision Info["revision"] = revision
var cfg struct { initConfig(config)
Mod map[string]string `yaml:"log"` initLogger()
}
cfg.Mod = map[string]string{
"format": "", // useless, but anyway
"level": "info",
"output": "stdout", // TODO: change to stderr someday
"time": zerolog.TimeFormatUnixMs,
}
LoadConfig(&cfg)
log.Logger = NewLogger(cfg.Mod)
modules = cfg.Mod
platform := fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH) platform := fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH)
log.Info().Str("version", Version).Str("platform", platform).Str("revision", revision).Msg("go2rtc") Logger.Info().Str("version", Version).Str("platform", platform).Str("revision", revision).Msg("go2rtc")
log.Debug().Str("version", runtime.Version()).Str("vcs.time", vcsTime).Msg("build") Logger.Debug().Str("version", runtime.Version()).Str("vcs.time", vcsTime).Msg("build")
if ConfigPath != "" { if ConfigPath != "" {
log.Info().Str("path", ConfigPath).Msg("config") Logger.Info().Str("path", ConfigPath).Msg("config")
}
migrateStore()
}
func LoadConfig(v any) {
for _, data := range configs {
if err := yaml.Unmarshal(data, v); err != nil {
log.Warn().Err(err).Msg("[app] read config")
}
} }
} }
func PatchConfig(key string, value any, path ...string) error {
if ConfigPath == "" {
return errors.New("config file disabled")
}
// empty config is OK
b, _ := os.ReadFile(ConfigPath)
b, err := yaml.Patch(b, key, value, path...)
if err != nil {
return err
}
return os.WriteFile(ConfigPath, b, 0644)
}
// internal
type Config []string
func (c *Config) String() string {
return strings.Join(*c, " ")
}
func (c *Config) Set(value string) error {
*c = append(*c, value)
return nil
}
var configs [][]byte
func readRevisionTime() (revision, vcsTime string) { func readRevisionTime() (revision, vcsTime string) {
if info, ok := debug.ReadBuildInfo(); ok { if info, ok := debug.ReadBuildInfo(); ok {
for _, setting := range info.Settings { for _, setting := range info.Settings {
@@ -202,25 +105,3 @@ func readRevisionTime() (revision, vcsTime string) {
} }
return return
} }
func parseConfString(s string) []byte {
i := strings.IndexByte(s, '=')
if i < 0 {
return nil
}
items := strings.Split(s[:i], ".")
if len(items) < 2 {
return nil
}
// `log.level=trace` => `{log: {level: trace}}`
var pre string
var suf = s[i+1:]
for _, item := range items {
pre += "{" + item + ": "
suf += "}"
}
return []byte(pre + suf)
}

109
internal/app/config.go Normal file
View File

@@ -0,0 +1,109 @@
package app
import (
"errors"
"os"
"path/filepath"
"strings"
"github.com/AlexxIT/go2rtc/pkg/shell"
"github.com/AlexxIT/go2rtc/pkg/yaml"
)
func LoadConfig(v any) {
for _, data := range configs {
if err := yaml.Unmarshal(data, v); err != nil {
Logger.Warn().Err(err).Send()
}
}
}
func PatchConfig(key string, value any, path ...string) error {
if ConfigPath == "" {
return errors.New("config file disabled")
}
// empty config is OK
b, _ := os.ReadFile(ConfigPath)
b, err := yaml.Patch(b, key, value, path...)
if err != nil {
return err
}
return os.WriteFile(ConfigPath, b, 0644)
}
type flagConfig []string
func (c *flagConfig) String() string {
return strings.Join(*c, " ")
}
func (c *flagConfig) Set(value string) error {
*c = append(*c, value)
return nil
}
var configs [][]byte
func initConfig(confs flagConfig) {
if confs == nil {
confs = []string{"go2rtc.yaml"}
}
for _, conf := range confs {
if len(conf) == 0 {
continue
}
if conf[0] == '{' {
// config as raw YAML or JSON
configs = append(configs, []byte(conf))
} else if data := parseConfString(conf); data != nil {
configs = append(configs, data)
} else {
// config as file
if ConfigPath == "" {
ConfigPath = conf
}
if data, _ = os.ReadFile(conf); data == nil {
continue
}
data = []byte(shell.ReplaceEnvVars(string(data)))
configs = append(configs, data)
}
}
if ConfigPath != "" {
if !filepath.IsAbs(ConfigPath) {
if cwd, err := os.Getwd(); err == nil {
ConfigPath = filepath.Join(cwd, ConfigPath)
}
}
Info["config_path"] = ConfigPath
}
}
func parseConfString(s string) []byte {
i := strings.IndexByte(s, '=')
if i < 0 {
return nil
}
items := strings.Split(s[:i], ".")
if len(items) < 2 {
return nil
}
// `log.level=trace` => `{log: {level: trace}}`
var pre string
var suf = s[i+1:]
for _, item := range items {
pre += "{" + item + ": "
suf += "}"
}
return []byte(pre + suf)
}

View File

@@ -6,30 +6,49 @@ import (
"github.com/mattn/go-isatty" "github.com/mattn/go-isatty"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/rs/zerolog/log"
) )
var MemoryLog = newBuffer(16) var MemoryLog = newBuffer(16)
// NewLogger support: func GetLogger(module string) zerolog.Logger {
if s, ok := modules[module]; ok {
lvl, err := zerolog.ParseLevel(s)
if err == nil {
return Logger.Level(lvl)
}
Logger.Warn().Err(err).Caller().Send()
}
return Logger
}
// initLogger support:
// - output: empty (only to memory), stderr, stdout // - output: empty (only to memory), stderr, stdout
// - format: empty (autodetect color support), color, json, text // - format: empty (autodetect color support), color, json, text
// - time: empty (disable timestamp), UNIXMS, UNIXMICRO, UNIXNANO // - time: empty (disable timestamp), UNIXMS, UNIXMICRO, UNIXNANO
// - level: disabled, trace, debug, info, warn, error... // - level: disabled, trace, debug, info, warn, error...
func NewLogger(config map[string]string) zerolog.Logger { func initLogger() {
var cfg struct {
Mod map[string]string `yaml:"log"`
}
cfg.Mod = modules // defaults
LoadConfig(&cfg)
var writer io.Writer var writer io.Writer
switch config["output"] { switch modules["output"] {
case "stderr": case "stderr":
writer = os.Stderr writer = os.Stderr
case "stdout": case "stdout":
writer = os.Stdout writer = os.Stdout
} }
timeFormat := config["time"] timeFormat := modules["time"]
if writer != nil { if writer != nil {
if format := config["format"]; format != "json" { if format := modules["format"]; format != "json" {
console := &zerolog.ConsoleWriter{Out: writer} console := &zerolog.ConsoleWriter{Out: writer}
switch format { switch format {
@@ -61,31 +80,24 @@ func NewLogger(config map[string]string) zerolog.Logger {
writer = MemoryLog writer = MemoryLog
} }
logger := zerolog.New(writer) lvl, _ := zerolog.ParseLevel(modules["level"])
Logger = zerolog.New(writer).Level(lvl)
if timeFormat != "" { if timeFormat != "" {
zerolog.TimeFieldFormat = timeFormat zerolog.TimeFieldFormat = timeFormat
logger = logger.With().Timestamp().Logger() Logger = Logger.With().Timestamp().Logger()
} }
lvl, _ := zerolog.ParseLevel(config["level"])
return logger.Level(lvl)
} }
func GetLogger(module string) zerolog.Logger { var Logger zerolog.Logger
if s, ok := modules[module]; ok {
lvl, err := zerolog.ParseLevel(s)
if err == nil {
return log.Level(lvl)
}
log.Warn().Err(err).Caller().Send()
}
return log.Logger
}
// modules log levels // modules log levels
var modules map[string]string var modules = map[string]string{
"format": "", // useless, but anyway
"level": "info",
"output": "stdout", // TODO: change to stderr someday
"time": zerolog.TimeFormatUnixMs,
}
const chunkSize = 1 << 16 const chunkSize = 1 << 16

View File

@@ -1,35 +0,0 @@
package app
import (
"encoding/json"
"os"
"github.com/rs/zerolog/log"
)
func migrateStore() {
const name = "go2rtc.json"
data, _ := os.ReadFile(name)
if data == nil {
return
}
var store struct {
Streams map[string]string `json:"streams"`
}
if err := json.Unmarshal(data, &store); err != nil {
log.Warn().Err(err).Caller().Send()
return
}
for id, url := range store.Streams {
if err := PatchConfig(id, url, "streams"); err != nil {
log.Warn().Err(err).Caller().Send()
return
}
}
_ = os.Remove(name)
}

View File

@@ -12,7 +12,6 @@ import (
"github.com/AlexxIT/go2rtc/internal/streams" "github.com/AlexxIT/go2rtc/internal/streams"
"github.com/AlexxIT/go2rtc/pkg/core" "github.com/AlexxIT/go2rtc/pkg/core"
"github.com/AlexxIT/go2rtc/pkg/dvrip" "github.com/AlexxIT/go2rtc/pkg/dvrip"
"github.com/rs/zerolog/log"
) )
func Init() { func Init() {
@@ -92,10 +91,7 @@ func sendBroadcasts(conn *net.UDPConn) {
for i := 0; i < 3; i++ { for i := 0; i < 3; i++ {
time.Sleep(100 * time.Millisecond) time.Sleep(100 * time.Millisecond)
_, _ = conn.WriteToUDP(data, addr)
if _, err = conn.WriteToUDP(data, addr); err != nil {
log.Err(err).Caller().Send()
}
} }
} }

View File

@@ -14,6 +14,7 @@ import (
"github.com/AlexxIT/go2rtc/internal/streams" "github.com/AlexxIT/go2rtc/internal/streams"
"github.com/AlexxIT/go2rtc/pkg/core" "github.com/AlexxIT/go2rtc/pkg/core"
"github.com/AlexxIT/go2rtc/pkg/ffmpeg" "github.com/AlexxIT/go2rtc/pkg/ffmpeg"
"github.com/rs/zerolog"
) )
func Init() { func Init() {
@@ -29,6 +30,8 @@ func Init() {
app.LoadConfig(&cfg) app.LoadConfig(&cfg)
log = app.GetLogger("ffmpeg")
// zerolog levels: trace debug info warn error fatal panic disabled // zerolog levels: trace debug info warn error fatal panic disabled
// FFmpeg levels: trace debug verbose info warning error fatal panic quiet // FFmpeg levels: trace debug verbose info warning error fatal panic quiet
if cfg.Log.Level == "warn" { if cfg.Log.Level == "warn" {
@@ -145,6 +148,8 @@ var defaults = map[string]string{
"h265/videotoolbox": "-c:v hevc_videotoolbox -g 50 -bf 0 -profile:v main -level:v 5.1", "h265/videotoolbox": "-c:v hevc_videotoolbox -g 50 -bf 0 -profile:v main -level:v 5.1",
} }
var log zerolog.Logger
// configTemplate - return template from config (defaults) if exist or return raw template // configTemplate - return template from config (defaults) if exist or return raw template
func configTemplate(template string) string { func configTemplate(template string) string {
if s := defaults[template]; s != "" { if s := defaults[template]; s != "" {

View File

@@ -7,8 +7,6 @@ import (
"github.com/AlexxIT/go2rtc/internal/api" "github.com/AlexxIT/go2rtc/internal/api"
"github.com/AlexxIT/go2rtc/pkg/ffmpeg" "github.com/AlexxIT/go2rtc/pkg/ffmpeg"
"github.com/rs/zerolog/log"
) )
const ( const (
@@ -152,7 +150,6 @@ var cache = map[string]string{}
func run(bin string, args string) bool { func run(bin string, args string) bool {
err := exec.Command(bin, strings.Split(args, " ")...).Run() err := exec.Command(bin, strings.Split(args, " ")...).Run()
log.Printf("%v %v", args, err)
return err == nil return err == nil
} }

View File

@@ -6,7 +6,6 @@ import (
"sync" "sync"
"github.com/AlexxIT/go2rtc/pkg/ffmpeg" "github.com/AlexxIT/go2rtc/pkg/ffmpeg"
"github.com/rs/zerolog/log"
) )
var verMu sync.Mutex var verMu sync.Mutex

View File

@@ -10,6 +10,7 @@ import (
"github.com/AlexxIT/go2rtc/internal/api" "github.com/AlexxIT/go2rtc/internal/api"
"github.com/AlexxIT/go2rtc/internal/api/ws" "github.com/AlexxIT/go2rtc/internal/api/ws"
"github.com/AlexxIT/go2rtc/internal/app"
"github.com/AlexxIT/go2rtc/internal/ffmpeg" "github.com/AlexxIT/go2rtc/internal/ffmpeg"
"github.com/AlexxIT/go2rtc/internal/streams" "github.com/AlexxIT/go2rtc/internal/streams"
"github.com/AlexxIT/go2rtc/pkg/ascii" "github.com/AlexxIT/go2rtc/pkg/ascii"
@@ -18,7 +19,7 @@ import (
"github.com/AlexxIT/go2rtc/pkg/mjpeg" "github.com/AlexxIT/go2rtc/pkg/mjpeg"
"github.com/AlexxIT/go2rtc/pkg/tcp" "github.com/AlexxIT/go2rtc/pkg/tcp"
"github.com/AlexxIT/go2rtc/pkg/y4m" "github.com/AlexxIT/go2rtc/pkg/y4m"
"github.com/rs/zerolog/log" "github.com/rs/zerolog"
) )
func Init() { func Init() {
@@ -28,8 +29,12 @@ func Init() {
api.HandleFunc("api/stream.y4m", apiStreamY4M) api.HandleFunc("api/stream.y4m", apiStreamY4M)
ws.HandleFunc("mjpeg", handlerWS) ws.HandleFunc("mjpeg", handlerWS)
log = app.GetLogger("mjpeg")
} }
var log zerolog.Logger
func handlerKeyframe(w http.ResponseWriter, r *http.Request) { func handlerKeyframe(w http.ResponseWriter, r *http.Request) {
src := r.URL.Query().Get("src") src := r.URL.Query().Get("src")
stream := streams.Get(src) stream := streams.Get(src)

View File

@@ -7,7 +7,6 @@ import (
"github.com/AlexxIT/go2rtc/internal/streams" "github.com/AlexxIT/go2rtc/internal/streams"
"github.com/AlexxIT/go2rtc/pkg/aac" "github.com/AlexxIT/go2rtc/pkg/aac"
"github.com/AlexxIT/go2rtc/pkg/tcp" "github.com/AlexxIT/go2rtc/pkg/tcp"
"github.com/rs/zerolog/log"
) )
func apiStreamAAC(w http.ResponseWriter, r *http.Request) { func apiStreamAAC(w http.ResponseWriter, r *http.Request) {
@@ -23,7 +22,6 @@ func apiStreamAAC(w http.ResponseWriter, r *http.Request) {
cons.UserAgent = r.UserAgent() cons.UserAgent = r.UserAgent()
if err := stream.AddConsumer(cons); err != nil { if err := stream.AddConsumer(cons); err != nil {
log.Error().Err(err).Caller().Send()
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }

View File

@@ -7,7 +7,6 @@ import (
"github.com/AlexxIT/go2rtc/internal/streams" "github.com/AlexxIT/go2rtc/internal/streams"
"github.com/AlexxIT/go2rtc/pkg/mpegts" "github.com/AlexxIT/go2rtc/pkg/mpegts"
"github.com/AlexxIT/go2rtc/pkg/tcp" "github.com/AlexxIT/go2rtc/pkg/tcp"
"github.com/rs/zerolog/log"
) )
func Init() { func Init() {
@@ -36,7 +35,6 @@ func outputMpegTS(w http.ResponseWriter, r *http.Request) {
cons.UserAgent = r.UserAgent() cons.UserAgent = r.UserAgent()
if err := stream.AddConsumer(cons); err != nil { if err := stream.AddConsumer(cons); err != nil {
log.Error().Err(err).Caller().Send()
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }

View File

@@ -36,6 +36,8 @@ import (
) )
func main() { func main() {
app.Version = "1.9.2"
// 1. Core modules: app, api/ws, streams // 1. Core modules: app, api/ws, streams
app.Init() // init config and logs app.Init() // init config and logs

View File

@@ -5,7 +5,6 @@ import (
"github.com/AlexxIT/go2rtc/pkg/core" "github.com/AlexxIT/go2rtc/pkg/core"
"github.com/pion/rtp" "github.com/pion/rtp"
"github.com/rs/zerolog/log"
) )
func Log(handler core.HandlerFunc) core.HandlerFunc { func Log(handler core.HandlerFunc) core.HandlerFunc {

View File

@@ -6,7 +6,6 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"log"
"net/rpc" "net/rpc"
"net/url" "net/url"
"strconv" "strconv"
@@ -138,7 +137,7 @@ func (c *Client) Connect() error {
} }
offer := pc.LocalDescription() offer := pc.LocalDescription()
log.Printf("[roborock] offer\n%s", offer.SDP) //log.Printf("[roborock] offer\n%s", offer.SDP)
if err = c.SendSDPtoRobot(offer); err != nil { if err = c.SendSDPtoRobot(offer); err != nil {
return err return err
} }
@@ -151,7 +150,7 @@ func (c *Client) Connect() error {
time.Sleep(time.Second) time.Sleep(time.Second)
if desc, _ := c.GetDeviceSDP(); desc != nil { if desc, _ := c.GetDeviceSDP(); desc != nil {
log.Printf("[roborock] answer\n%s", desc.SDP) //log.Printf("[roborock] answer\n%s", desc.SDP)
if err = c.conn.SetAnswer(desc.SDP); err != nil { if err = c.conn.SetAnswer(desc.SDP); err != nil {
return err return err
} }

View File

@@ -6,12 +6,12 @@ import (
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/AlexxIT/go2rtc/pkg/mqtt"
"github.com/rs/zerolog/log"
"net" "net"
"net/rpc" "net/rpc"
"net/url" "net/url"
"time" "time"
"github.com/AlexxIT/go2rtc/pkg/mqtt"
) )
type Codec struct { type Codec struct {
@@ -56,7 +56,7 @@ func (c *Codec) WriteRequest(r *rpc.Request, v any) error {
return err return err
} }
log.Printf("[roborock] send: %s", payload) //log.Printf("[roborock] send: %s", payload)
payload = c.Encrypt(payload, ts, ts, ts) payload = c.Encrypt(payload, ts, ts, ts)
@@ -86,7 +86,7 @@ func (c *Codec) ReadResponseHeader(r *rpc.Response) error {
continue continue
} }
log.Printf("[roborock] recv %s", payload) //log.Printf("[roborock] recv %s", payload)
// get content from response payload: // get content from response payload:
// {"t":1676871268,"dps":{"102":"{\"id\":315003,\"result\":[\"ok\"]}"}} // {"t":1676871268,"dps":{"102":"{\"id\":315003,\"result\":[\"ok\"]}"}}