Add optional MusicBrainz/Cover Art Archive metadata enrichment
getArtistInfo/2 and getAlbumInfo/2 now return a real MusicBrainz disambiguation/cover-art lookup for entities that carry a locally tagged MusicBrainz ID, gated behind the new VIBESONIC_ENABLE_EXTERNAL_METADATA flag (default off). Responses are cached in the existing external_metadata_cache table for 30 days to avoid hammering the free MusicBrainz/CAA services. VibeSonic still works completely offline: with the flag off, or when an entity has no tagged MusicBrainz ID, or on any network/lookup failure, the handlers fall back to local-only fields exactly as before — nothing is ever fabricated, and a failed external lookup never fails the request. internal/metadataenrich is a small, independently-testable HTTP client for the two real APIs (base URLs overridable for tests, exercised against httptest servers rather than the live network). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014r98AtcBbevpscWLsfwVvv
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"time"
|
||||
|
||||
"git.noroutes.net/NoRoutes/vibetunes/internal/idgen"
|
||||
)
|
||||
|
||||
// GetCachedExternalMetadata returns a previously fetched MusicBrainz/
|
||||
// Cover Art Archive response for (entityType, mbid, source), if any.
|
||||
// found is false when nothing has been cached yet — callers decide
|
||||
// whether/how to refresh, this layer never guesses an age-based expiry.
|
||||
func GetCachedExternalMetadata(db *sql.DB, entityType, mbid, source string) (dataJSON string, fetchedAt time.Time, found bool, err error) {
|
||||
var fetchedAtStr string
|
||||
err = db.QueryRow(`SELECT data_json, fetched_at FROM external_metadata_cache WHERE entity_type = ? AND mbid = ? AND source = ?`,
|
||||
entityType, mbid, source).Scan(&dataJSON, &fetchedAtStr)
|
||||
if err == sql.ErrNoRows {
|
||||
return "", time.Time{}, false, nil
|
||||
}
|
||||
if err != nil {
|
||||
return "", time.Time{}, false, err
|
||||
}
|
||||
fetchedAt, _ = time.Parse("2006-01-02T15:04:05.000Z", fetchedAtStr)
|
||||
return dataJSON, fetchedAt, true, nil
|
||||
}
|
||||
|
||||
// PutCachedExternalMetadata stores (or replaces) the cached response
|
||||
// for (entityType, mbid, source).
|
||||
func PutCachedExternalMetadata(db *sql.DB, entityType, mbid, source, dataJSON string) error {
|
||||
_, err := db.Exec(`
|
||||
INSERT INTO external_metadata_cache (id, entity_type, mbid, source, data_json, fetched_at)
|
||||
VALUES (?, ?, ?, ?, ?, strftime('%Y-%m-%dT%H:%M:%fZ','now'))
|
||||
ON CONFLICT(entity_type, mbid, source) DO UPDATE SET
|
||||
data_json = excluded.data_json,
|
||||
fetched_at = excluded.fetched_at`,
|
||||
idgen.New("emc"), entityType, mbid, source, dataJSON)
|
||||
return err
|
||||
}
|
||||
Reference in New Issue
Block a user