feat(subsonic): add support for track/album/artist ratings/stars

fixes #171
fixes #31

* Initial code. Compiles and passes unit tests.

* Moved average rating calculation from rating fetch to set rating function. Still only compiled and unit tested.

* Bug fixes

* Fixed bug in savePlayQueue. Removed unique_index for star / rating entries because it's not valid.

* Changed time format on stars to RFC3339Nano to match created date format.

* Lint fixes.

* More lint fixes.

* Removed add* functions and replaced with Preload.

* Fixed several bugs in handlers for getStarred and getStarred2.

* Fixed bug when using music folder ID.

Co-authored-by: Brian Doherty <brian@hplaptop.dohertyfamily.me>
This commit is contained in:
brian-doherty
2022-10-25 19:37:44 -05:00
committed by sentriz
parent 25b39085d8
commit e8759cb6c1
10 changed files with 666 additions and 133 deletions

View File

@@ -44,6 +44,7 @@ func (db *DB) Migrate(ctx MigrationContext) error {
construct(ctx, "202204270903", migratePodcastDropUserID),
construct(ctx, "202206011628", migrateInternetRadioStations),
construct(ctx, "202206101425", migrateUser),
construct(ctx, "202207251148", migrateStarRating),
}
return gormigrate.
@@ -371,3 +372,18 @@ func migrateUser(tx *gorm.DB, _ MigrationContext) error {
).
Error
}
func migrateStarRating(tx *gorm.DB, _ MigrationContext) error {
return tx.AutoMigrate(
Album{},
AlbumStar{},
AlbumRating{},
Artist{},
ArtistStar{},
ArtistRating{},
Track{},
TrackStar{},
TrackRating{},
).
Error
}

View File

@@ -1,4 +1,5 @@
// Package db provides database helpers and models
//
//nolint:lll // struct tags get very long and can't be split
package db
@@ -42,12 +43,15 @@ func joinInt(in []int, sep string) string {
}
type Artist struct {
ID int `gorm:"primary_key"`
Name string `gorm:"not null; unique_index"`
NameUDec string `sql:"default: null"`
Albums []*Album `gorm:"foreignkey:TagArtistID"`
AlbumCount int `sql:"-"`
Cover string `sql:"default: null"`
ID int `gorm:"primary_key"`
Name string `gorm:"not null; unique_index"`
NameUDec string `sql:"default: null"`
Albums []*Album `gorm:"foreignkey:TagArtistID"`
AlbumCount int `sql:"-"`
Cover string `sql:"default: null"`
ArtistStar *ArtistStar
ArtistRating *ArtistRating
AverageRating float64 `sql:"default: null"`
}
func (a *Artist) SID() *specid.ID {
@@ -98,6 +102,9 @@ type Track struct {
TagTrackNumber int `sql:"default: null"`
TagDiscNumber int `sql:"default: null"`
TagBrainzID string `sql:"default: null"`
TrackStar *TrackStar
TrackRating *TrackRating
AverageRating float64 `sql:"default: null"`
}
func (t *Track) AudioLength() int { return t.Length }
@@ -212,6 +219,9 @@ type Album struct {
Tracks []*Track
ChildCount int `sql:"-"`
Duration int `sql:"-"`
AlbumStar *AlbumStar
AlbumRating *AlbumRating
AverageRating float64 `sql:"default: null"`
}
func (a *Album) SID() *specid.ID {
@@ -304,6 +314,42 @@ type AlbumGenre struct {
GenreID int `gorm:"not null; unique_index:idx_album_id_genre_id" sql:"default: null; type:int REFERENCES genres(id) ON DELETE CASCADE"`
}
type AlbumStar struct {
UserID int `gorm:"primary_key; not null" sql:"default: null; type:int REFERENCES users(id) ON DELETE CASCADE"`
AlbumID int `gorm:"primary_key; not null" sql:"default: null; type:int REFERENCES albums(id) ON DELETE CASCADE"`
StarDate time.Time
}
type AlbumRating struct {
UserID int `gorm:"primary_key; not null" sql:"default: null; type:int REFERENCES users(id) ON DELETE CASCADE"`
AlbumID int `gorm:"primary_key; not null" sql:"default: null; type:int REFERENCES albums(id) ON DELETE CASCADE"`
Rating int `gorm:"not null; check:(rating >= 1 AND rating <= 5)"`
}
type ArtistStar struct {
UserID int `gorm:"primary_key; not null" sql:"default: null; type:int REFERENCES users(id) ON DELETE CASCADE"`
ArtistID int `gorm:"primary_key; not null" sql:"default: null; type:int REFERENCES artists(id) ON DELETE CASCADE"`
StarDate time.Time
}
type ArtistRating struct {
UserID int `gorm:"primary_key; not null" sql:"default: null; type:int REFERENCES users(id) ON DELETE CASCADE"`
ArtistID int `gorm:"primary_key; not null" sql:"default: null; type:int REFERENCES artists(id) ON DELETE CASCADE"`
Rating int `gorm:"not null; check:(rating >= 1 AND rating <= 5)"`
}
type TrackStar struct {
UserID int `gorm:"primary_key; not null" sql:"default: null; type:int REFERENCES users(id) ON DELETE CASCADE"`
TrackID int `gorm:"primary_key; not null" sql:"default: null; type:int REFERENCES tracks(id) ON DELETE CASCADE"`
StarDate time.Time
}
type TrackRating struct {
UserID int `gorm:"primary_key; not null" sql:"default: null; type:int REFERENCES users(id) ON DELETE CASCADE"`
TrackID int `gorm:"primary_key; not null" sql:"default: null; type:int REFERENCES tracks(id) ON DELETE CASCADE"`
Rating int `gorm:"not null; check:(rating >= 1 AND rating <= 5)"`
}
type PodcastAutoDownload string
const (