diff --git a/persistence/mediafile_repository.go b/persistence/mediafile_repository.go index 2fcbaee90..935a64bb9 100644 --- a/persistence/mediafile_repository.go +++ b/persistence/mediafile_repository.go @@ -411,6 +411,15 @@ func (r *mediaFileRepository) ReassignReferences(prevID, newID string) error { if _, err := r.executeSQL(upd); err != nil { return fmt.Errorf("reassigning playlist tracks: %w", err) } + upd = Update("scrobbles").Set("media_file_id", newID).Where(Eq{"media_file_id": prevID}) + if _, err := r.executeSQL(upd); err != nil { + return fmt.Errorf("reassigning scrobbles: %w", err) + } + // OR IGNORE: scrobble_buffer is unique on (user_id, service, media_file_id, play_time) + buf := Expr("update or ignore scrobble_buffer set media_file_id = ? where media_file_id = ?", newID, prevID) + if _, err := r.executeSQL(buf); err != nil { + return fmt.Errorf("reassigning buffered scrobbles: %w", err) + } return nil } diff --git a/persistence/mediafile_repository_test.go b/persistence/mediafile_repository_test.go index 29346c732..3e2cb8b6a 100644 --- a/persistence/mediafile_repository_test.go +++ b/persistence/mediafile_repository_test.go @@ -16,6 +16,7 @@ import ( "github.com/navidrome/navidrome/model/criteria" "github.com/navidrome/navidrome/model/id" "github.com/navidrome/navidrome/model/request" + "github.com/navidrome/navidrome/utils/slice" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/pocketbase/dbx" @@ -941,6 +942,8 @@ var _ = Describe("MediaRepository", func() { _ = mr.Delete(next.ID) _, _ = mr.(*mediaFileRepository).executeSQL(squirrel.Delete("annotation").Where(squirrel.Eq{"item_id": []string{prev.ID, next.ID}})) _, _ = mr.(*mediaFileRepository).executeSQL(squirrel.Delete("bookmark").Where(squirrel.Eq{"item_id": []string{prev.ID, next.ID}})) + _, _ = mr.(*mediaFileRepository).executeSQL(squirrel.Delete("scrobbles").Where(squirrel.Eq{"media_file_id": []string{prev.ID, next.ID}})) + _, _ = mr.(*mediaFileRepository).executeSQL(squirrel.Delete("scrobble_buffer").Where(squirrel.Eq{"media_file_id": []string{prev.ID, next.ID}})) }) It("moves annotations, bookmarks and playlist entries onto the new id", func() { @@ -963,6 +966,29 @@ var _ = Describe("MediaRepository", func() { Expect(withTracks.Tracks[0].MediaFileID).To(Equal(next.ID)) }) + It("moves scrobbles and buffered scrobbles onto the new id", func() { + ctx := request.WithUser(log.NewContext(context.TODO()), model.User{ID: "userid"}) + scrobbles := NewScrobbleRepository(ctx, GetDBXBuilder()) + buffer := NewScrobbleBufferRepository(ctx, GetDBXBuilder()) + Expect(scrobbles.RecordScrobble(prev.ID, time.Now())).To(Succeed()) + Expect(buffer.Enqueue("lastfm", "userid", prev.ID, time.Now())).To(Succeed()) + + Expect(mr.ReassignReferences(prev.ID, next.ID)).To(Succeed()) + Expect(mr.Delete(prev.ID)).To(Succeed()) + + all, err := scrobbles.GetAll() + Expect(err).ToNot(HaveOccurred()) + mine := slice.Map(slice.Filter(all, func(sc model.Scrobble) bool { + return sc.MediaFileID == prev.ID || sc.MediaFileID == next.ID + }), func(sc model.Scrobble) string { return sc.MediaFileID }) + Expect(mine).To(ConsistOf(next.ID)) + + entry, err := buffer.Next("lastfm", "userid") + Expect(err).ToNot(HaveOccurred()) + Expect(entry).ToNot(BeNil()) + Expect(entry.MediaFile.ID).To(Equal(next.ID)) + }) + It("keeps the new id's own annotation and bookmark when both exist", func() { Expect(mr.SetRating(5, prev.ID)).To(Succeed()) Expect(mr.SetRating(1, next.ID)).To(Succeed())