From 29d43effe33778d41cd32ed5b35aa4f2d51a1fd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=B6rdle=20Bot?= Date: Fri, 21 Nov 2025 15:51:22 +0100 Subject: [PATCH] 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 --- Dockerfile | 1 + app/admin/page.tsx | 34 ++++++++++++++++++++ app/api/songs/route.ts | 30 +++++++++++++++++ app/page.tsx | 5 ++- components/Game.tsx | 41 ++++++++++++++++++++++- docker-compose.example.yml | 4 +-- prisma/schema.prisma | 1 + scripts/migrate-covers.mjs | 66 ++++++++++++++++++++++++++++++++++++++ 8 files changed, 178 insertions(+), 4 deletions(-) create mode 100644 scripts/migrate-covers.mjs diff --git a/Dockerfile b/Dockerfile index 931c237..e08bb8b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -47,6 +47,7 @@ COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static COPY --from=builder --chown=nextjs:nodejs /app/public ./public COPY --from=builder --chown=nextjs:nodejs /app/prisma ./prisma +COPY --from=builder --chown=nextjs:nodejs /app/scripts ./scripts # Create uploads directory and set permissions RUN mkdir -p public/uploads && chown nextjs:nodejs public/uploads diff --git a/app/admin/page.tsx b/app/admin/page.tsx index dac4b32..d0b5fca 100644 --- a/app/admin/page.tsx +++ b/app/admin/page.tsx @@ -35,6 +35,10 @@ export default function AdminPage() { const [currentPage, setCurrentPage] = useState(1); const itemsPerPage = 10; + // Audio state + const [playingSongId, setPlayingSongId] = useState(null); + const [audioElement, setAudioElement] = useState(null); + const handleLogin = async () => { const res = await fetch('/api/admin/login', { method: 'POST', @@ -132,6 +136,29 @@ export default function AdminPage() { } }; + const handlePlayPause = (song: Song) => { + if (playingSongId === song.id) { + // Pause current song + audioElement?.pause(); + setPlayingSongId(null); + } else { + // Stop any currently playing song + audioElement?.pause(); + + // Play new song + const audio = new Audio(`/uploads/${song.filename}`); + audio.play(); + setAudioElement(audio); + setPlayingSongId(song.id); + + // Reset when song ends + audio.onended = () => { + setPlayingSongId(null); + setAudioElement(null); + }; + } + }; + // Filter and sort songs const filteredSongs = songs.filter(song => song.title.toLowerCase().includes(searchQuery.toLowerCase()) || @@ -297,6 +324,13 @@ export default function AdminPage() { {song.activations}
+