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
import (
"errors"
"flag"
"fmt"
"os"
"os/exec"
"path/filepath"
"runtime"
"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 UserAgent = "go2rtc/" + Version
var ConfigPath string
var Info = map[string]any{
"version": Version,
}
var (
Version string
UserAgent string
ConfigPath string
Info = make(map[string]any)
)
const usage = `Usage of go2rtc:
@@ -33,12 +24,12 @@ const usage = `Usage of go2rtc:
`
func Init() {
var confs Config
var config flagConfig
var daemon bool
var version bool
flag.Var(&confs, "config", "")
flag.Var(&confs, "c", "")
flag.Var(&config, "config", "")
flag.Var(&config, "c", "")
flag.BoolVar(&daemon, "daemon", false, "")
flag.BoolVar(&daemon, "d", false, "")
flag.BoolVar(&version, "version", false, "")
@@ -69,118 +60,30 @@ func Init() {
// Re-run the program in background and exit
cmd := exec.Command(os.Args[0], args...)
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)
os.Exit(0)
}
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
}
UserAgent = "go2rtc/" + Version
Info["version"] = Version
Info["revision"] = revision
var cfg struct {
Mod map[string]string `yaml:"log"`
}
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
initConfig(config)
initLogger()
platform := fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH)
log.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.Info().Str("version", Version).Str("platform", platform).Str("revision", revision).Msg("go2rtc")
Logger.Debug().Str("version", runtime.Version()).Str("vcs.time", vcsTime).Msg("build")
if ConfigPath != "" {
log.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")
}
Logger.Info().Str("path", ConfigPath).Msg("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) {
if info, ok := debug.ReadBuildInfo(); ok {
for _, setting := range info.Settings {
@@ -202,25 +105,3 @@ func readRevisionTime() (revision, vcsTime string) {
}
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/rs/zerolog"
"github.com/rs/zerolog/log"
)
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
// - format: empty (autodetect color support), color, json, text
// - time: empty (disable timestamp), UNIXMS, UNIXMICRO, UNIXNANO
// - 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
switch config["output"] {
switch modules["output"] {
case "stderr":
writer = os.Stderr
case "stdout":
writer = os.Stdout
}
timeFormat := config["time"]
timeFormat := modules["time"]
if writer != nil {
if format := config["format"]; format != "json" {
if format := modules["format"]; format != "json" {
console := &zerolog.ConsoleWriter{Out: writer}
switch format {
@@ -61,31 +80,24 @@ func NewLogger(config map[string]string) zerolog.Logger {
writer = MemoryLog
}
logger := zerolog.New(writer)
lvl, _ := zerolog.ParseLevel(modules["level"])
Logger = zerolog.New(writer).Level(lvl)
if 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 {
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
}
var Logger zerolog.Logger
// 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

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

View File

@@ -14,6 +14,7 @@ import (
"github.com/AlexxIT/go2rtc/internal/streams"
"github.com/AlexxIT/go2rtc/pkg/core"
"github.com/AlexxIT/go2rtc/pkg/ffmpeg"
"github.com/rs/zerolog"
)
func Init() {
@@ -29,6 +30,8 @@ func Init() {
app.LoadConfig(&cfg)
log = app.GetLogger("ffmpeg")
// zerolog levels: trace debug info warn error fatal panic disabled
// FFmpeg levels: trace debug verbose info warning error fatal panic quiet
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",
}
var log zerolog.Logger
// configTemplate - return template from config (defaults) if exist or return raw template
func configTemplate(template string) string {
if s := defaults[template]; s != "" {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -6,7 +6,6 @@ import (
"encoding/json"
"errors"
"fmt"
"log"
"net/rpc"
"net/url"
"strconv"
@@ -138,7 +137,7 @@ func (c *Client) Connect() error {
}
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 {
return err
}
@@ -151,7 +150,7 @@ func (c *Client) Connect() error {
time.Sleep(time.Second)
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 {
return err
}

View File

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