diff --git a/db/db.go b/db/db.go index 3db1e514..94e06795 100644 --- a/db/db.go +++ b/db/db.go @@ -9,7 +9,6 @@ import ( "net/url" "os" "path/filepath" - "sort" "strings" "time" @@ -293,29 +292,30 @@ type Play struct { } type Album struct { - ID int `gorm:"primary_key"` - CreatedAt time.Time - UpdatedAt time.Time - ModifiedAt time.Time - LeftPath string `gorm:"unique_index:idx_album_abs_path"` - RightPath string `gorm:"not null; unique_index:idx_album_abs_path" sql:"default: null"` - RightPathUDec string `sql:"default: null"` - Parent *Album - ParentID int `sql:"default: null; type:int REFERENCES albums(id) ON DELETE CASCADE"` - RootDir string `gorm:"unique_index:idx_album_abs_path" sql:"default: null"` - Genres []*Genre `gorm:"many2many:album_genres"` - Cover string `sql:"default: null"` - Artists []*Artist `gorm:"many2many:album_artists"` - TagTitle string `sql:"default: null"` - TagTitleUDec string `sql:"default: null"` - TagBrainzID string `sql:"default: null"` - TagYear int `sql:"default: null"` - Tracks []*Track - ChildCount int `sql:"-"` - Duration int `sql:"-"` - AlbumStar *AlbumStar - AlbumRating *AlbumRating - AverageRating float64 `sql:"default: null"` + ID int `gorm:"primary_key"` + CreatedAt time.Time + UpdatedAt time.Time + ModifiedAt time.Time + LeftPath string `gorm:"unique_index:idx_album_abs_path"` + RightPath string `gorm:"not null; unique_index:idx_album_abs_path" sql:"default: null"` + RightPathUDec string `sql:"default: null"` + Parent *Album + ParentID int `sql:"default: null; type:int REFERENCES albums(id) ON DELETE CASCADE"` + RootDir string `gorm:"unique_index:idx_album_abs_path" sql:"default: null"` + Genres []*Genre `gorm:"many2many:album_genres"` + Cover string `sql:"default: null"` + Artists []*Artist `gorm:"many2many:album_artists"` + TagTitle string `sql:"default: null"` + TagAlbumArtist string // display purposes only + TagTitleUDec string `sql:"default: null"` + TagBrainzID string `sql:"default: null"` + TagYear int `sql:"default: null"` + Tracks []*Track + ChildCount int `sql:"-"` + Duration int `sql:"-"` + AlbumStar *AlbumStar + AlbumRating *AlbumRating + AverageRating float64 `sql:"default: null"` } func (a *Album) SID() *specid.ID { @@ -333,18 +333,6 @@ func (a *Album) IndexRightPath() string { return a.RightPath } -func (a *Album) ArtistsStrings() []string { - artists := append([]*Artist(nil), a.Artists...) - sort.Slice(artists, func(i, j int) bool { - return artists[i].ID < artists[j].ID - }) - strs := make([]string, 0, len(artists)) - for _, artist := range artists { - strs = append(strs, artist.Name) - } - return strs -} - type PlayQueue struct { ID int `gorm:"primary_key"` CreatedAt time.Time diff --git a/db/migrations.go b/db/migrations.go index 0c30f20c..587d4f6e 100644 --- a/db/migrations.go +++ b/db/migrations.go @@ -66,6 +66,7 @@ func (db *DB) Migrate(ctx MigrationContext) error { construct(ctx, "202309070009", migrateDeleteArtistCoverField), construct(ctx, "202309131743", migrateArtistInfo), construct(ctx, "202309161411", migratePlaylistsPaths), + construct(ctx, "202310252205", migrateAlbumTagArtistString), } return gormigrate. @@ -729,3 +730,7 @@ func backupDBPre016(tx *gorm.DB, ctx MigrationContext) error { } return Dump(context.Background(), tx, fmt.Sprintf("%s.%d.bak", ctx.DBPath, time.Now().Unix())) } + +func migrateAlbumTagArtistString(tx *gorm.DB, _ MigrationContext) error { + return tx.AutoMigrate(Album{}).Error +} diff --git a/scanner/scanner.go b/scanner/scanner.go index 3028dbd4..3c471c70 100644 --- a/scanner/scanner.go +++ b/scanner/scanner.go @@ -372,6 +372,7 @@ func populateAlbum(tx *db.DB, album *db.Album, trags tagcommon.Info, modTime tim albumName := tagcommon.MustAlbum(trags) album.TagTitle = albumName album.TagTitleUDec = decoded(albumName) + album.TagAlbumArtist = tagcommon.MustAlbumArtist(trags) album.TagBrainzID = trags.AlbumBrainzID() album.TagYear = trags.Year() @@ -381,7 +382,6 @@ func populateAlbum(tx *db.DB, album *db.Album, trags tagcommon.Info, modTime tim if err := tx.Save(&album).Error; err != nil { return fmt.Errorf("saving album: %w", err) } - return nil } diff --git a/server/ctrlsubsonic/handlers_common.go b/server/ctrlsubsonic/handlers_common.go index af837f78..d42caf9c 100644 --- a/server/ctrlsubsonic/handlers_common.go +++ b/server/ctrlsubsonic/handlers_common.go @@ -7,6 +7,7 @@ import ( "math" "net/http" "path/filepath" + "sort" "strings" "sync" "time" @@ -71,7 +72,7 @@ func (c *Controller) ServeScrobble(r *http.Request) *spec.Response { scrobbleTrack.Track = track.TagTitle scrobbleTrack.Artist = track.TagTrackArtist scrobbleTrack.Album = track.Album.TagTitle - scrobbleTrack.AlbumArtist = strings.Join(track.Album.ArtistsStrings(), ", ") + scrobbleTrack.AlbumArtist = albumArtistString(track.Album) scrobbleTrack.TrackNumber = uint(track.TagTrackNumber) scrobbleTrack.Duration = time.Second * time.Duration(track.Length) if _, err := uuid.Parse(track.TagBrainzID); err == nil { @@ -513,3 +514,21 @@ func lowerUDecOrHash(in string) string { } return string(lower) } + +// not everyone's records may have the album.TagAlbumArtist column set, since we didn't always store that. +// try it first, but fallback to a list from the artists table +func albumArtistString(album *db.Album) string { + if album.TagAlbumArtist != "" { + return album.TagAlbumArtist + } + + artists := append([]*db.Artist(nil), album.Artists...) + sort.Slice(artists, func(i, j int) bool { + return artists[i].ID < artists[j].ID + }) + names := make([]string, 0, len(artists)) + for _, artist := range artists { + names = append(names, artist.Name) + } + return strings.Join(names, ", ") +}