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

@@ -11,6 +11,9 @@ interface GameProps {
id: number;
audioUrl: string;
songId: number;
title: string;
artist: string;
coverImage: string | null;
} | null;
}
@@ -139,6 +142,24 @@ export default function Game({ dailyPuzzle }: GameProps) {
<div className="message-box success">
<h2 style={{ fontSize: '1.5rem', fontWeight: 'bold', marginBottom: '0.5rem' }}>You won!</h2>
<p>Come back tomorrow for a new song.</p>
{/* Song Details */}
<div style={{ margin: '1.5rem 0', padding: '1rem', background: 'rgba(255,255,255,0.5)', borderRadius: '0.5rem', display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
{dailyPuzzle.coverImage && (
<img
src={dailyPuzzle.coverImage}
alt="Album Cover"
style={{ width: '150px', height: '150px', objectFit: 'cover', borderRadius: '0.5rem', marginBottom: '1rem', boxShadow: '0 4px 6px rgba(0,0,0,0.1)' }}
/>
)}
<h3 style={{ fontSize: '1.125rem', fontWeight: 'bold', margin: '0 0 0.5rem 0' }}>{dailyPuzzle.title}</h3>
<p style={{ fontSize: '0.875rem', color: '#666', margin: '0 0 1rem 0' }}>{dailyPuzzle.artist}</p>
<audio controls style={{ width: '100%' }}>
<source src={dailyPuzzle.audioUrl} type="audio/mpeg" />
Your browser does not support the audio element.
</audio>
</div>
{statistics && <Statistics statistics={statistics} />}
<button onClick={handleShare} className="btn-primary" style={{ marginTop: '1rem' }}>
{shareText}
@@ -149,7 +170,25 @@ export default function Game({ dailyPuzzle }: GameProps) {
{hasLost && (
<div className="message-box failure">
<h2 style={{ fontSize: '1.5rem', fontWeight: 'bold', marginBottom: '0.5rem' }}>Game Over</h2>
<p>The song was hidden.</p>
<p>The song was:</p>
{/* Song Details */}
<div style={{ margin: '1.5rem 0', padding: '1rem', background: 'rgba(255,255,255,0.5)', borderRadius: '0.5rem', display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
{dailyPuzzle.coverImage && (
<img
src={dailyPuzzle.coverImage}
alt="Album Cover"
style={{ width: '150px', height: '150px', objectFit: 'cover', borderRadius: '0.5rem', marginBottom: '1rem', boxShadow: '0 4px 6px rgba(0,0,0,0.1)' }}
/>
)}
<h3 style={{ fontSize: '1.125rem', fontWeight: 'bold', margin: '0 0 0.5rem 0' }}>{dailyPuzzle.title}</h3>
<p style={{ fontSize: '0.875rem', color: '#666', margin: '0 0 1rem 0' }}>{dailyPuzzle.artist}</p>
<audio controls style={{ width: '100%' }}>
<source src={dailyPuzzle.audioUrl} type="audio/mpeg" />
Your browser does not support the audio element.
</audio>
</div>
{statistics && <Statistics statistics={statistics} />}
<button onClick={handleShare} className="btn-primary" style={{ marginTop: '1rem' }}>
{shareText}