pegasus/backend/internal/usermap.go

87 lines
1.9 KiB
Go

// backend/internal/usermap.go
package internal
import (
"fmt"
"os"
"path/filepath"
"strings"
"gopkg.in/yaml.v3"
)
// StringOrList allows both a single string ("Stein_90s")
// and a list of strings (["Stein_90s","Home Videos"]) in YAML.
type StringOrList []string
func (s *StringOrList) UnmarshalYAML(value *yaml.Node) error {
switch value.Kind {
case yaml.ScalarNode:
var str string
if err := value.Decode(&str); err != nil {
return err
}
*s = StringOrList{str}
return nil
case yaml.SequenceNode:
var list []string
if err := value.Decode(&list); err != nil {
return err
}
*s = StringOrList(list)
return nil
default:
return fmt.Errorf("expected string or list, got kind=%v", value.Kind)
}
}
type UserMap struct {
// Maps Jellyfin username -> one or more relative media subdirs
Map map[string]StringOrList `yaml:"map"`
}
func LoadUserMap(path string) (*UserMap, error) {
b, err := os.ReadFile(path)
if err != nil {
return nil, err
}
var m UserMap
if err := yaml.Unmarshal(b, &m); err != nil {
return nil, err
}
return &m, nil
}
// Resolve returns the first configured library for the user (back-compat).
func (m *UserMap) Resolve(username string) (string, error) {
roots, err := m.ResolveAll(username)
if err != nil {
return "", err
}
return roots[0], nil
}
// ResolveAll returns all configured libraries for the user.
func (m *UserMap) ResolveAll(username string) ([]string, error) {
v, ok := m.Map[username]
if !ok || len(v) == 0 {
return nil, fmt.Errorf("no mapping for user %q", username)
}
out := make([]string, 0, len(v))
for _, dir := range v {
dir = strings.TrimSpace(dir)
if dir == "" {
continue
}
clean := filepath.Clean(dir)
if clean == "." || clean == "/" {
continue
}
out = append(out, clean)
}
if len(out) == 0 {
return nil, fmt.Errorf("no valid mapping for user %q", username)
}
return out, nil
}