Add duplicate detection with fuzzy matching and fix artist metadata extraction
This commit is contained in:
@@ -462,6 +462,16 @@ export default function AdminPage() {
|
||||
song: data.song,
|
||||
validation: data.validation
|
||||
});
|
||||
} else if (res.status === 409) {
|
||||
// Duplicate detected
|
||||
const data = await res.json();
|
||||
results.push({
|
||||
filename: file.name,
|
||||
success: false,
|
||||
isDuplicate: true,
|
||||
duplicate: data.duplicate,
|
||||
error: `Duplicate: Already exists as "${data.duplicate.title}" by "${data.duplicate.artist}"`
|
||||
});
|
||||
} else {
|
||||
results.push({
|
||||
filename: file.name,
|
||||
@@ -486,12 +496,24 @@ export default function AdminPage() {
|
||||
|
||||
// Auto-trigger categorization after uploads
|
||||
const successCount = results.filter(r => r.success).length;
|
||||
const duplicateCount = results.filter(r => r.isDuplicate).length;
|
||||
const failedCount = results.filter(r => !r.success && !r.isDuplicate).length;
|
||||
if (successCount > 0) {
|
||||
setMessage(`✅ Uploaded ${successCount}/${files.length} songs successfully!\n\n🤖 Starting auto-categorization...`);
|
||||
let msg = `✅ Uploaded ${successCount}/${files.length} songs successfully!`;
|
||||
if (duplicateCount > 0) {
|
||||
msg += `\n⚠️ Skipped ${duplicateCount} duplicate(s)`;
|
||||
}
|
||||
if (failedCount > 0) {
|
||||
msg += `\n❌ ${failedCount} failed`;
|
||||
}
|
||||
msg += '\n\n🤖 Starting auto-categorization...';
|
||||
setMessage(msg);
|
||||
// Small delay to let user see the message
|
||||
setTimeout(() => {
|
||||
handleAICategorization();
|
||||
}, 1000);
|
||||
} else if (duplicateCount > 0 && failedCount === 0) {
|
||||
setMessage(`⚠️ All ${duplicateCount} file(s) were duplicates - nothing uploaded.`);
|
||||
} else {
|
||||
setMessage(`❌ All uploads failed.`);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { PrismaClient } from '@prisma/client';
|
||||
import { writeFile, unlink } from 'fs/promises';
|
||||
import path from 'path';
|
||||
import { parseBuffer } from 'music-metadata';
|
||||
import { isDuplicateSong } from '@/lib/fuzzyMatch';
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
@@ -72,8 +73,16 @@ export async function POST(request: Request) {
|
||||
if (metadata.common.title) {
|
||||
title = metadata.common.title;
|
||||
}
|
||||
if (metadata.common.artist) {
|
||||
|
||||
// Handle artist - prefer artists array if available
|
||||
if (metadata.common.artists && metadata.common.artists.length > 0) {
|
||||
// Join multiple artists with '/'
|
||||
artist = metadata.common.artists.join('/');
|
||||
} else if (metadata.common.artist) {
|
||||
artist = metadata.common.artist;
|
||||
} else if (metadata.common.albumartist) {
|
||||
// Fallback to album artist
|
||||
artist = metadata.common.albumartist;
|
||||
}
|
||||
|
||||
// Validation info
|
||||
@@ -114,6 +123,28 @@ export async function POST(request: Request) {
|
||||
if (!title) title = 'Unknown Title';
|
||||
if (!artist) artist = 'Unknown Artist';
|
||||
|
||||
// Check for duplicates
|
||||
const existingSongs = await prisma.song.findMany({
|
||||
select: { id: true, title: true, artist: true, filename: true }
|
||||
});
|
||||
|
||||
for (const existing of existingSongs) {
|
||||
if (isDuplicateSong(artist, title, existing.artist, existing.title)) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: 'Duplicate song detected',
|
||||
duplicate: {
|
||||
id: existing.id,
|
||||
title: existing.title,
|
||||
artist: existing.artist,
|
||||
filename: existing.filename
|
||||
}
|
||||
},
|
||||
{ status: 409 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Create URL-safe filename
|
||||
const originalName = file.name.replace(/\.mp3$/i, '');
|
||||
const sanitizedName = originalName
|
||||
|
||||
Reference in New Issue
Block a user