feat: the same song on several releases counts as one song (M498 #5438)
release / govulncheck (push) Successful in 21s
release / web (push) Successful in 1m7s
release / go (push) Successful in 1m29s
release / integration (push) Successful in 4m11s
release / Attach APK to the Release (tag releases only) (push) Canceled after 0s
release / Build + push container image (push) Canceled after 0s
release / Verify release artifacts (tag releases only) (push) Canceled after 0s
release / android (push) Canceled after 5m58s
release / Build signed APK (releases and dev) (push) Canceled after 5m59s
release / govulncheck (push) Successful in 21s
release / web (push) Successful in 1m7s
release / go (push) Successful in 1m29s
release / integration (push) Successful in 4m11s
release / Attach APK to the Release (tag releases only) (push) Canceled after 0s
release / Build + push container image (push) Canceled after 0s
release / Verify release artifacts (tag releases only) (push) Canceled after 0s
release / android (push) Canceled after 5m58s
release / Build signed APK (releases and dev) (push) Canceled after 5m59s
A single and the album it is on stay two files, since each fulfils its own
release in Lidarr, but they are one song to the listener.
- tracks.song_id links copies; the generated song_key (song_id, else the
track's own id) is what they share (migration 0076).
- The resolver links each cross-release group every pass (idempotent; only
with auto-resolve on) and, when a link is new, shares existing likes across
the song and logs them for sync.
- A like or unlike (web and Subsonic) reaches every copy; each change is
logged and published so clients update every heart.
- The Liked list and its count show the song once; Shuffle and the mix writer
take one copy per song.
- A merge keeps the removed copy's song link; "Not the same song" on the
Across releases tab dismisses the group and undoes the link.
Shared plays ("heard via another copy") are left for a later step.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -30,7 +30,8 @@
|
||||
// purpose. A third tab lists what the resolver did.
|
||||
//
|
||||
// Dismissing a group says "these are not duplicates", and the sweep will not
|
||||
// propose that set again. Merging (#3911) keeps one copy, moves the others'
|
||||
// propose that set again. On the cross-release tab it reads "Not the same
|
||||
// song" and also undoes the link that made the copies one song (#5438). Merging (#3911) keeps one copy, moves the others'
|
||||
// likes, plays and playlist entries onto it, and deletes their files, so it
|
||||
// asks twice. The server refuses to remove a copy Lidarr uses.
|
||||
|
||||
@@ -337,7 +338,8 @@
|
||||
<p class="mt-3 text-text-primary">No songs repeated across releases.</p>
|
||||
<p class="mt-1 text-sm text-text-secondary">
|
||||
A single and the album it's on, or two editions of one album, show here. Each copy
|
||||
belongs to its own release, so none is removed.
|
||||
belongs to its own release, so none is removed. They count as one song: a like on one
|
||||
is a like on all, and a mix plays it once.
|
||||
</p>
|
||||
{:else}
|
||||
<p class="mt-3 text-text-primary">Nothing needs review.</p>
|
||||
@@ -366,7 +368,7 @@
|
||||
disabled={dismissing === group.id || merging === group.id}
|
||||
class="rounded-md border border-border px-3 py-1.5 text-sm text-text-secondary hover:bg-surface-hover hover:text-text-primary disabled:opacity-50"
|
||||
>
|
||||
Not duplicates
|
||||
{tab === 'cross_release' ? 'Not the same song' : 'Not duplicates'}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
|
||||
@@ -323,6 +323,15 @@ describe('admin duplicates', () => {
|
||||
expect(createDuplicatesQuery).toHaveBeenLastCalledWith('cross_release');
|
||||
});
|
||||
|
||||
// Copies across releases are linked as one song; dismissing one says they
|
||||
// are not, which is what the button names.
|
||||
test('Across releases dismisses as Not the same song', async () => {
|
||||
renderWith(response());
|
||||
await fireEvent.click(screen.getByRole('tab', { name: /across releases/i }));
|
||||
await fireEvent.click(screen.getByRole('button', { name: 'Not the same song' }));
|
||||
expect(dismissDuplicateGroup).toHaveBeenCalledWith('g-1');
|
||||
});
|
||||
|
||||
test('Resolved automatically lists merges and release changes', async () => {
|
||||
renderWith(response(), [
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user