feat: genre field in DetailPanel and ID3 TCON tag

- Added genre column to clips table (auto-migration)
- Genre field in DetailPanel metadata section (between Album and Comment)
- Genre saved via PATCH /api/clips/:id/metadata
- Genre embedded in ID3 TCON tag on download
- Placeholder text: 'e.g. Hardcore Techno, Phonk, Dark Ambient...'
This commit is contained in:
rin.kagamine committed 2026-05-31 11:18:37 -04:00
1 parent 1903d8bd2a
commit bef707f9a4
5 files changed
+29 -12

No files matched your search

+2 -2
View File
@@ -190,12 +190,12 @@ function App() {
}
}
const handleUpdateMetadata = async (id: string, artist: string, album: string, comment: string) => {
const handleUpdateMetadata = async (id: string, artist: string, album: string, genre: string, comment: string) => {
try {
const res = await fetch(`/api/clips/${id}/metadata`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ artist, album, comment }),
body: JSON.stringify({ artist, album, genre, comment }),
})
if (res.ok) {
const updated = await res.json()
+12 -5
View File
@@ -17,6 +17,7 @@ interface Clip {
prompt_structure?: Record<string, string> | null
artist?: string | null
album?: string | null
genre?: string | null
comment?: string | null
created_at: string
}
@@ -33,7 +34,7 @@ interface Props {
onPlay?: (clip: Clip) => void
onTogglePlay?: () => void
onGenerateArtwork?: (clip: Clip) => void
onUpdateMetadata?: (id: string, artist: string, album: string, comment: string) => void
onUpdateMetadata?: (id: string, artist: string, album: string, genre: string, comment: string) => void
}
const modelName = (modelId: string) => {
@@ -383,25 +384,31 @@ export default function DetailPanel({ clip, isPlaying, currentClipId, onClose, o
</div>
</div>
{/* Metadata Fields (Artist, Album, Comment) */}
{/* Metadata Fields (Artist, Album, Genre, Comment) */}
<div className="space-y-3">
<MetadataField
label="Artist"
value={clip.artist || ''}
placeholder="Artist name..."
onSave={(val) => onUpdateMetadata?.(clip.id, val, clip.album || '', clip.comment || '')}
onSave={(val) => onUpdateMetadata?.(clip.id, val, clip.album || '', clip.genre || '', clip.comment || '')}
/>
<MetadataField
label="Album"
value={clip.album || ''}
placeholder="Album name..."
onSave={(val) => onUpdateMetadata?.(clip.id, clip.artist || '', val, clip.comment || '')}
onSave={(val) => onUpdateMetadata?.(clip.id, clip.artist || '', val, clip.genre || '', clip.comment || '')}
/>
<MetadataField
label="Genre"
value={clip.genre || ''}
placeholder="e.g. Hardcore Techno, Phonk, Dark Ambient..."
onSave={(val) => onUpdateMetadata?.(clip.id, clip.artist || '', clip.album || '', val, clip.comment || '')}
/>
<MetadataField
label="Comment"
value={clip.comment || ''}
placeholder="Notes, credits, etc..."
onSave={(val) => onUpdateMetadata?.(clip.id, clip.artist || '', clip.album || '', val)}
onSave={(val) => onUpdateMetadata?.(clip.id, clip.artist || '', clip.album || '', clip.genre || '', val)}
/>
</div>