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.
This commit is contained in:
melserngl 2025-11-21 12:16:53 -05:00
parent 17a73c9f95
commit 1b690619af
5 changed files with 11 additions and 17 deletions

View File

@ -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
})

View File

@ -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)

View File

@ -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 {

View File

@ -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

View File

@ -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
}