From 1b690619af09ff433b40c02879e1d6f0b02452e3 Mon Sep 17 00:00:00 2001 From: melserngl <128845117+nglmercer@users.noreply.github.com> Date: Fri, 21 Nov 2025 12:16:53 -0500 Subject: [PATCH] refactor(fs): root filesystem to cwd and simplify path handling - Switch API reload to `NewRootedDiskFilesystem(Root: ".")` for cwd-relative ops - Use `NewString` for RTMP.App/FFmpeg.Binary (drop absolute/exec enforcement) - Remove absolute path resolution in JSON config store - Improve `diskFilesystem.LookPath`: use `filepath.IsAbs`, add Windows `.exe` suffix - Simplify FFmpeg skills init error handling Allows portable execution from any dir (e.g., containers), fixes Windows compat. --- app/api/api.go | 2 +- config/config.go | 4 ++-- config/store/json.go | 6 ------ ffmpeg/skills/skills.go | 8 ++------ io/fs/disk.go | 8 ++++++-- 5 files changed, 11 insertions(+), 17 deletions(-) diff --git a/app/api/api.go b/app/api/api.go index cfc6d895..5dde2a89 100644 --- a/app/api/api.go +++ b/app/api/api.go @@ -153,7 +153,7 @@ func (a *api) Reload() error { logger := log.New("Core").WithOutput(log.NewConsoleWriter(a.log.writer, log.Lwarn, true)) - rootfs, _ := fs.NewDiskFilesystem(fs.DiskConfig{}) + rootfs, _ := fs.NewRootedDiskFilesystem(fs.RootedDiskConfig{Root: "."}) store, err := configstore.NewJSON(rootfs, a.config.path, func() { a.errorChan <- ErrConfigReload }) diff --git a/config/config.go b/config/config.go index 33d9492b..2b730a91 100644 --- a/config/config.go +++ b/config/config.go @@ -216,7 +216,7 @@ func (d *Config) init() { d.vars.Register(value.NewBool(&d.RTMP.EnableTLS, false), "rtmp.enable_tls", "CORE_RTMP_ENABLE_TLS", nil, "Enable RTMPS server instead of RTMP", false, false) d.vars.Register(value.NewAddress(&d.RTMP.Address, ":1935"), "rtmp.address", "CORE_RTMP_ADDRESS", nil, "RTMP server listen address", false, false) d.vars.Register(value.NewAddress(&d.RTMP.AddressTLS, ":1936"), "rtmp.address_tls", "CORE_RTMP_ADDRESS_TLS", nil, "RTMPS server listen address", false, false) - d.vars.Register(value.NewAbsolutePath(&d.RTMP.App, "/"), "rtmp.app", "CORE_RTMP_APP", nil, "RTMP app for publishing", false, false) + d.vars.Register(value.NewString(&d.RTMP.App, "/"), "rtmp.app", "CORE_RTMP_APP", nil, "RTMP app for publishing", false, false) d.vars.Register(value.NewString(&d.RTMP.Token, ""), "rtmp.token", "CORE_RTMP_TOKEN", nil, "RTMP token for publishing and playing", false, true) // SRT @@ -228,7 +228,7 @@ func (d *Config) init() { d.vars.Register(value.NewStringList(&d.SRT.Log.Topics, []string{}, ","), "srt.log.topics", "CORE_SRT_LOG_TOPICS", nil, "List of topics to log", false, false) // FFmpeg - d.vars.Register(value.NewExec(&d.FFmpeg.Binary, "ffmpeg", d.fs), "ffmpeg.binary", "CORE_FFMPEG_BINARY", nil, "Path to ffmpeg binary", true, false) + d.vars.Register(value.NewString(&d.FFmpeg.Binary, "ffmpeg"), "ffmpeg.binary", "CORE_FFMPEG_BINARY", nil, "Path to ffmpeg binary", true, false) d.vars.Register(value.NewInt64(&d.FFmpeg.MaxProcesses, 0), "ffmpeg.max_processes", "CORE_FFMPEG_MAXPROCESSES", nil, "Max. allowed simultaneously running ffmpeg instances, 0 for unlimited", false, false) d.vars.Register(value.NewStringList(&d.FFmpeg.Access.Input.Allow, []string{}, " "), "ffmpeg.access.input.allow", "CORE_FFMPEG_ACCESS_INPUT_ALLOW", nil, "List of allowed expression to match against the input addresses", false, false) d.vars.Register(value.NewStringList(&d.FFmpeg.Access.Input.Block, []string{}, " "), "ffmpeg.access.input.block", "CORE_FFMPEG_ACCESS_INPUT_BLOCK", nil, "List of blocked expression to match against the input addresses", false, false) diff --git a/config/store/json.go b/config/store/json.go index 976d18a0..26cdf35a 100644 --- a/config/store/json.go +++ b/config/store/json.go @@ -4,7 +4,6 @@ import ( gojson "encoding/json" "fmt" "os" - "path/filepath" "github.com/datarhei/core/v16/config" v1 "github.com/datarhei/core/v16/config/v1" @@ -32,11 +31,6 @@ func NewJSON(f fs.Filesystem, path string, reloadFn func()) (Store, error) { reloadFn: reloadFn, } - path, err := filepath.Abs(path) - if err != nil { - return nil, fmt.Errorf("failed to determine absolute path of '%s': %w", path, err) - } - c.path = path if len(c.path) == 0 { diff --git a/ffmpeg/skills/skills.go b/ffmpeg/skills/skills.go index 8f9d103f..34bc925d 100644 --- a/ffmpeg/skills/skills.go +++ b/ffmpeg/skills/skills.go @@ -112,12 +112,8 @@ func New(binary string) (Skills, error) { c := Skills{} ffmpeg, err := version(binary) - if len(ffmpeg.Version) == 0 || err != nil { - if err != nil { - return Skills{}, fmt.Errorf("can't parse ffmpeg version info: %w", err) - } - - return Skills{}, fmt.Errorf("can't parse ffmpeg version info") + if err != nil { + return Skills{}, fmt.Errorf("can't parse ffmpeg version info: %w", err) } c.FFmpeg = ffmpeg diff --git a/io/fs/disk.go b/io/fs/disk.go index 88352c72..3f8e8926 100644 --- a/io/fs/disk.go +++ b/io/fs/disk.go @@ -13,6 +13,7 @@ import ( "github.com/datarhei/core/v16/glob" "github.com/datarhei/core/v16/log" + "runtime" ) // DiskConfig is the config required to create a new disk filesystem. @@ -554,7 +555,7 @@ func (fs *diskFilesystem) List(path, pattern string) []FileInfo { } func (fs *diskFilesystem) LookPath(file string) (string, error) { - if strings.Contains(file, "/") { + if filepath.IsAbs(file) { file = fs.cleanPath(file) err := fs.findExecutable(file) if err == nil { @@ -568,6 +569,9 @@ func (fs *diskFilesystem) LookPath(file string) (string, error) { // Unix shell semantics: path element "" means "." dir = "." } + if runtime.GOOS == "windows" && !strings.HasSuffix(file, ".exe") { + file += ".exe" + } path := filepath.Join(dir, file) path = fs.cleanPath(path) if err := fs.findExecutable(path); err == nil { @@ -591,7 +595,7 @@ func (fs *diskFilesystem) findExecutable(file string) error { return fmt.Errorf("is a directory") } - if m&0111 != 0 { + if runtime.GOOS == "windows" || m&0111 != 0 { return nil }