Rename host library to master throughout
The two libraries are now "master" and "slave" everywhere: UI text, API JSON fields (master_root, rel_master, master_size, ...), Go identifiers, database columns, quality verdicts, quarantine subfolders and docs. Existing databases are upgraded by a new migration that renames the columns in place and rewrites stored "host" values, so pairs, history, cached verdicts and restorable quarantine entries carry over. Network "host" usages (SplitHostPort, the Origin/Host check) are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
d06b6d546d
commit
5b088bb4a6
19 files changed
+339
-326
No files matched your search
+22
-22
@@ -26,8 +26,8 @@ const QuarantineDirName = ".playlink-trash"
|
||||
|
||||
var (
|
||||
ErrBusy = errors.New("another job is already running for this library pair")
|
||||
ErrDowngrade = errors.New("the host copy is lower quality than the slave copy; confirm the downgrade to continue")
|
||||
ErrConfirm = errors.New("this action modifies the host library and must be confirmed")
|
||||
ErrDowngrade = errors.New("the master copy is lower quality than the slave copy; confirm the downgrade to continue")
|
||||
ErrConfirm = errors.New("this action modifies the master library and must be confirmed")
|
||||
)
|
||||
|
||||
type Engine struct {
|
||||
@@ -163,7 +163,7 @@ func (e *Engine) startJob(pairID int64, kind string, run func(ctx context.Contex
|
||||
|
||||
type libs struct {
|
||||
pair store.Pair
|
||||
host fsops.Root
|
||||
master fsops.Root
|
||||
slave fsops.Root
|
||||
quarantine string
|
||||
dev uint64
|
||||
@@ -187,23 +187,23 @@ func (e *Engine) withinAllowed(p string) bool {
|
||||
// called before every scan and operation so configuration drift (a remount,
|
||||
// a moved folder) is caught before anything is touched.
|
||||
func (e *Engine) open(p store.Pair) (*libs, error) {
|
||||
host, err := fsops.NewRoot(p.HostRoot)
|
||||
master, err := fsops.NewRoot(p.MasterRoot)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("host library: %w", err)
|
||||
return nil, fmt.Errorf("master library: %w", err)
|
||||
}
|
||||
slave, err := fsops.NewRoot(p.SlaveRoot)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("slave library: %w", err)
|
||||
}
|
||||
for _, r := range []string{host.Path, slave.Path} {
|
||||
for _, r := range []string{master.Path, slave.Path} {
|
||||
if !e.withinAllowed(r) {
|
||||
return nil, fmt.Errorf("%s is outside the allowed roots %v", r, e.allowedRoots)
|
||||
}
|
||||
}
|
||||
if fsops.Within(host.Path, slave.Path) || fsops.Within(slave.Path, host.Path) {
|
||||
return nil, errors.New("host and slave libraries must not overlap or contain each other")
|
||||
if fsops.Within(master.Path, slave.Path) || fsops.Within(slave.Path, master.Path) {
|
||||
return nil, errors.New("master and slave libraries must not overlap or contain each other")
|
||||
}
|
||||
hi, err := fsops.Lstat(host.Path)
|
||||
mi, err := fsops.Lstat(master.Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -211,17 +211,17 @@ func (e *Engine) open(p store.Pair) (*libs, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if hi.Dev != si.Dev {
|
||||
return nil, fmt.Errorf("host and slave are on different filesystems (device %d vs %d): %w. "+
|
||||
"In Docker, mount a single common parent folder instead of two separate volumes", hi.Dev, si.Dev, fsops.ErrCrossDevice)
|
||||
if mi.Dev != si.Dev {
|
||||
return nil, fmt.Errorf("master and slave are on different filesystems (device %d vs %d): %w. "+
|
||||
"In Docker, mount a single common parent folder instead of two separate volumes", mi.Dev, si.Dev, fsops.ErrCrossDevice)
|
||||
}
|
||||
|
||||
q := filepath.Clean(p.QuarantineDir)
|
||||
if !filepath.IsAbs(q) {
|
||||
return nil, errors.New("quarantine directory must be absolute")
|
||||
}
|
||||
if fsops.Within(host.Path, q) {
|
||||
return nil, errors.New("quarantine directory must not be inside the host library")
|
||||
if fsops.Within(master.Path, q) {
|
||||
return nil, errors.New("quarantine directory must not be inside the master library")
|
||||
}
|
||||
if q == slave.Path || !e.withinAllowed(q) {
|
||||
return nil, errors.New("quarantine directory must be inside the allowed roots and not the slave root itself")
|
||||
@@ -236,7 +236,7 @@ func (e *Engine) open(p store.Pair) (*libs, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if qi.Dev != hi.Dev {
|
||||
if qi.Dev != mi.Dev {
|
||||
return nil, errors.New("quarantine directory must be on the same filesystem as the libraries")
|
||||
}
|
||||
|
||||
@@ -250,7 +250,7 @@ func (e *Engine) open(p store.Pair) (*libs, error) {
|
||||
if len(exts) == 0 {
|
||||
return nil, errors.New("no file extensions configured")
|
||||
}
|
||||
return &libs{pair: p, host: host, slave: slave, quarantine: q, dev: hi.Dev, exts: exts}, nil
|
||||
return &libs{pair: p, master: master, slave: slave, quarantine: q, dev: mi.Dev, exts: exts}, nil
|
||||
}
|
||||
|
||||
// NormalizePair fills defaults and validates a pair. It returns non-fatal
|
||||
@@ -260,7 +260,7 @@ func (e *Engine) NormalizePair(p *store.Pair) ([]string, error) {
|
||||
if p.Name == "" {
|
||||
return nil, errors.New("name is required")
|
||||
}
|
||||
p.HostRoot = filepath.Clean(strings.TrimSpace(p.HostRoot))
|
||||
p.MasterRoot = filepath.Clean(strings.TrimSpace(p.MasterRoot))
|
||||
p.SlaveRoot = filepath.Clean(strings.TrimSpace(p.SlaveRoot))
|
||||
if strings.TrimSpace(p.QuarantineDir) == "" {
|
||||
p.QuarantineDir = filepath.Join(p.SlaveRoot, QuarantineDirName)
|
||||
@@ -288,7 +288,7 @@ func (e *Engine) NormalizePair(p *store.Pair) ([]string, error) {
|
||||
return nil, err
|
||||
}
|
||||
// Store resolved paths so later containment checks compare like with like.
|
||||
p.HostRoot, p.SlaveRoot, p.QuarantineDir = l.host.Path, l.slave.Path, l.quarantine
|
||||
p.MasterRoot, p.SlaveRoot, p.QuarantineDir = l.master.Path, l.slave.Path, l.quarantine
|
||||
return layoutWarnings(l), nil
|
||||
}
|
||||
|
||||
@@ -312,20 +312,20 @@ func layoutWarnings(l *libs) []string {
|
||||
}
|
||||
return
|
||||
}
|
||||
hd, _ := list(l.host.Path)
|
||||
md, _ := list(l.master.Path)
|
||||
sd, sf := list(l.slave.Path)
|
||||
if len(sd) == 0 && sf == 0 {
|
||||
warns = append(warns, "The slave library is empty. Use Build to pick songs from the host library to hardlink into it.")
|
||||
warns = append(warns, "The slave library is empty. Use Build to pick songs from the master library to hardlink into it.")
|
||||
return warns
|
||||
}
|
||||
shared := 0
|
||||
for d := range sd {
|
||||
if hd[d] {
|
||||
if md[d] {
|
||||
shared++
|
||||
}
|
||||
}
|
||||
if len(sd) > 0 && shared*2 < len(sd) {
|
||||
warns = append(warns, fmt.Sprintf("Only %d of %d top-level folders in the slave library also exist in the host library. "+
|
||||
warns = append(warns, fmt.Sprintf("Only %d of %d top-level folders in the slave library also exist in the master library. "+
|
||||
"Both libraries should use the same folder layout, or most files will not match.", shared, len(sd)))
|
||||
}
|
||||
return warns
|
||||
|
||||
Reference in new issue
Block a user