feat: the library's folders are set in Admin › Library, and changing a folder moves its tracks with it, keeping their plays, likes and playlists (#6129)
release / govulncheck (push) Successful in 15s
release / web (push) Successful in 1m20s
release / go (push) Failing after 1m37s
release / integration (push) Failing after 4m56s
release / android (push) Successful in 5m1s
release / Build signed APK (releases and dev) (push) Successful in 5m13s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Build + push container image (push) Skipped
release / Verify release artifacts (tag releases only) (push) Skipped

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-19 20:37:03 -04:00
co-authored by Claude Opus 5.5
parent 453a977e48
commit edcb3a57ef
25 changed files with 1102 additions and 57 deletions
+16 -5
View File
@@ -9,6 +9,7 @@ import (
"net/http"
"os"
"os/signal"
"slices"
"syscall"
"time"
@@ -135,7 +136,18 @@ func run() error {
if fpErr != nil {
logger.Warn("fingerprint settings: using defaults", "err", fpErr)
}
scanner := library.New(pool, logger, cfg.Library.ScanPaths, fpSettings)
// The library's folders (#6129): the ones saved in Settings, or
// MINSTREL_LIBRARY_SCAN_PATHS until any are. A load failure falls back to
// env, which is what the server read before the setting existed.
roots, rootsSaved, rootsErr := library.LoadRoots(ctx, pool, cfg.Library.ScanPaths)
switch {
case rootsErr != nil:
logger.Warn("library folders: using MINSTREL_LIBRARY_SCAN_PATHS", "err", rootsErr)
case rootsSaved && len(cfg.Library.ScanPaths) > 0 && !slices.Equal(roots, cfg.Library.ScanPaths):
logger.Warn("library folders: the ones saved in Settings are used, not MINSTREL_LIBRARY_SCAN_PATHS",
"settings", roots, "env", cfg.Library.ScanPaths)
}
scanner := library.New(pool, logger, roots, fpSettings)
// Loudness analysis settings (M464 #4995): shared by the loudness backfill
// and the admin API, and falls back to the defaults like the above.
@@ -195,7 +207,7 @@ func run() error {
// One unified scan chain: library walk → MBID backfill → cover enrich.
// Boot-time scan and manual-trigger scans share this path; results land
// in scan_runs for the admin overview.
if cfg.Library.ScanOnStartup && len(cfg.Library.ScanPaths) > 0 {
if cfg.Library.ScanOnStartup && len(roots) > 0 {
go func() {
if _, err := library.RunScan(ctx, pool, scanner, coverEnricher,
logger.With("component", "scan_run"),
@@ -340,7 +352,7 @@ func run() error {
if acoustIDSettings != nil {
printer = &importer.AcoustIDPrinter{Settings: acoustIDSettings, Client: acoustIDClient}
}
imp := importer.New(pool, scanner, cat, printer, cfg.Library.ScanPaths, logger.With("component", "import"))
imp := importer.New(pool, scanner, cat, printer, scanner.Roots, logger.With("component", "import"))
imp.DataDir = cfg.Storage.DataDir
go imp.Run(ctx)
@@ -431,8 +443,7 @@ func run() error {
// Filesystem watcher: near-instant pickup of new/changed files via
// fsnotify, scanning just the affected paths + enriching their albums
// inline. Replaces the removed configurable scan scheduler.
watcher := library.NewWatcher(scanner, coverEnricher,
logger.With("component", "watcher"), cfg.Library.ScanPaths)
watcher := library.NewWatcher(scanner, coverEnricher, logger.With("component", "watcher"))
go func() {
if werr := watcher.Run(ctx); werr != nil {
logger.Warn("library watcher exited", "err", werr)