Skip to content

Commit

Permalink
feat: store and expose individual track artists
Browse files Browse the repository at this point in the history
  • Loading branch information
sentriz committed Oct 28, 2023
1 parent eac4db8 commit 74f58b8
Show file tree
Hide file tree
Showing 23 changed files with 158 additions and 51 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ password can then be changed from the web interface
| `GONIC_PODCAST_PURGE_AGE` | `-podcast-purge-age` | **optional** age (in days) to purge podcast episodes if not accessed |
| `GONIC_EXCLUDE_PATTERN` | `-exclude-pattern` | **optional** files matching this regex pattern will not be imported |
| `GONIC_MULTI_VALUE_GENRE` | `-multi-value-genre` | **optional** setting for multi-valued genre tags when scanning ([see more](#multi-valued-tags)) |
| `GONIC_MULTI_VALUE_ARTIST` | `-multi-value-artist` | **optional** setting for multi-valued artist tags when scanning ([see more](#multi-valued-tags)) |
| `GONIC_MULTI_VALUE_ALBUM_ARTIST` | `-multi-value-album-artist` | **optional** setting for multi-valued album artist tags when scanning ([see more](#multi-valued-tags)) |
| `GONIC_EXPVAR` | `-expvar` | **optional** enable the /debug/vars endpoint (exposes useful debugging attributes as well as database stats) |

Expand Down
4 changes: 3 additions & 1 deletion cmd/gonic/gonic.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,9 @@ func main() {

confExcludePatterns := set.String("exclude-pattern", "", "regex pattern to exclude files from scan (optional)")

var confMultiValueGenre, confMultiValueAlbumArtist multiValueSetting
var confMultiValueGenre, confMultiValueArtist, confMultiValueAlbumArtist multiValueSetting
set.Var(&confMultiValueGenre, "multi-value-genre", "setting for mutli-valued genre scanning (optional)")
set.Var(&confMultiValueArtist, "multi-value-artist", "setting for mutli-valued track artist scanning (optional)")
set.Var(&confMultiValueAlbumArtist, "multi-value-album-artist", "setting for mutli-valued album artist scanning (optional)")

confExpvar := set.Bool("expvar", false, "enable the /debug/vars endpoint (optional)")
Expand Down Expand Up @@ -184,6 +185,7 @@ func main() {
dbc,
map[scanner.Tag]scanner.MultiValueSetting{
scanner.Genre: scanner.MultiValueSetting(confMultiValueGenre),
scanner.Artist: scanner.MultiValueSetting(confMultiValueArtist),
scanner.AlbumArtist: scanner.MultiValueSetting(confMultiValueAlbumArtist),
},
tagReader,
Expand Down
30 changes: 19 additions & 11 deletions db/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,17 +201,18 @@ type Track struct {
Filename string `gorm:"not null; unique_index:idx_folder_filename" sql:"default: null"`
FilenameUDec string `sql:"default: null"`
Album *Album
AlbumID int `gorm:"not null; unique_index:idx_folder_filename" sql:"default: null; type:int REFERENCES albums(id) ON DELETE CASCADE"`
Genres []*Genre `gorm:"many2many:track_genres"`
Size int `sql:"default: null"`
Length int `sql:"default: null"`
Bitrate int `sql:"default: null"`
TagTitle string `sql:"default: null"`
TagTitleUDec string `sql:"default: null"`
TagTrackArtist string `sql:"default: null"`
TagTrackNumber int `sql:"default: null"`
TagDiscNumber int `sql:"default: null"`
TagBrainzID string `sql:"default: null"`
AlbumID int `gorm:"not null; unique_index:idx_folder_filename" sql:"default: null; type:int REFERENCES albums(id) ON DELETE CASCADE"`
Artists []*Artist `gorm:"many2many:track_artists"`
Genres []*Genre `gorm:"many2many:track_genres"`
Size int `sql:"default: null"`
Length int `sql:"default: null"`
Bitrate int `sql:"default: null"`
TagTitle string `sql:"default: null"`
TagTitleUDec string `sql:"default: null"`
TagTrackArtist string `sql:"default: null"`
TagTrackNumber int `sql:"default: null"`
TagDiscNumber int `sql:"default: null"`
TagBrainzID string `sql:"default: null"`
TrackStar *TrackStar
TrackRating *TrackRating
AverageRating float64 `sql:"default: null"`
Expand Down Expand Up @@ -372,6 +373,13 @@ type AlbumArtist struct {
ArtistID int `gorm:"not null; unique_index:idx_album_id_artist_id" sql:"default: null; type:int REFERENCES artists(id) ON DELETE CASCADE"`
}

type TrackArtist struct {
Track *Track
TrackID int `gorm:"not null; unique_index:idx_track_id_artist_id" sql:"default: null; type:int REFERENCES tracks(id) ON DELETE CASCADE"`
Artist *Artist
ArtistID int `gorm:"not null; unique_index:idx_track_id_artist_id" sql:"default: null; type:int REFERENCES artists(id) ON DELETE CASCADE"`
}

type TrackGenre struct {
Track *Track
TrackID int `gorm:"not null; unique_index:idx_track_id_genre_id" sql:"default: null; type:int REFERENCES tracks(id) ON DELETE CASCADE"`
Expand Down
5 changes: 5 additions & 0 deletions db/migrations.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ func (db *DB) Migrate(ctx MigrationContext) error {
construct(ctx, "202309131743", migrateArtistInfo),
construct(ctx, "202309161411", migratePlaylistsPaths),
construct(ctx, "202310252205", migrateAlbumTagArtistString),
construct(ctx, "202310281803", migrateTrackArtists),
}

return gormigrate.
Expand Down Expand Up @@ -734,3 +735,7 @@ func backupDBPre016(tx *gorm.DB, ctx MigrationContext) error {
func migrateAlbumTagArtistString(tx *gorm.DB, _ MigrationContext) error {
return tx.AutoMigrate(Album{}).Error
}

func migrateTrackArtists(tx *gorm.DB, _ MigrationContext) error {
return tx.AutoMigrate(TrackArtist{}).Error
}
2 changes: 2 additions & 0 deletions mockfs/mockfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,7 @@ func (m *tagReader) Read(absPath string) (tagcommon.Info, error) {
type TagInfo struct {
RawTitle string
RawArtist string
RawArtists []string
RawAlbum string
RawAlbumArtist string
RawAlbumArtists []string
Expand All @@ -351,6 +352,7 @@ type TagInfo struct {
func (i *TagInfo) Title() string { return i.RawTitle }
func (i *TagInfo) BrainzID() string { return "" }
func (i *TagInfo) Artist() string { return i.RawArtist }
func (i *TagInfo) Artists() []string { return i.RawArtists }
func (i *TagInfo) Album() string { return i.RawAlbum }
func (i *TagInfo) AlbumArtist() string { return i.RawAlbumArtist }
func (i *TagInfo) AlbumArtists() []string { return i.RawAlbumArtists }
Expand Down
29 changes: 27 additions & 2 deletions scanner/scanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -296,15 +296,15 @@ 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)
}
}

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)
Expand Down Expand Up @@ -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++

Expand Down Expand Up @@ -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()) }()
Expand Down Expand Up @@ -654,6 +678,7 @@ type Tag uint8

const (
Genre Tag = iota
Artist
AlbumArtist
)

Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

6 changes: 5 additions & 1 deletion server/ctrlsubsonic/handlers_by_folder.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ func (c *Controller) ServeGetMusicDirectory(r *http.Request) *spec.Response {
Where("album_id=?", id.Value).
Preload("Album").
Preload("Album.Artists").
Preload("Artists").
Preload("TrackStar", "user_id=?", user.ID).
Preload("TrackRating", "user_id=?", user.ID).
Order("filename").
Expand Down Expand Up @@ -255,7 +256,9 @@ func (c *Controller) ServeSearchTwo(r *http.Request) *spec.Response {
for _, s := range queries {
q = q.Where(`filename LIKE ? OR filename LIKE ?`, s, s)
}
q = q.Preload("TrackStar", "user_id=?", user.ID).
q = q.
Preload("Artists").
Preload("TrackStar", "user_id=?", user.ID).
Preload("TrackRating", "user_id=?", user.ID).
Offset(params.GetOrInt("songOffset", 0)).
Limit(params.GetOrInt("songCount", 20))
Expand Down Expand Up @@ -338,6 +341,7 @@ func (c *Controller) ServeGetStarred(r *http.Request) *spec.Response {
Preload("Album").
Joins("JOIN track_stars ON tracks.id=track_stars.track_id").
Where("track_stars.user_id=?", user.ID).
Preload("Artists").
Preload("TrackStar", "user_id=?", user.ID).
Preload("TrackRating", "user_id=?", user.ID)
if m := getMusicFolder(c.musicPaths, params); m != "" {
Expand Down
Loading

0 comments on commit 74f58b8

Please sign in to comment.