feat: Add cover art support and auto-migration

- Extract cover art from MP3s during upload
- Display cover art in game result screens (win/loss)
- Add coverImage field to Song model
- Add migration script to backfill covers for existing songs
- Configure Docker to run migration script on startup
This commit is contained in:
Hördle Bot
2025-11-21 15:51:22 +01:00
parent 0c9508076f
commit 29d43effe3
8 changed files with 178 additions and 4 deletions

View File

@@ -21,6 +21,7 @@ export async function GET() {
artist: song.artist,
filename: song.filename,
createdAt: song.createdAt,
coverImage: song.coverImage,
activations: song.puzzles.length,
}));
@@ -62,11 +63,30 @@ export async function POST(request: Request) {
await writeFile(path.join(uploadDir, filename), buffer);
// Handle cover image
let coverImage = null;
try {
const metadata = await parseBuffer(buffer, file.type);
const picture = metadata.common.picture?.[0];
if (picture) {
const extension = picture.format.split('/')[1] || 'jpg';
const coverFilename = `cover-${Date.now()}.${extension}`;
const coverPath = path.join(process.cwd(), 'public/uploads/covers', coverFilename);
await writeFile(coverPath, picture.data);
coverImage = coverFilename;
}
} catch (e) {
console.error('Failed to extract cover image:', e);
}
const song = await prisma.song.create({
data: {
title,
artist,
filename,
coverImage,
},
});
@@ -123,6 +143,16 @@ export async function DELETE(request: Request) {
// Continue with DB deletion even if file deletion fails
}
// Delete cover image if exists
if (song.coverImage) {
const coverPath = path.join(process.cwd(), 'public/uploads/covers', song.coverImage);
try {
await unlink(coverPath);
} catch (e) {
console.error('Failed to delete cover image:', e);
}
}
// Delete from database (will cascade delete related puzzles)
await prisma.song.delete({
where: { id: Number(id) },