feat: Library Only mode — toggle, search, artist page, similar artists

Backend:
- Migration 17: similar_artist_map table stores per-artist similar
  artist relationships (source_mbid → similar_mbid + name + score)
- Tier 4 index build now persists similar artists to this table
- GetLibrarySimilarArtists(mbid) queries similar artists filtered
  by JOIN with the artists table (library-only, no API calls)
- Added db field to explore.Service for direct queries

Frontend:
- ExploreSettingsStore with libraryOnly toggle, persisted to
  localStorage
- Top bar toggle button with active/inactive styling
- Explore search: skips full MB/LB pipeline when library-only,
  uses only searchLibraryCache (pure JS, instant)
- Artist detail page: in library-only mode, skips all API calls
  (no top tracks, no top releases, no LB play count, no MB
  artist lookup). Uses library store for discography, calls
  GetLibrarySimilarArtists for similar artists.
- Similar artists section: changed from horizontal scroll to
  wrapping flex layout with collapsible toggle (Show all N)
- Removed debug artist ranking log
This commit is contained in:
2026-03-30 15:36:37 -04:00
parent 6440b0333c
commit d73226b173
11 changed files with 278 additions and 24 deletions
+45
View File
@@ -409,6 +409,14 @@ func runMigrations(
}
}
if version < 17 { //nolint:mnd
if err := migration17SimilarArtistMap(
ctx, db, logger,
); err != nil {
return err
}
}
return nil
}
@@ -1833,6 +1841,43 @@ func migration16ArtistImages(
return nil
}
func migration17SimilarArtistMap(
ctx context.Context,
db *sql.DB,
logger *slog.Logger,
) error {
logger.Info("applying migration 17: similar_artist_map table")
if _, err := db.ExecContext(ctx, `
CREATE TABLE IF NOT EXISTS similar_artist_map (
source_artist_mbid TEXT NOT NULL,
similar_artist_mbid TEXT NOT NULL,
similar_artist_name TEXT NOT NULL,
score INTEGER NOT NULL DEFAULT 0,
PRIMARY KEY (source_artist_mbid, similar_artist_mbid)
)
`); err != nil {
return fmt.Errorf("migration 17: create similar_artist_map: %w", err)
}
if _, err := db.ExecContext(ctx, `
CREATE INDEX IF NOT EXISTS idx_similar_artist_map_source
ON similar_artist_map(source_artist_mbid)
`); err != nil {
return fmt.Errorf("migration 17: create source index: %w", err)
}
if _, err := db.ExecContext(ctx,
"PRAGMA user_version = 17",
); err != nil {
return fmt.Errorf("could not set user_version to 17: %w", err)
}
logger.Info("migration 17 complete")
return nil
}
// readLibraryDirFromTOML reads the TOML config file and returns
// the Library.DirectoryPath value, or "" if not configured.
func readLibraryDirFromTOML(logger *slog.Logger) string {