feat: store and expose individual track artists

a
This commit is contained in:
sentriz
2023-10-28 18:27:17 +01:00
committed by Senan Kelly
parent 1a45356fa2
commit c1a34dc021
24 changed files with 176 additions and 64 deletions

View File

@@ -296,7 +296,7 @@ func (s *Scanner) scanDir(tx *db.DB, c *Context, absPath string) error {
sort.Strings(tracks)
for i, basename := range tracks {
absPath := filepath.Join(musicDir, relPath, basename)
if err := s.populateTrackAndAlbumArtists(tx, c, i, &album, basename, absPath); err != nil {
if err := s.populateTrackAndArtists(tx, c, i, &album, basename, absPath); err != nil {
return fmt.Errorf("populate track %q: %w", basename, err)
}
}
@@ -304,7 +304,7 @@ func (s *Scanner) scanDir(tx *db.DB, c *Context, absPath string) error {
return nil
}
func (s *Scanner) populateTrackAndAlbumArtists(tx *db.DB, c *Context, i int, album *db.Album, basename string, absPath string) error {
func (s *Scanner) populateTrackAndArtists(tx *db.DB, c *Context, i int, album *db.Album, basename string, absPath string) error {
stat, err := os.Stat(absPath)
if err != nil {
return fmt.Errorf("stating %q: %w", basename, err)
@@ -362,6 +362,19 @@ func (s *Scanner) populateTrackAndAlbumArtists(tx *db.DB, c *Context, i int, alb
return fmt.Errorf("populate track genres: %w", err)
}
trackArtistNames := parseMulti(trags, s.multiValueSettings[Artist], tagcommon.MustArtists, tagcommon.MustArtist)
var trackArtistIDs []int
for _, trackArtistName := range trackArtistNames {
trackArtist, err := populateArtist(tx, trackArtistName)
if err != nil {
return fmt.Errorf("populate track artist: %w", err)
}
trackArtistIDs = append(trackArtistIDs, trackArtist.ID)
}
if err := populateTrackArtists(tx, &track, trackArtistIDs); err != nil {
return fmt.Errorf("populate track artists: %w", err)
}
c.seenTracks[track.ID] = struct{}{}
c.seenTracksNew++
@@ -498,6 +511,17 @@ func populateAlbumArtists(tx *db.DB, album *db.Album, albumArtistIDs []int) erro
return nil
}
func populateTrackArtists(tx *db.DB, track *db.Track, trackArtistIDs []int) error {
if err := tx.Where("track_id=?", track.ID).Delete(db.TrackArtist{}).Error; err != nil {
return fmt.Errorf("delete old track artists: %w", err)
}
if err := tx.InsertBulkLeftMany("track_artists", []string{"track_id", "artist_id"}, track.ID, trackArtistIDs); err != nil {
return fmt.Errorf("insert bulk track artists: %w", err)
}
return nil
}
func (s *Scanner) cleanTracks(c *Context) error {
start := time.Now()
defer func() { log.Printf("finished clean tracks in %s, %d removed", durSince(start), c.TracksMissing()) }()
@@ -546,15 +570,15 @@ func (s *Scanner) cleanArtists(c *Context) error {
start := time.Now()
defer func() { log.Printf("finished clean artists in %s, %d removed", durSince(start), c.ArtistsMissing()) }()
sub := s.db.
Select("artists.id").
Model(&db.Artist{}).
Joins("LEFT JOIN album_artists ON album_artists.artist_id=artists.id").
Where("album_artists.artist_id IS NULL").
SubQuery()
q := s.db.
Where("artists.id IN ?", sub).
Delete(&db.Artist{})
// gorm doesn't seem to support subqueries without parens for UNION
q := s.db.Exec(`
DELETE FROM artists
WHERE id NOT IN (
SELECT artist_id FROM track_artists
UNION
SELECT artist_id FROM album_artists
)
`)
if err := q.Error; err != nil {
return err
}
@@ -654,6 +678,7 @@ type Tag uint8
const (
Genre Tag = iota
Artist
AlbumArtist
)

View File

@@ -562,7 +562,7 @@ func TestCompilationAlbumWithoutAlbumArtist(t *testing.T) {
assert.Equal(t, 5, trackCount)
var artists []*db.Artist
assert.NoError(t, m.DB().Find(&artists).Error)
assert.NoError(t, m.DB().Joins("JOIN album_artists ON album_artists.artist_id=artists.id").Group("artists.id").Find(&artists).Error)
assert.Equal(t, 1, len(artists)) // we only have one album artist
assert.Equal(t, "artist 0", artists[0].Name) // it came from the first track's fallback to artist tag
@@ -656,7 +656,7 @@ func TestMultiArtistSupport(t *testing.T) {
m.ScanAndClean()
var artists []*db.Artist
assert.NoError(t, m.DB().Find(&artists).Error)
assert.NoError(t, m.DB().Joins("JOIN album_artists ON album_artists.artist_id=artists.id").Group("artists.id").Find(&artists).Error)
assert.Len(t, artists, 3) // alan, liz, mercury
var albumArtists []*db.AlbumArtist
@@ -695,7 +695,7 @@ func TestMultiArtistSupport(t *testing.T) {
m.ScanAndClean()
assert.NoError(t, m.DB().Find(&artists).Error)
assert.NoError(t, m.DB().Joins("JOIN album_artists ON album_artists.artist_id=artists.id").Group("artists.id").Find(&artists).Error)
assert.Len(t, artists, 2) // alan, liz
assert.NoError(t, m.DB().Find(&albumArtists).Error)
@@ -745,7 +745,7 @@ func TestMultiArtistPreload(t *testing.T) {
}
var artists []*db.Artist
assert.NoError(t, m.DB().Preload("Albums").Find(&artists).Error)
assert.NoError(t, m.DB().Preload("Albums").Joins("JOIN album_artists ON album_artists.artist_id=artists.id").Group("artists.id").Find(&artists).Error)
assert.Equal(t, 3, len(artists))
for _, artist := range artists {

File diff suppressed because one or more lines are too long

View File

@@ -1,3 +0,0 @@
go test fuzz v1
[]byte("001")
int64(3472329395739373616)

View File

@@ -1,3 +0,0 @@
go test fuzz v1
[]byte("001")
int64(3472329395739373616)

View File

@@ -1,3 +0,0 @@
go test fuzz v1
[]byte("")
int64(0)