Notifications inbox (M489), Discover taste-arm fix, web tooling majors #151

Merged
bvandeusen merged 26 commits from dev into main 2026-10-08 11:11:09 -04:00
2 changed files with 92 additions and 1 deletions
Showing only changes of commit 5dffe51b95 - Show all commits
+37 -1
View File
@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import { pageTitle } from '$lib/branding'; import { pageTitle } from '$lib/branding';
import { Copy } from 'lucide-svelte'; import { Copy, TriangleAlert } from 'lucide-svelte';
import { import {
createDuplicatesQuery, createDuplicatesQuery,
runDuplicateSweep, runDuplicateSweep,
@@ -122,6 +122,27 @@
return n === 1 ? `1 ${one}` : `${n} ${many}`; return n === 1 ? `1 ${one}` : `${n} ${many}`;
} }
// Identical audio under different titles means at least one file's tags are
// wrong, and the recording the other title names may not be in the library at
// all. #3885: "WWW" was a second copy of the instrumental, and the vocal was
// missing with nothing to say so. Exact tier only: an acoustic match spans
// encodings, where a "Remastered" suffix is routine rather than a lie.
function differingTitles(g: AdminDuplicateGroup): string[] {
if (g.tier !== 'exact') return [];
const byKey = new Map<string, string>();
for (const m of g.members) {
const title = m.title.trim().replace(/\s+/g, ' ');
const key = title.toLowerCase();
if (!byKey.has(key)) byKey.set(key, title);
}
return byKey.size > 1 ? [...byKey.values()] : [];
}
function quotedList(titles: string[]): string {
const q = titles.map((t) => `“${t}”`);
return q.length <= 2 ? q.join(' and ') : `${q.slice(0, -1).join(', ')} and ${q[q.length - 1]}`;
}
// What the copy carries is the fact that decides which to keep. // What the copy carries is the fact that decides which to keep.
function historyLabel(m: AdminDuplicateMember): string { function historyLabel(m: AdminDuplicateMember): string {
if (m.like_count === 0 && m.play_count === 0) return 'no likes or plays'; if (m.like_count === 0 && m.play_count === 0) return 'no likes or plays';
@@ -224,6 +245,7 @@
{:else} {:else}
<ul class="space-y-4"> <ul class="space-y-4">
{#each groups as group (group.id)} {#each groups as group (group.id)}
{@const titles = differingTitles(group)}
<li class="overflow-hidden rounded-lg border border-border bg-surface" data-testid="duplicate-group"> <li class="overflow-hidden rounded-lg border border-border bg-surface" data-testid="duplicate-group">
<div class="flex items-center justify-between gap-4 border-b border-border px-4 py-3"> <div class="flex items-center justify-between gap-4 border-b border-border px-4 py-3">
<div class="min-w-0"> <div class="min-w-0">
@@ -258,6 +280,20 @@
</div> </div>
</div> </div>
{#if titles.length > 0}
<p
class="flex items-start gap-2 border-b border-border bg-surface-hover px-4 py-3 text-xs text-text-secondary"
data-testid="title-mismatch"
>
<TriangleAlert size={14} class="mt-0.5 flex-shrink-0 text-warning-fg" aria-hidden="true" />
<span>
The audio is identical, but the copies are titled {quotedList(titles)}. If those
name different recordings, one file is mislabelled and the other recording isn't in
your library. Keep the copy whose title matches what you hear.
</span>
</p>
{/if}
{#if confirmingMerge === group.id} {#if confirmingMerge === group.id}
<!-- What the second click will do, said plainly before it is done. --> <!-- What the second click will do, said plainly before it is done. -->
<div class="space-y-2 border-b border-border bg-surface-hover px-4 py-3 text-sm" data-testid="merge-confirm"> <div class="space-y-2 border-b border-border bg-surface-hover px-4 py-3 text-sm" data-testid="merge-confirm">
@@ -124,6 +124,61 @@ describe('admin duplicates', () => {
expect(text(screen.getByTestId('duplicate-tier'))).toBe('Same recording · 96% match'); expect(text(screen.getByTestId('duplicate-tier'))).toBe('Same recording · 96% match');
}); });
// #3885: identical audio titled "WWW" and "WWW (instrumental)". One file
// claimed to be the vocal and wasn't, and nothing said the vocal was missing.
describe('identical audio under different titles', () => {
function www() {
const r = response();
r.groups[0] = {
...r.groups[0],
members: [member('www-01'), member('www-02', { title: 'WWW (instrumental)' })]
};
return r;
}
test('says the copies are titled differently and names both titles', () => {
renderWith(www());
const notice = text(screen.getByTestId('title-mismatch'));
expect(notice).toContain('titled “WWW” and “WWW (instrumental)”');
expect(notice).toContain("isn't in your library");
});
test('three different titles are all named', () => {
const r = www();
r.groups[0].members.push(member('www-03', { title: 'WWW (Radio Edit)' }));
renderWith(r);
expect(text(screen.getByTestId('title-mismatch'))).toContain(
'“WWW”, “WWW (instrumental)” and “WWW (Radio Edit)”'
);
});
test('titles differing only in case or spacing are the same title', () => {
const r = response();
r.groups[0] = {
...r.groups[0],
members: [
member('www-01', { title: 'WWW (Instrumental)' }),
member('www-02', { title: ' www (instrumental) ' })
]
};
renderWith(r);
expect(screen.queryByTestId('title-mismatch')).toBeNull();
});
test('matching titles raise nothing', () => {
renderWith(response());
expect(screen.queryByTestId('title-mismatch')).toBeNull();
});
// Across encodings a "Remastered" suffix is routine, not a mislabel.
test('an acoustic group with different titles raises nothing', () => {
const r = www();
r.groups[0] = { ...r.groups[0], tier: 'acoustic', worst_bit_error_rate: 0.04 };
renderWith(r);
expect(screen.queryByTestId('title-mismatch')).toBeNull();
});
});
// Three states all show zero groups. Each must say which it is. // Three states all show zero groups. Each must say which it is.
test('empty while still fingerprinting says so', () => { test('empty while still fingerprinting says so', () => {
renderWith( renderWith(