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
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:
@@ -12,6 +12,7 @@ import (
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/dhowden/tag"
|
||||
@@ -87,7 +88,11 @@ type Stats struct {
|
||||
type Scanner struct {
|
||||
pool *pgxpool.Pool
|
||||
logger *slog.Logger
|
||||
paths []string
|
||||
// paths are the library's folders, changed by SetRoots (#6129). Read them
|
||||
// through Roots.
|
||||
paths []string
|
||||
rootsMu sync.RWMutex
|
||||
rootsChanged chan struct{}
|
||||
// fingerprint derives a file's acoustic identity (M400). A field so an
|
||||
// integration test can substitute a deterministic one: CI has no real audio
|
||||
// to fingerprint, and what the test pins is WHEN the scan fingerprints, not
|
||||
@@ -99,7 +104,8 @@ type Scanner struct {
|
||||
}
|
||||
|
||||
func New(pool *pgxpool.Pool, logger *slog.Logger, paths []string, settings *FingerprintSettingsService) *Scanner {
|
||||
return &Scanner{pool: pool, logger: logger, paths: paths, fingerprint: computeFingerprint, settings: settings}
|
||||
return &Scanner{pool: pool, logger: logger, paths: paths, rootsChanged: make(chan struct{}, 1),
|
||||
fingerprint: computeFingerprint, settings: settings}
|
||||
}
|
||||
|
||||
// Scan walks every configured root and upserts any audio file whose mtime is
|
||||
@@ -206,7 +212,7 @@ func (s *Scanner) enumerate(
|
||||
) ([]string, int) {
|
||||
paths := make([]string, 0, 8192)
|
||||
errs := 0
|
||||
for _, root := range s.paths {
|
||||
for _, root := range s.Roots() {
|
||||
// WalkDir's own error return is folded into the per-entry handler below,
|
||||
// so a bad root is counted rather than aborting the whole scan — one
|
||||
// unreadable root shouldn't discard the others' results.
|
||||
@@ -691,4 +697,11 @@ func IsAudioFile(path string) bool {
|
||||
}
|
||||
|
||||
// Roots are the folders the scanner reads: the library.
|
||||
func (s *Scanner) Roots() []string { return append([]string(nil), s.paths...) }
|
||||
func (s *Scanner) Roots() []string {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
s.rootsMu.RLock()
|
||||
defer s.rootsMu.RUnlock()
|
||||
return append([]string(nil), s.paths...)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user