import React, { useState, useEffect, useCallback } from 'react'; import { X, Music, Play, Pause, Clock, Loader2 } from 'lucide-react'; import { useEditor } from '../../context/EditorContext'; import { FileDropZone } from '../ui/FileDropZone'; import { uploadMedia } from '../../utils/mediaUploader'; import { useAudioPreview } from '../../hooks/useAudioPreview'; import { getAudioDuration, formatDuration } from '../../utils/audioMetadata'; import { AudioWaveformCanvas } from '../timeline/AudioWaveformCanvas'; interface AudioPanelProps { onClose: () => void; } interface AudioItem { src: string; name: string; duration: number | null; } /** * Panel for adding audio files. Draggable to timeline. * Auto-routes to audio layers. Supports preview and waveform. */ export const AudioPanel: React.FC = ({ onClose }) => { const { designMD } = useEditor(); const [localAudios, setLocalAudios] = useState([]); const [brandDuration, setBrandDuration] = useState(null); const preview = useAudioPreview(); const [playingIdx, setPlayingIdx] = useState(null); const [isUploading, setIsUploading] = useState(false); // Load brand audio duration useEffect(() => { if (designMD.brandAudioUrl) { getAudioDuration(designMD.brandAudioUrl).then(d => setBrandDuration(d)); } }, [designMD.brandAudioUrl]); const handleUpload = useCallback(async (files: File[]) => { const audioFiles = files.filter(f => f.type.startsWith('audio/')); if (audioFiles.length === 0) return; setIsUploading(true); try { const items: AudioItem[] = []; for (const file of audioFiles) { const result = await uploadMedia(file); let duration: number | null = null; try { duration = await getAudioDuration(result.url); } catch {} items.push({ src: result.url, name: result.originalName, duration }); } setLocalAudios(prev => [...items, ...prev]); } catch (err) { console.error('Audio upload failed:', err); } finally { setIsUploading(false); } }, []); const handleTogglePreview = useCallback((src: string, idx: number) => { if (playingIdx === idx) { preview.pause(); setPlayingIdx(null); } else { preview.setSrc(src); preview.play(); setPlayingIdx(idx); } }, [playingIdx, preview]); // Stop preview when panel closes useEffect(() => { return () => { preview.pause(); }; }, []); const allAudios = [...localAudios]; const hasBrandAudio = !!designMD.brandAudioUrl; return (

Audio

{/* Upload */} {isUploading && (
Subiendo al servidor...
)} {/* Brand Audio */} {hasBrandAudio && (
Audio de Marca
{ e.dataTransfer.setData('text/plain', designMD.brandAudioUrl!); e.dataTransfer.setData('application/json', JSON.stringify({ type: 'audio', src: designMD.brandAudioUrl })); e.dataTransfer.effectAllowed = 'copy'; }} >
Jingle de Marca
Arrastrar al timeline {brandDuration !== null && ( {formatDuration(brandDuration)} )}
{/* Mini Waveform */}
)} {/* Uploaded Audios */} {localAudios.length > 0 && (
Mis Audios
{localAudios.map((audio, i) => (
{ e.dataTransfer.setData('text/plain', audio.src); e.dataTransfer.setData('application/json', JSON.stringify({ type: 'audio', src: audio.src, fileName: audio.name, })); e.dataTransfer.effectAllowed = 'copy'; }} >
{audio.name}
Arrastrar al timeline {audio.duration !== null && ( {formatDuration(audio.duration)} )}
{/* Mini Waveform */}
))}
)} {/* Empty state */} {!hasBrandAudio && localAudios.length === 0 && (

Sin audio disponible

Sube archivos de audio o configura el jingle de marca

)}
); };