From 3317a690040bb1680df2426eb1ad22d20101b9f7 Mon Sep 17 00:00:00 2001 From: Geert Rademakes Date: Wed, 6 Aug 2025 15:01:40 +0200 Subject: [PATCH] feat: Update Music Storage page to use dark theme - Update MusicStorage page with dark theme colors (gray.900, gray.800, gray.700) - Update MusicUpload component with dark theme styling - Update SongMatching component with dark theme colors - Match the color scheme of the main browser page - Use consistent text colors (white, gray.100, gray.400, gray.500) - Update cards, buttons, and alerts with dark backgrounds - Improve hover states and visual consistency The Music Storage page now has a consistent dark theme that matches the main Rekordbox Reader interface. --- .../frontend/src/components/MusicUpload.tsx | 39 +- .../frontend/src/components/SongMatching.tsx | 716 +++++++++--------- packages/frontend/src/pages/MusicStorage.tsx | 76 +- 3 files changed, 430 insertions(+), 401 deletions(-) diff --git a/packages/frontend/src/components/MusicUpload.tsx b/packages/frontend/src/components/MusicUpload.tsx index 6b021f4..6b35b27 100644 --- a/packages/frontend/src/components/MusicUpload.tsx +++ b/packages/frontend/src/components/MusicUpload.tsx @@ -123,7 +123,7 @@ export const MusicUpload: React.FC = ({ onUploadComplete }) => = ({ onUploadComplete }) => transition="all 0.2s" _hover={{ borderColor: 'blue.400', - bg: 'blue.50', + bg: 'blue.900', }} - bg={isDragActive ? 'blue.50' : 'transparent'} + bg={isDragActive ? 'blue.900' : 'gray.800'} > - - + + {isDragActive ? 'Drop the music files here...' : 'Drag & drop music files here, or click to select'} - + Supports MP3, WAV, FLAC, AAC, OGG, WMA, Opus (max 100MB per file) @@ -152,30 +152,30 @@ export const MusicUpload: React.FC = ({ onUploadComplete }) => {uploadProgress.length > 0 && ( - Upload Progress - {uploadProgress.map((item, index) => ( - + - + {item.fileName} {item.status === 'error' ? ( - - + + - Upload failed - {item.error} + Upload failed + {item.error} ) : ( @@ -183,6 +183,7 @@ export const MusicUpload: React.FC = ({ onUploadComplete }) => value={item.progress} colorScheme={item.status === 'success' ? 'green' : 'blue'} size="sm" + bg="gray.700" /> )} @@ -191,11 +192,11 @@ export const MusicUpload: React.FC = ({ onUploadComplete }) => )} {isUploading && ( - - + + - Uploading files... - + Uploading files... + Please wait while your music files are being uploaded and processed. diff --git a/packages/frontend/src/components/SongMatching.tsx b/packages/frontend/src/components/SongMatching.tsx index 9c6d6e0..3ead7f5 100644 --- a/packages/frontend/src/components/SongMatching.tsx +++ b/packages/frontend/src/components/SongMatching.tsx @@ -31,6 +31,7 @@ import { StatHelpText, IconButton, Tooltip, + Spinner, } from '@chakra-ui/react'; import { FiPlay, FiLink, FiSearch, FiZap, FiMusic, FiCheck, FiX } from 'react-icons/fi'; @@ -273,368 +274,373 @@ export const SongMatching: React.FC = () => { } return ( - - - - 🎵 Song Matching & Linking - - - {/* Statistics */} - {stats && ( - - - Total Songs - {stats.totalSongs} - In Rekordbox library - - - Music Files - {stats.totalMusicFiles} - Uploaded to S3 - - - Match Rate - {stats.matchRate}% - {stats.matchedMusicFiles} of {stats.totalMusicFiles} linked - - - )} - - {/* Auto-linking */} - + + {/* Statistics */} + {stats && ( + - - Auto-Linking - - + Matching Statistics - - Automatically match and link music files to songs in your Rekordbox library. - This will attempt to find matches based on filename, title, artist, and other metadata. - Original file paths are preserved and S3 information is added alongside. + + + Total Songs + {stats.totalSongs} + + + Music Files + {stats.totalMusicFiles} + + + Match Rate + {stats.matchRate} + + {stats.matchedMusicFiles} of {stats.totalMusicFiles} files matched + + + + Unmatched + {stats.unmatchedMusicFiles} + + {stats.songsWithoutMusicFiles} songs without files + + + + + + )} + + {/* Auto-Link Button */} + + + + + Automatically match and link music files to songs in your Rekordbox library - - + + + + - {/* Unmatched Music Files */} - - - Unmatched Music Files ({unmatchedMusicFiles.length}) - - - {unmatchedMusicFiles.length === 0 ? ( - - All music files have been matched! 🎉 - - ) : ( - - {unmatchedMusicFiles.slice(0, 10).map((musicFile) => ( - - - - - {musicFile.title || musicFile.originalName} - - {musicFile.artist && ( - - {musicFile.artist} - - )} - {musicFile.album && ( - - {musicFile.album} - - )} - - {formatDuration(musicFile.duration || 0)} - - {musicFile.format?.toUpperCase()} - - - - - } - size="sm" - variant="ghost" - onClick={() => handleGetSuggestions(musicFile)} - /> - - - } - size="sm" - variant="ghost" - colorScheme="blue" - /> - - - - - ))} - {unmatchedMusicFiles.length > 10 && ( - - Showing first 10 of {unmatchedMusicFiles.length} unmatched files - - )} - - )} - - - - {/* Matched Music Files */} - - - Matched Music Files ({matchedMusicFiles.length}) - - - {matchedMusicFiles.length === 0 ? ( - - No music files have been matched yet. - - ) : ( - - {matchedMusicFiles.slice(0, 10).map((musicFile) => ( - - - - - - {musicFile.title || musicFile.originalName} - - - - Linked - - - {musicFile.artist && ( - - {musicFile.artist} - - )} - {musicFile.songId && ( - - → {musicFile.songId.title} by {musicFile.songId.artist} - - )} - {musicFile.songId?.location && ( - - 📁 {musicFile.songId.location} - - )} - - - - } - size="sm" - variant="ghost" - colorScheme="red" - onClick={() => handleUnlinkMusicFile(musicFile.songId._id)} - /> - - - } - size="sm" - variant="ghost" - colorScheme="blue" - /> - - - - - ))} - {matchedMusicFiles.length > 10 && ( - - Showing first 10 of {matchedMusicFiles.length} matched files - - )} - - )} - - - - {/* Songs with Music Files */} - - - Songs with Music Files ({songsWithMusicFiles.length}) - - - {songsWithMusicFiles.length === 0 ? ( - - No songs have music files linked yet. - - ) : ( - - {songsWithMusicFiles.slice(0, 10).map((song) => ( - - - - - - {song.title} - - - - Has S3 File - - - - {song.artist} - - {song.location && ( - - 📁 {song.location} - - )} - {song.s3File?.streamingUrl && ( - - 🎵 S3: {song.s3File.s3Key} - - )} - - - - } - size="sm" - variant="ghost" - colorScheme="red" - onClick={() => handleUnlinkMusicFile(song._id)} - /> - - - } - size="sm" - variant="ghost" - colorScheme="blue" - /> - - - - - ))} - {songsWithMusicFiles.length > 10 && ( - - Showing first 10 of {songsWithMusicFiles.length} songs with music files - - )} - - )} - - - - {/* Suggestions Modal */} - - - - - Matching Suggestions for "{selectedMusicFile?.title || selectedMusicFile?.originalName}" - - - - {loadingSuggestions ? ( - - ) : suggestions.length === 0 ? ( - - No matching suggestions found. - - ) : ( - - {suggestions.map((match, index) => ( - - - - - - {match.song.title} - - - {(match.confidence * 100).toFixed(0)}% - - - - {match.song.artist} - - {match.song.album && ( - - {match.song.album} - - )} - {match.song.location && ( - - 📁 {match.song.location} - - )} - - {match.matchReason} - - - - - - ))} - + onClick={() => handleGetSuggestions(file)} + _hover={{ bg: "blue.900" }} + /> + + + } + size="sm" + variant="ghost" + colorScheme="green" + _hover={{ bg: "green.900" }} + /> + + + + + ))} + {unmatchedMusicFiles.length > 10 && ( + + Showing first 10 of {unmatchedMusicFiles.length} unmatched files + )} - - - - - - - - + + )} + + + + {/* Matched Music Files */} + + + Matched Music Files ({matchedMusicFiles.length}) + + + {matchedMusicFiles.length === 0 ? ( + + No music files are matched yet. + + ) : ( + + {matchedMusicFiles.slice(0, 10).map((file) => ( + + + + + + {file.title || file.originalName} + + + + Matched + + + + {file.artist} + + + {file.album} + + + + + } + size="sm" + variant="ghost" + colorScheme="red" + onClick={() => handleUnlinkMusicFile(file.songId)} + _hover={{ bg: "red.900" }} + /> + + + } + size="sm" + variant="ghost" + colorScheme="blue" + _hover={{ bg: "blue.900" }} + /> + + + + + ))} + {matchedMusicFiles.length > 10 && ( + + Showing first 10 of {matchedMusicFiles.length} matched files + + )} + + )} + + + + {/* Songs with Music Files */} + + + Songs with Music Files ({songsWithMusicFiles.length}) + + + {songsWithMusicFiles.length === 0 ? ( + + No songs have music files linked yet. + + ) : ( + + {songsWithMusicFiles.slice(0, 10).map((song) => ( + + + + + + {song.title} + + + + Has S3 File + + + + {song.artist} + + {song.location && ( + + 📁 {song.location} + + )} + {song.s3File?.streamingUrl && ( + + 🎵 S3: {song.s3File.s3Key} + + )} + + + + } + size="sm" + variant="ghost" + colorScheme="red" + onClick={() => handleUnlinkMusicFile(song._id)} + _hover={{ bg: "red.900" }} + /> + + + } + size="sm" + variant="ghost" + colorScheme="blue" + _hover={{ bg: "blue.800" }} + /> + + + + + ))} + {songsWithMusicFiles.length > 10 && ( + + Showing first 10 of {songsWithMusicFiles.length} songs with music files + + )} + + )} + + + + {/* Suggestions Modal */} + + + + + Matching Suggestions for "{selectedMusicFile?.title || selectedMusicFile?.originalName}" + + + + {loadingSuggestions ? ( + + + Finding matching songs... + + ) : suggestions.length === 0 ? ( + + No matching songs found. You can manually link this file later. + + ) : ( + + {suggestions.map((suggestion, index) => ( + + + + + + {suggestion.song.title} + + + {Math.round(suggestion.confidence * 100)}% + + + + {suggestion.song.artist} + + {suggestion.song.location && ( + + 📁 {suggestion.song.location} + + )} + + {suggestion.matchReason} + + + + } + size="sm" + variant="ghost" + colorScheme="blue" + onClick={() => { + handleLinkMusicFile(selectedMusicFile._id, suggestion.song._id); + onClose(); + }} + _hover={{ bg: "blue.900" }} + /> + + + + ))} + + )} + + + + + + + ); }; \ No newline at end of file diff --git a/packages/frontend/src/pages/MusicStorage.tsx b/packages/frontend/src/pages/MusicStorage.tsx index 9ff135d..d7b45e1 100644 --- a/packages/frontend/src/pages/MusicStorage.tsx +++ b/packages/frontend/src/pages/MusicStorage.tsx @@ -171,40 +171,55 @@ export const MusicStorage: React.FC = () => { }; return ( - + - + 🎵 Music Storage & Playback - - + + - S3 Storage Feature - + S3 Storage Feature + Upload your music files to S3-compatible storage (MinIO) and stream them directly in the browser. Supports MP3, WAV, FLAC, AAC, OGG, WMA, and Opus formats. - - - Upload Music - Music Library - Song Matching - Player + + + + Upload Music + + + Music Library + + + Song Matching + + + Player + {/* Upload Tab */} - + - + Upload Music Files - + Drag and drop your music files here or click to select. Files will be uploaded to S3 storage and metadata will be automatically extracted. @@ -214,21 +229,23 @@ export const MusicStorage: React.FC = () => { {/* Library Tab */} - + - Music Library + Music Library - + {musicFiles.length} file{musicFiles.length !== 1 ? 's' : ''} @@ -250,6 +267,8 @@ export const MusicStorage: React.FC = () => { onClick={handleSyncS3} isLoading={isSyncing} loadingText="Syncing..." + colorScheme="blue" + _hover={{ bg: "blue.700" }} > Sync S3 Bucket @@ -257,10 +276,10 @@ export const MusicStorage: React.FC = () => { ) : ( {musicFiles.map((file) => ( - + - + {file.format?.toUpperCase() || 'AUDIO'} { variant="ghost" colorScheme="red" onClick={() => handleDeleteFile(file._id)} + _hover={{ bg: "red.900" }} /> - + {file.title || file.originalName} {file.artist && ( - + {file.artist} )} @@ -293,7 +313,7 @@ export const MusicStorage: React.FC = () => { {formatFileSize(file.size)} {file.songId && ( - + Linked to Rekordbox )} @@ -303,6 +323,7 @@ export const MusicStorage: React.FC = () => { size="sm" colorScheme="blue" onClick={() => setSelectedFile(file)} + _hover={{ bg: "blue.700" }} /> @@ -314,14 +335,14 @@ export const MusicStorage: React.FC = () => { {/* Song Matching Tab */} - + {/* Player Tab */} - + - Music Player + Music Player {selectedFile ? ( { p={8} textAlign="center" border="2px dashed" - borderColor="gray.300" + borderColor="gray.600" borderRadius="lg" color="gray.500" + bg="gray.800" > Select a music file from the library to start playing