From 1250647dc3ba07b274501e5d84d7f04dc8095f35 Mon Sep 17 00:00:00 2001 From: Deluan Date: Mon, 10 Nov 2025 19:47:20 -0500 Subject: [PATCH] refactor(folder): enhance external scanner to support target-specific scanning Signed-off-by: Deluan --- core/maintenance_test.go | 4 +- scanner/external.go | 81 +++++++++++++++------------------------- 2 files changed, 33 insertions(+), 52 deletions(-) diff --git a/core/maintenance_test.go b/core/maintenance_test.go index 8e8796ffa..e83d1f8bd 100644 --- a/core/maintenance_test.go +++ b/core/maintenance_test.go @@ -373,10 +373,10 @@ type extendedDataStore struct { gcError error } -func (ds *extendedDataStore) GC(ctx context.Context) error { +func (ds *extendedDataStore) GC(ctx context.Context, libraryIDs ...int) error { ds.gcCalled = true if ds.gcError != nil { return ds.gcError } - return ds.MockDataStore.GC(ctx) + return ds.MockDataStore.GC(ctx, libraryIDs...) } diff --git a/scanner/external.go b/scanner/external.go index 86d171346..869034cc2 100644 --- a/scanner/external.go +++ b/scanner/external.go @@ -12,7 +12,6 @@ import ( "github.com/navidrome/navidrome/conf" "github.com/navidrome/navidrome/log" - . "github.com/navidrome/navidrome/utils/gg" ) // scannerExternal is a scanner that runs an external process to do the scanning. It is used to avoid @@ -25,68 +24,50 @@ import ( type scannerExternal struct{} func (s *scannerExternal) scanAll(ctx context.Context, fullScan bool, progress chan<- *ProgressInfo) { - exe, err := os.Executable() - if err != nil { - progress <- &ProgressInfo{Error: fmt.Sprintf("failed to get executable path: %s", err)} - return - } - log.Debug(ctx, "Spawning external scanner process", "fullScan", fullScan, "path", exe) - cmd := exec.CommandContext(ctx, exe, "scan", - "--nobanner", "--subprocess", - "--configfile", conf.Server.ConfigFile, - "--datafolder", conf.Server.DataFolder, - "--cachefolder", conf.Server.CacheFolder, - If(fullScan, "--full", "")) - - in, out := io.Pipe() - defer in.Close() - defer out.Close() - cmd.Stdout = out - cmd.Stderr = os.Stderr - - if err := cmd.Start(); err != nil { - progress <- &ProgressInfo{Error: fmt.Sprintf("failed to start scanner process: %s", err)} - return - } - go s.wait(cmd, out) - - decoder := gob.NewDecoder(in) - for { - var p ProgressInfo - if err := decoder.Decode(&p); err != nil { - if !errors.Is(err, io.EOF) { - progress <- &ProgressInfo{Error: fmt.Sprintf("failed to read status from scanner: %s", err)} - } - break - } - progress <- &p - } + s.scan(ctx, fullScan, nil, progress) } func (s *scannerExternal) scanFolders(ctx context.Context, fullScan bool, targets []ScanTarget, progress chan<- *ProgressInfo) { + s.scan(ctx, fullScan, targets, progress) +} + +func (s *scannerExternal) scan(ctx context.Context, fullScan bool, targets []ScanTarget, progress chan<- *ProgressInfo) { exe, err := os.Executable() if err != nil { progress <- &ProgressInfo{Error: fmt.Sprintf("failed to get executable path: %s", err)} return } - // Build targets string for CLI - var targetsStr string - for i, target := range targets { - if i > 0 { - targetsStr += "," - } - targetsStr += strconv.Itoa(target.LibraryID) + ":" + target.FolderPath - } - - log.Debug(ctx, "Spawning external scanner process with targets", "fullScan", fullScan, "path", exe, "targets", targetsStr) - cmd := exec.CommandContext(ctx, exe, "scan", + // Build command arguments + args := []string{ + "scan", "--nobanner", "--subprocess", "--configfile", conf.Server.ConfigFile, "--datafolder", conf.Server.DataFolder, "--cachefolder", conf.Server.CacheFolder, - "--targets", targetsStr, - If(fullScan, "--full", "")) + } + + // Add targets if provided + if len(targets) > 0 { + var targetsStr string + for i, target := range targets { + if i > 0 { + targetsStr += "," + } + targetsStr += strconv.Itoa(target.LibraryID) + ":" + target.FolderPath + } + args = append(args, "--targets", targetsStr) + log.Debug(ctx, "Spawning external scanner process with targets", "fullScan", fullScan, "path", exe, "targets", targetsStr) + } else { + log.Debug(ctx, "Spawning external scanner process", "fullScan", fullScan, "path", exe) + } + + // Add full scan flag if needed + if fullScan { + args = append(args, "--full") + } + + cmd := exec.CommandContext(ctx, exe, args...) in, out := io.Pipe() defer in.Close()