diff --git a/db/migrations.go b/db/migrations.go index c560c02..0bd40a8 100644 --- a/db/migrations.go +++ b/db/migrations.go @@ -54,6 +54,7 @@ func (db *DB) Migrate(ctx MigrationContext) error { construct(ctx, "202207251148", migrateStarRating), construct(ctx, "202211111057", migratePlaylistsQueuesToFullID), construct(ctx, "202304221528", migratePlaylistsToM3U), + construct(ctx, "202305301718", migratePlayCountToLength), } return gormigrate. @@ -516,3 +517,19 @@ func migratePlaylistsToM3U(tx *gorm.DB, ctx MigrationContext) error { return nil } + +func migratePlayCountToLength(tx *gorm.DB, _ MigrationContext) error { + // As a best guess, we set length played so far as length of album * current count / number of tracks in album + step := tx.Exec(` + UPDATE plays SET length= + ((SELECT SUM(length) FROM tracks WHERE tracks.album_id=plays.album_id)*plays.count/ + (SELECT COUNT(*) FROM tracks WHERE tracks.album_id=plays.album_id)); + `) + if err := step.Error; err != nil { + return fmt.Errorf("calculate length: %w", err) + } + + return nil +} + + diff --git a/db/model.go b/db/model.go index 7d65891..1709156 100644 --- a/db/model.go +++ b/db/model.go @@ -190,6 +190,7 @@ type Play struct { AlbumID int `gorm:"not null; index" sql:"default: null; type:int REFERENCES albums(id) ON DELETE CASCADE"` Time time.Time `sql:"default: null"` Count int + Length int } type Album struct { diff --git a/server/ctrlsubsonic/handlers_by_folder.go b/server/ctrlsubsonic/handlers_by_folder.go index 24b13ff..61caf26 100644 --- a/server/ctrlsubsonic/handlers_by_folder.go +++ b/server/ctrlsubsonic/handlers_by_folder.go @@ -150,7 +150,7 @@ func (c *Controller) ServeGetAlbumList(r *http.Request) *spec.Response { JOIN plays ON albums.id=plays.album_id AND plays.user_id=?`, user.ID) - q = q.Order("plays.count DESC") + q = q.Order("plays.length DESC") case "newest": q = q.Order("created_at DESC") case "random": diff --git a/server/ctrlsubsonic/handlers_by_tags.go b/server/ctrlsubsonic/handlers_by_tags.go index 20f735f..f55906a 100644 --- a/server/ctrlsubsonic/handlers_by_tags.go +++ b/server/ctrlsubsonic/handlers_by_tags.go @@ -162,7 +162,7 @@ func (c *Controller) ServeGetAlbumListTwo(r *http.Request) *spec.Response { case "frequent": user := r.Context().Value(CtxUser).(*db.User) q = q.Joins("JOIN plays ON albums.id=plays.album_id AND plays.user_id=?", user.ID) - q = q.Order("plays.count DESC") + q = q.Order("plays.length DESC") case "newest": q = q.Order("created_at DESC") case "random": diff --git a/server/ctrlsubsonic/handlers_common.go b/server/ctrlsubsonic/handlers_common.go index 050862d..d5f4960 100644 --- a/server/ctrlsubsonic/handlers_common.go +++ b/server/ctrlsubsonic/handlers_common.go @@ -69,7 +69,7 @@ func (c *Controller) ServeScrobble(r *http.Request) *spec.Response { optStamp := params.GetOrTime("time", time.Now()) optSubmission := params.GetOrBool("submission", true) - if err := streamUpdateStats(c.DB, user.ID, track.Album.ID, optStamp); err != nil { + if err := streamUpdateStats(c.DB, user.ID, track, optStamp); err != nil { return spec.NewError(0, "error updating stats: %v", err) } diff --git a/server/ctrlsubsonic/handlers_raw.go b/server/ctrlsubsonic/handlers_raw.go index 5006314..16b4acc 100644 --- a/server/ctrlsubsonic/handlers_raw.go +++ b/server/ctrlsubsonic/handlers_raw.go @@ -58,19 +58,20 @@ func streamGetTransPrefProfile(dbc *db.DB, userID int, client string) (mime stri var errUnknownMediaType = fmt.Errorf("media type is unknown") -func streamUpdateStats(dbc *db.DB, userID, albumID int, playTime time.Time) error { +func streamUpdateStats(dbc *db.DB, userID int, track *db.Track, playTime time.Time) error { var play db.Play err := dbc. - Where("album_id=? AND user_id=?", albumID, userID). + Where("album_id=? AND user_id=?", track.AlbumID, userID). First(&play). Error if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { return fmt.Errorf("find stat: %w", err) } - play.AlbumID = albumID + play.AlbumID = track.AlbumID play.UserID = userID play.Count++ // for getAlbumList?type=frequent + play.Length += track.Length if playTime.After(play.Time) { play.Time = playTime // for getAlbumList?type=recent } @@ -269,7 +270,7 @@ func (c *Controller) ServeStream(w http.ResponseWriter, r *http.Request) *spec.R if track, ok := audioFile.(*db.Track); ok && track.Album != nil { defer func() { - if err := streamUpdateStats(c.DB, user.ID, track.Album.ID, time.Now()); err != nil { + if err := streamUpdateStats(c.DB, user.ID, track, time.Now()); err != nil { log.Printf("error updating track status: %v", err) } }()