feat: multi-source artist images with thumbnails + grid integration
Complete rewrite of the artist image pipeline:
STORAGE:
- Migration 16: artist_images table tracking source, URL, path,
primary flag, dimensions per image (up to 10 per artist)
- Directory structure: artist-images/{mbid[:2]}/{mbid}/ with
primary.jpg + primary_sm.jpg/_md.jpg/_lg.jpg thumbnails
- Miss marker (.miss file) prevents re-fetching artists with no image
SOURCES (priority order):
1. MusicBrainz direct image relations (Wikimedia Commons)
2. Wikidata P18 property (Wikimedia Commons)
3. Wikipedia lead image (NEW — via Wikidata sitelinks → Wikipedia API)
Each source is checked, deduplicated, and the first available
image becomes the primary with sm/md/lg thumbnail generation
(100px/200px/400px, matching cover art tier sizes).
ASSET SERVING:
- /artist-images/ path registered with Wails asset handler
- Serves files via http.FileServer from the artist-images directory
- Same pattern as /covers/ for cover art
ARTIST MODEL:
- Artist struct gains ImageSmall/ImageMedium/ImageLarge fields
- resolveArtistImages does bulk MBID lookup → disk stat for each
- Populated in GetAllArtists and GetAllArtistsByLibrary
GRID VIEW:
- artists-view uses model URLs directly (no more base64 data URLs)
- Size selection based on imageSize * devicePixelRatio (like cover-grid)
- Removed batch GetArtistImages call and in-memory cache — no longer needed
This commit is contained in:
@@ -4,12 +4,15 @@ import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"yellowjacket/backend/coverart"
|
||||
"yellowjacket/backend/database/sql/sqlcgen"
|
||||
"yellowjacket/backend/system"
|
||||
)
|
||||
|
||||
// Sentinel errors for library queries.
|
||||
@@ -142,8 +145,11 @@ func (l *Library) GetTrackMBIDs(filePath string) TrackMBIDs {
|
||||
|
||||
// Artist represents an artist in the library.
|
||||
type Artist struct {
|
||||
ID int64
|
||||
Name string
|
||||
ID int64
|
||||
Name string
|
||||
ImageSmall string
|
||||
ImageMedium string
|
||||
ImageLarge string
|
||||
}
|
||||
|
||||
// Album represents an album for the cover grid display.
|
||||
@@ -366,9 +372,69 @@ func (l *Library) GetAllArtists() ([]Artist, error) {
|
||||
})
|
||||
}
|
||||
|
||||
// Resolve artist image URLs from the disk cache.
|
||||
l.resolveArtistImages(artists)
|
||||
|
||||
return artists, nil
|
||||
}
|
||||
|
||||
// resolveArtistImages populates ImageSmall/Medium/Large for artists
|
||||
// that have cached images on disk. Does a bulk MBID lookup from the
|
||||
// artists table, then checks the artist-images directory for each.
|
||||
func (l *Library) resolveArtistImages(artists []Artist) {
|
||||
if len(artists) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
dataDir, err := system.GetUserDataDirPath()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
baseDir := filepath.Join(dataDir, "artist-images")
|
||||
|
||||
// Bulk load name→mbid from the artists table.
|
||||
rows, err := l.db.QueryContext(
|
||||
"SELECT name, mbid FROM artists WHERE mbid IS NOT NULL AND mbid != ''",
|
||||
)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
defer func() { _ = rows.Close() }()
|
||||
|
||||
mbidMap := make(map[string]string)
|
||||
|
||||
for rows.Next() {
|
||||
var name, mbid string
|
||||
if err := rows.Scan(&name, &mbid); err == nil {
|
||||
mbidMap[name] = mbid
|
||||
}
|
||||
}
|
||||
|
||||
for i := range artists {
|
||||
mbid, ok := mbidMap[artists[i].Name]
|
||||
if !ok || len(mbid) < 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
dir := filepath.Join(baseDir, mbid[:2], mbid)
|
||||
prefix := "/artist-images/" + mbid[:2] + "/" + mbid + "/"
|
||||
|
||||
if _, err := os.Stat(filepath.Join(dir, "primary_sm.jpg")); err == nil {
|
||||
artists[i].ImageSmall = prefix + "primary_sm.jpg"
|
||||
}
|
||||
|
||||
if _, err := os.Stat(filepath.Join(dir, "primary_md.jpg")); err == nil {
|
||||
artists[i].ImageMedium = prefix + "primary_md.jpg"
|
||||
}
|
||||
|
||||
if _, err := os.Stat(filepath.Join(dir, "primary_lg.jpg")); err == nil {
|
||||
artists[i].ImageLarge = prefix + "primary_lg.jpg"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetAlbumsByArtist returns all albums where the given artist is the album artist.
|
||||
func (l *Library) GetAlbumsByArtist(
|
||||
artistID int64,
|
||||
@@ -646,6 +712,8 @@ func (l *Library) GetAllArtistsByLibrary(
|
||||
})
|
||||
}
|
||||
|
||||
l.resolveArtistImages(artists)
|
||||
|
||||
return artists, nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user