M1/#293: library scanner (walk + tag-parse + upsert incremental) + admin scan endpoint #6
Reference in New Issue
Block a user
Delete Branch "dev"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Implements Fable task #293 — library scanner (spec §4 #1, §12).
internal/library/scanner.go:filepath.WalkDiroverlibrary.scan_paths; per-fileos.Stat→ mtime vstracks.updated_atskip;tag.ReadFrom(pure-Go, no cgo viagithub.com/dhowden/tag); resolve-or-create artist (GetArtistByName→UpsertArtist) and album (GetAlbumByArtistAndTitle→UpsertAlbum);UpsertTrack. Defaults:Unknown Artist/Unknown Album/ filename fallback.DurationMs=0pending ffprobe. Walk + per-file errors logged and counted; scan keeps going. Context-cancel viafs.SkipAll.internal/auth/middleware.go:RequireAdmin(pool)checksX-API-Token→GetUserByAPIToken→IsAdmin; 401 missing/invalid, 403 not admin, 500 lookup. ExposesUserFromContext.internal/server/server.go:ScanTriggerinterface for testability; mounts/api/adminwithRequireAdminandPOST /api/admin/scan(synchronous — small-library v1).internal/config/config.go:LibraryConfig{ScanPaths, ScanOnStartup};SMARTMUSIC_LIBRARY_SCAN_PATHS(colon-separated, empty entries dropped) +SMARTMUSIC_LIBRARY_SCAN_ON_STARTUPenv.cmd/minstrel/main.go: startup scan in goroutine whenscan_on_startup+ non-empty paths; scanner passed intoserver.New.internal/db/queries/albums.sql:GetAlbumByArtistAndTitlefor the no-mbid dedupe path (partial-unique mbid index doesn't dedupe NULL rows).internal/library/scanner_test.go: integration test gated onMINSTREL_TEST_DATABASE_URL; generates ID3v2.3 fixtures at test time (no binaries in git); asserts walk counts, article-strip sort (The Artist Y→ sortArtist Y), and incremental skip on re-scan.Test plan
go build ./...clean,go vet ./...clean,gofmt -lcleango test -short ./...passesMINSTREL_TEST_DATABASE_URLintegration run: scanner test green (first scan Added=4, second Skipped=4; artist sort[Artist X, Artist Y])/healthz200;/api/admin/scan— 401 (no token), 401 (bad token), 200 (admin token) with JSON statstest.ymlrun on this PR green:mainimage publishes after merge