mirror of
https://github.com/navidrome/navidrome.git
synced 2026-10-18 07:30:29 +00:00
fix: only move album annotations if moving a track would empty the old album
This commit is contained in:
parent
cb185e567b
commit
9da12262a6
@ -93,13 +93,20 @@ func (s *maintenanceService) RemapMissingFile(ctx context.Context, missingID, ta
|
||||
return fmt.Errorf("delete discarded track: %w", err)
|
||||
}
|
||||
|
||||
// Reassign album annotations (starred, rating) and preserve album created_at on album change
|
||||
if oldAlbumID != newAlbumID {
|
||||
if err := tx.Album(ctx).ReassignAnnotation(oldAlbumID, newAlbumID); err != nil {
|
||||
log.Warn(ctx, "Could not reassign album annotations", "from", oldAlbumID, "to", newAlbumID, err)
|
||||
// Reassign album annotations (starred, rating) if the old album is now empty
|
||||
oldAlbumTracks, err := tx.MediaFile(ctx).CountAll(model.QueryOptions{Filters: squirrel.Eq{"album_id": oldAlbumID}})
|
||||
if err != nil {
|
||||
return fmt.Errorf("get old album tracks: %w", err)
|
||||
}
|
||||
if err := tx.Album(ctx).CopyAttributes(oldAlbumID, newAlbumID, "created_at"); err != nil && !errors.Is(err, model.ErrNotFound) {
|
||||
log.Warn(ctx, "Could not copy album created_at", "from", oldAlbumID, "to", newAlbumID, err)
|
||||
if oldAlbumTracks == 0 {
|
||||
if err := tx.Album(ctx).ReassignAnnotation(oldAlbumID, newAlbumID); err != nil {
|
||||
return fmt.Errorf("reassign album annotations: %w", err)
|
||||
}
|
||||
// Copy across the create_at timestamp from the old album
|
||||
if err := tx.Album(ctx).CopyAttributes(oldAlbumID, newAlbumID, "created_at"); err != nil && !errors.Is(err, model.ErrNotFound) {
|
||||
return fmt.Errorf("copy album attributes: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
@ -272,18 +272,32 @@ var _ = Describe("Maintenance", func() {
|
||||
Expect(ds.GCCalled).To(BeTrue())
|
||||
})
|
||||
|
||||
It("reassigns album annotations when the album changes", func() {
|
||||
It("reassigns album annotations when the old album is emptied", func() {
|
||||
albumRepo := ds.MockedAlbum.(*extendedAlbumRepo)
|
||||
mfRepo.SetData(model.MediaFiles{
|
||||
{ID: "m1", AlbumID: "album1", Missing: true},
|
||||
{ID: "t1", AlbumID: "album2", Missing: false},
|
||||
})
|
||||
|
||||
mfRepo.SetCountAll(0)
|
||||
Expect(service.RemapMissingFile(ctx, "m1", "t1")).To(Succeed())
|
||||
|
||||
Expect(albumRepo.ReassignAnnotationCalls).To(HaveKeyWithValue("album1", "album2"))
|
||||
})
|
||||
|
||||
It("does not reassign album annotations when the old album is not emptied", func() {
|
||||
albumRepo := ds.MockedAlbum.(*extendedAlbumRepo)
|
||||
mfRepo.SetData(model.MediaFiles{
|
||||
{ID: "m1", AlbumID: "album1", Missing: true},
|
||||
{ID: "m2", AlbumID: "album1", Missing: false},
|
||||
{ID: "t1", AlbumID: "album2", Missing: false},
|
||||
})
|
||||
|
||||
Expect(service.RemapMissingFile(ctx, "m1", "t1")).To(Succeed())
|
||||
|
||||
Expect(albumRepo.ReassignAnnotationCalls).To(BeEmpty())
|
||||
})
|
||||
|
||||
It("does not reassign annotations when the album is unchanged", func() {
|
||||
albumRepo := ds.MockedAlbum.(*extendedAlbumRepo)
|
||||
mfRepo.SetData(model.MediaFiles{
|
||||
|
||||
@ -311,7 +311,7 @@ var _ = Describe("phaseMissingTracks", func() {
|
||||
When("PurgeMissing is 'always'", func() {
|
||||
BeforeEach(func() {
|
||||
conf.Server.Scanner.PurgeMissing = consts.PurgeMissingAlways
|
||||
mr.CountAllValue = 3
|
||||
mr.SetCountAll(3)
|
||||
mr.DeleteAllMissingValue = 3
|
||||
})
|
||||
It("should purge missing files", func() {
|
||||
@ -325,7 +325,7 @@ var _ = Describe("phaseMissingTracks", func() {
|
||||
When("PurgeMissing is 'full'", func() {
|
||||
BeforeEach(func() {
|
||||
conf.Server.Scanner.PurgeMissing = consts.PurgeMissingFull
|
||||
mr.CountAllValue = 2
|
||||
mr.SetCountAll(2)
|
||||
mr.DeleteAllMissingValue = 2
|
||||
})
|
||||
It("should not purge missing files if not a full scan", func() {
|
||||
@ -346,7 +346,7 @@ var _ = Describe("phaseMissingTracks", func() {
|
||||
When("PurgeMissing is 'never'", func() {
|
||||
BeforeEach(func() {
|
||||
conf.Server.Scanner.PurgeMissing = consts.PurgeMissingNever
|
||||
mr.CountAllValue = 1
|
||||
mr.SetCountAll(1)
|
||||
mr.DeleteAllMissingValue = 1
|
||||
})
|
||||
It("should not purge missing files", func() {
|
||||
|
||||
@ -24,8 +24,9 @@ type MockMediaFileRepo struct {
|
||||
model.MediaFileRepository
|
||||
Data map[string]*model.MediaFile
|
||||
Err bool
|
||||
// Add fields and methods for controlling CountAll and DeleteAllMissing in tests
|
||||
CountAllValue int64
|
||||
// Add fields and methods for controlling CountAll and DeleteAllMissing in tests.
|
||||
// A nil CountAllValue is unset, and CountAll falls back to counting rows in Data.
|
||||
CountAllValue *int64
|
||||
CountAllOptions model.QueryOptions
|
||||
DeleteAllMissingValue int64
|
||||
Options model.QueryOptions
|
||||
@ -40,6 +41,10 @@ func (m *MockMediaFileRepo) SetError(err bool) {
|
||||
m.Err = err
|
||||
}
|
||||
|
||||
func (m *MockMediaFileRepo) SetCountAll(count int64) {
|
||||
m.CountAllValue = &count
|
||||
}
|
||||
|
||||
func (m *MockMediaFileRepo) SetData(mfs model.MediaFiles) {
|
||||
m.Data = make(map[string]*model.MediaFile)
|
||||
for i, mf := range mfs {
|
||||
@ -259,11 +264,11 @@ func (m *MockMediaFileRepo) CountAll(opts ...model.QueryOptions) (int64, error)
|
||||
if m.Err {
|
||||
return 0, errors.New("error")
|
||||
}
|
||||
if m.CountAllValue != 0 {
|
||||
if len(opts) > 0 {
|
||||
m.CountAllOptions = opts[0]
|
||||
}
|
||||
return m.CountAllValue, nil
|
||||
if len(opts) > 0 {
|
||||
m.CountAllOptions = opts[0]
|
||||
}
|
||||
if m.CountAllValue != nil {
|
||||
return *m.CountAllValue, nil
|
||||
}
|
||||
return int64(len(m.Data)), nil
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user