import { useState, useEffect } from 'react' import Generator from './components/Generator' import Gallery from './components/Gallery' import Player from './components/Player' import DetailPanel from './components/DetailPanel' import SettingsModal from './components/SettingsModal' interface Clip { id: string title?: string prompt: string model: string duration_seconds: number status: string error_message?: string file_size_bytes?: number artwork_filename?: string | null created_at: string } function App() { const [clips, setClips] = useState([]) const [currentClip, setCurrentClip] = useState(null) const [isPlaying, setIsPlaying] = useState(false) const [selectedClip, setSelectedClip] = useState(null) const [settingsOpen, setSettingsOpen] = useState(false) const fetchClips = async () => { try { const res = await fetch('/api/clips') const data = await res.json() setClips(data.clips) } catch (err) { console.error('Failed to fetch clips:', err) } } useEffect(() => { fetchClips() const interval = setInterval(fetchClips, 5000) return () => clearInterval(interval) }, []) const handlePlay = (clip: Clip) => { setCurrentClip(clip) setIsPlaying(true) } const handleDelete = async (id: string) => { try { await fetch(`/api/clips/${id}`, { method: 'DELETE' }) if (currentClip?.id === id) { setCurrentClip(null) setIsPlaying(false) } fetchClips() } catch (err) { console.error('Failed to delete clip:', err) } } const handleRename = async (id: string, title: string) => { try { await fetch(`/api/clips/${id}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ title }), }) setClips(prev => prev.map(c => c.id === id ? { ...c, title } : c)) if (currentClip?.id === id) { setCurrentClip({ ...currentClip, title }) } if (selectedClip?.id === id) { setSelectedClip({ ...selectedClip, title }) } } catch (err) { console.error('Failed to rename clip:', err) } } const handleUploadArtwork = async (id: string, file: File) => { try { const formData = new FormData() formData.append('file', file) const res = await fetch(`/api/clips/${id}/artwork`, { method: 'PATCH', body: formData, }) if (!res.ok) { const err = await res.json() console.error('Artwork upload failed:', err.detail) return } const updated = await res.json() setClips(prev => prev.map(c => c.id === id ? { ...c, artwork_filename: updated.artwork_filename } : c)) if (currentClip?.id === id) { setCurrentClip({ ...currentClip, artwork_filename: updated.artwork_filename }) } if (selectedClip?.id === id) { setSelectedClip({ ...selectedClip, artwork_filename: updated.artwork_filename }) } } catch (err) { console.error('Failed to upload artwork:', err) } } const handleRemoveArtwork = async (id: string) => { try { await fetch(`/api/clips/${id}/artwork`, { method: 'DELETE' }) setClips(prev => prev.map(c => c.id === id ? { ...c, artwork_filename: null } : c)) if (currentClip?.id === id) { setCurrentClip({ ...currentClip, artwork_filename: null }) } if (selectedClip?.id === id) { setSelectedClip({ ...selectedClip, artwork_filename: null }) } } catch (err) { console.error('Failed to remove artwork:', err) } } const handleClearWaveforms = async () => { try { await fetch('/api/admin/clear-waveforms', { method: 'POST' }) } catch (err) { console.error('Failed to clear waveforms:', err) } } const handleDeleteAll = async () => { try { await fetch('/api/admin/delete-all-clips', { method: 'POST' }) setClips([]) setCurrentClip(null) setIsPlaying(false) setSelectedClip(null) } catch (err) { console.error('Failed to delete all clips:', err) } } return (
L

Lyria Studio

v0.1
setSelectedClip(clip)} onUploadArtwork={handleUploadArtwork} currentClipId={currentClip?.id} />
{currentClip && ( setIsPlaying(!isPlaying)} onStop={() => setIsPlaying(false)} onClose={() => { setCurrentClip(null) setIsPlaying(false) }} /> )} {selectedClip && ( setSelectedClip(null)} onRename={handleRename} onDelete={handleDelete} onUploadArtwork={handleUploadArtwork} onRemoveArtwork={handleRemoveArtwork} /> )} setSettingsOpen(false)} onClearWaveforms={handleClearWaveforms} onDeleteAll={handleDeleteAll} clipCount={clips.length} />
) } export default App