feat: store and expose individual track artists
a
This commit is contained in:
@@ -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
|
||||
)
|
||||
|
||||
|
||||
@@ -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
@@ -1,3 +0,0 @@
|
||||
go test fuzz v1
|
||||
[]byte("001")
|
||||
int64(3472329395739373616)
|
||||
@@ -1,3 +0,0 @@
|
||||
go test fuzz v1
|
||||
[]byte("001")
|
||||
int64(3472329395739373616)
|
||||
@@ -1,3 +0,0 @@
|
||||
go test fuzz v1
|
||||
[]byte("")
|
||||
int64(0)
|
||||
Reference in New Issue
Block a user