feat: MBIDs in library models + local-first search + explore cache

Backend:
- Added mbid column to sqlc schemas for artists and release_groups
- Regenerated sqlc queries to SELECT mbid in artist/album queries
- Added MBID field to library.Artist and library.Album Go structs
- All GetAllArtists/GetAllAlbums variants now populate MBID

Frontend:
- Updated Wails models.ts with MBID fields on Artist and Album
- Added cachedArtists/cachedAlbums getters to LibraryStore
- searchLibraryCache now includes MBIDs and local cover art URLs
  so library results can navigate to explore detail pages
- Added mergeWithLibrary() — when full MB results arrive, library
  entries are enriched with local images and 'In Library' flags
  rather than being replaced by MB-only versions
- Created ExploreCache store for cross-page data sharing: search
  results populate the cache, detail pages can read from it to
  avoid redundant API calls for already-fetched data
This commit is contained in:
2026-03-29 18:54:22 -04:00
parent 1999fdb0f4
commit 8096b28d17
13 changed files with 255 additions and 41 deletions
+14 -14
View File
@@ -11,13 +11,13 @@ import (
const createArtist = `-- name: CreateArtist :one
INSERT INTO artists (name) VALUES (?)
RETURNING id, name
RETURNING id, name, mbid
`
func (q *Queries) CreateArtist(ctx context.Context, name string) (Artist, error) {
row := q.db.QueryRowContext(ctx, createArtist, name)
var i Artist
err := row.Scan(&i.ID, &i.Name)
err := row.Scan(&i.ID, &i.Name, &i.Mbid)
return i, err
}
@@ -41,7 +41,7 @@ func (q *Queries) DeleteArtist(ctx context.Context, id int64) error {
}
const getAlbumArtists = `-- name: GetAlbumArtists :many
SELECT DISTINCT a.id, a.name
SELECT DISTINCT a.id, a.name, a.mbid
FROM artists a
JOIN artist_credit_artist aca ON aca.artist_id = a.id
JOIN artist_credit ac ON ac.id = aca.credit_id
@@ -58,7 +58,7 @@ func (q *Queries) GetAlbumArtists(ctx context.Context) ([]Artist, error) {
var items []Artist
for rows.Next() {
var i Artist
if err := rows.Scan(&i.ID, &i.Name); err != nil {
if err := rows.Scan(&i.ID, &i.Name, &i.Mbid); err != nil {
return nil, err
}
items = append(items, i)
@@ -73,7 +73,7 @@ func (q *Queries) GetAlbumArtists(ctx context.Context) ([]Artist, error) {
}
const getAlbumArtistsByLibrary = `-- name: GetAlbumArtistsByLibrary :many
SELECT DISTINCT a.id, a.name
SELECT DISTINCT a.id, a.name, a.mbid
FROM artists a
JOIN artist_credit_artist aca ON aca.artist_id = a.id
JOIN artist_credit ac ON ac.id = aca.credit_id
@@ -100,7 +100,7 @@ func (q *Queries) GetAlbumArtistsByLibrary(ctx context.Context, libraryID int64)
var items []Artist
for rows.Next() {
var i Artist
if err := rows.Scan(&i.ID, &i.Name); err != nil {
if err := rows.Scan(&i.ID, &i.Name, &i.Mbid); err != nil {
return nil, err
}
items = append(items, i)
@@ -115,7 +115,7 @@ func (q *Queries) GetAlbumArtistsByLibrary(ctx context.Context, libraryID int64)
}
const getAllArtists = `-- name: GetAllArtists :many
SELECT id, name FROM artists
SELECT id, name, mbid FROM artists
ORDER BY name
`
@@ -128,7 +128,7 @@ func (q *Queries) GetAllArtists(ctx context.Context) ([]Artist, error) {
var items []Artist
for rows.Next() {
var i Artist
if err := rows.Scan(&i.ID, &i.Name); err != nil {
if err := rows.Scan(&i.ID, &i.Name, &i.Mbid); err != nil {
return nil, err
}
items = append(items, i)
@@ -143,26 +143,26 @@ func (q *Queries) GetAllArtists(ctx context.Context) ([]Artist, error) {
}
const getArtist = `-- name: GetArtist :one
SELECT id, name FROM artists
SELECT id, name, mbid FROM artists
WHERE id = ? LIMIT 1
`
func (q *Queries) GetArtist(ctx context.Context, id int64) (Artist, error) {
row := q.db.QueryRowContext(ctx, getArtist, id)
var i Artist
err := row.Scan(&i.ID, &i.Name)
err := row.Scan(&i.ID, &i.Name, &i.Mbid)
return i, err
}
const getArtistByName = `-- name: GetArtistByName :one
SELECT id, name FROM artists
SELECT id, name, mbid FROM artists
WHERE name = ? LIMIT 1
`
func (q *Queries) GetArtistByName(ctx context.Context, name string) (Artist, error) {
row := q.db.QueryRowContext(ctx, getArtistByName, name)
var i Artist
err := row.Scan(&i.ID, &i.Name)
err := row.Scan(&i.ID, &i.Name, &i.Mbid)
return i, err
}
@@ -185,12 +185,12 @@ func (q *Queries) UpdateArtist(ctx context.Context, arg UpdateArtistParams) erro
const upsertArtist = `-- name: UpsertArtist :one
INSERT INTO artists (name) VALUES (?)
ON CONFLICT(name) DO UPDATE SET name = excluded.name
RETURNING id, name
RETURNING id, name, mbid
`
func (q *Queries) UpsertArtist(ctx context.Context, name string) (Artist, error) {
row := q.db.QueryRowContext(ctx, upsertArtist, name)
var i Artist
err := row.Scan(&i.ID, &i.Name)
err := row.Scan(&i.ID, &i.Name, &i.Mbid)
return i, err
}
+2
View File
@@ -12,6 +12,7 @@ import (
type Artist struct {
ID int64
Name string
Mbid sql.NullString
}
type ArtistCredit struct {
@@ -154,6 +155,7 @@ type ReleaseGroup struct {
Year sql.NullInt64
TotalTracks sql.NullInt64
TotalDiscs sql.NullInt64
Mbid sql.NullString
}
type ReleaseGroupRecording struct {
@@ -23,7 +23,7 @@ func (q *Queries) CountReleaseGroupRecordings(ctx context.Context, releaseGroupI
const createReleaseGroup = `-- name: CreateReleaseGroup :one
INSERT INTO release_groups (name) VALUES (?)
RETURNING id, name, cover_art_id, album_artist_credit_id, year, total_tracks, total_discs
RETURNING id, name, cover_art_id, album_artist_credit_id, year, total_tracks, total_discs, mbid
`
func (q *Queries) CreateReleaseGroup(ctx context.Context, name string) (ReleaseGroup, error) {
@@ -37,6 +37,7 @@ func (q *Queries) CreateReleaseGroup(ctx context.Context, name string) (ReleaseG
&i.Year,
&i.TotalTracks,
&i.TotalDiscs,
&i.Mbid,
)
return i, err
}
@@ -45,7 +46,7 @@ const createReleaseGroupFull = `-- name: CreateReleaseGroupFull :one
INSERT INTO release_groups (
name, cover_art_id, album_artist_credit_id, year, total_tracks, total_discs
) VALUES (?, ?, ?, ?, ?, ?)
RETURNING id, name, cover_art_id, album_artist_credit_id, year, total_tracks, total_discs
RETURNING id, name, cover_art_id, album_artist_credit_id, year, total_tracks, total_discs, mbid
`
type CreateReleaseGroupFullParams struct {
@@ -75,6 +76,7 @@ func (q *Queries) CreateReleaseGroupFull(ctx context.Context, arg CreateReleaseG
&i.Year,
&i.TotalTracks,
&i.TotalDiscs,
&i.Mbid,
)
return i, err
}
@@ -233,6 +235,7 @@ SELECT
rg.id,
rg.name,
rg.year,
rg.mbid,
COALESCE(ac.text, fallback_ac.text, '') as artist_name,
COALESCE(ca.file_path, '') as cover_art_path
FROM release_groups rg
@@ -252,6 +255,7 @@ type GetAllAlbumsWithDetailsRow struct {
ID int64
Name string
Year sql.NullInt64
Mbid sql.NullString
ArtistName string
CoverArtPath string
}
@@ -269,6 +273,7 @@ func (q *Queries) GetAllAlbumsWithDetails(ctx context.Context) ([]GetAllAlbumsWi
&i.ID,
&i.Name,
&i.Year,
&i.Mbid,
&i.ArtistName,
&i.CoverArtPath,
); err != nil {
@@ -290,6 +295,7 @@ SELECT
rg.id,
rg.name,
rg.year,
rg.mbid,
COALESCE(ac.text, fallback_ac.text, '') as artist_name,
COALESCE(ca.file_path, '') as cover_art_path
FROM release_groups rg
@@ -316,6 +322,7 @@ type GetAllAlbumsWithDetailsByLibraryRow struct {
ID int64
Name string
Year sql.NullInt64
Mbid sql.NullString
ArtistName string
CoverArtPath string
}
@@ -333,6 +340,7 @@ func (q *Queries) GetAllAlbumsWithDetailsByLibrary(ctx context.Context, libraryI
&i.ID,
&i.Name,
&i.Year,
&i.Mbid,
&i.ArtistName,
&i.CoverArtPath,
); err != nil {
@@ -350,7 +358,7 @@ func (q *Queries) GetAllAlbumsWithDetailsByLibrary(ctx context.Context, libraryI
}
const getAllReleaseGroups = `-- name: GetAllReleaseGroups :many
SELECT id, name, cover_art_id, album_artist_credit_id, year, total_tracks, total_discs FROM release_groups
SELECT id, name, cover_art_id, album_artist_credit_id, year, total_tracks, total_discs, mbid FROM release_groups
ORDER BY name
`
@@ -371,6 +379,7 @@ func (q *Queries) GetAllReleaseGroups(ctx context.Context) ([]ReleaseGroup, erro
&i.Year,
&i.TotalTracks,
&i.TotalDiscs,
&i.Mbid,
); err != nil {
return nil, err
}
@@ -386,7 +395,7 @@ func (q *Queries) GetAllReleaseGroups(ctx context.Context) ([]ReleaseGroup, erro
}
const getReleaseGroup = `-- name: GetReleaseGroup :one
SELECT id, name, cover_art_id, album_artist_credit_id, year, total_tracks, total_discs FROM release_groups
SELECT id, name, cover_art_id, album_artist_credit_id, year, total_tracks, total_discs, mbid FROM release_groups
WHERE id = ? LIMIT 1
`
@@ -401,12 +410,13 @@ func (q *Queries) GetReleaseGroup(ctx context.Context, id int64) (ReleaseGroup,
&i.Year,
&i.TotalTracks,
&i.TotalDiscs,
&i.Mbid,
)
return i, err
}
const getReleaseGroupByNameAndArtist = `-- name: GetReleaseGroupByNameAndArtist :one
SELECT id, name, cover_art_id, album_artist_credit_id, year, total_tracks, total_discs FROM release_groups
SELECT id, name, cover_art_id, album_artist_credit_id, year, total_tracks, total_discs, mbid FROM release_groups
WHERE name = ? AND album_artist_credit_id = ? LIMIT 1
`
@@ -426,6 +436,7 @@ func (q *Queries) GetReleaseGroupByNameAndArtist(ctx context.Context, arg GetRel
&i.Year,
&i.TotalTracks,
&i.TotalDiscs,
&i.Mbid,
)
return i, err
}
@@ -468,7 +479,7 @@ VALUES (?, ?, ?)
ON CONFLICT(name, album_artist_credit_id) DO UPDATE SET
album_artist_credit_id = COALESCE(excluded.album_artist_credit_id, release_groups.album_artist_credit_id),
year = COALESCE(excluded.year, release_groups.year)
RETURNING id, name, cover_art_id, album_artist_credit_id, year, total_tracks, total_discs
RETURNING id, name, cover_art_id, album_artist_credit_id, year, total_tracks, total_discs, mbid
`
type UpsertReleaseGroupParams struct {
@@ -488,6 +499,7 @@ func (q *Queries) UpsertReleaseGroup(ctx context.Context, arg UpsertReleaseGroup
&i.Year,
&i.TotalTracks,
&i.TotalDiscs,
&i.Mbid,
)
return i, err
}