'use client'; import * as React from 'react'; import { Upload, X, File, FileVideo, FileAudio, FileImage, Clock, HardDrive, Film } from 'lucide-react'; import { cn } from '@/lib/utils/cn'; import { Button } from '@/components/ui/button'; import type { ConversionFormat } from '@/types/media'; export interface FileUploadProps { onFileSelect: (files: File[]) => void; onFileRemove: (index: number) => void; selectedFiles?: File[]; accept?: string; maxSizeMB?: number; disabled?: boolean; inputRef?: React.RefObject; inputFormat?: ConversionFormat; } export function FileUpload({ onFileSelect, onFileRemove, selectedFiles = [], accept, maxSizeMB = 500, disabled = false, inputRef, inputFormat, }: FileUploadProps) { const [isDragging, setIsDragging] = React.useState(false); const [fileMetadata, setFileMetadata] = React.useState>({}); const localFileInputRef = React.useRef(null); const fileInputRef = inputRef || localFileInputRef; // Extract metadata for files React.useEffect(() => { const extractMetadata = async () => { if (selectedFiles.length === 0 || !inputFormat) { setFileMetadata({}); return; } const metadata: Record = {}; for (let i = 0; i < selectedFiles.length; i++) { const file = selectedFiles[i]; const baseMetadata = { name: file.name, size: file.size < 1024 * 1024 ? `${(file.size / 1024).toFixed(2)} KB` : `${(file.size / (1024 * 1024)).toFixed(2)} MB`, type: inputFormat.name, }; // Extract media-specific metadata if (inputFormat.category === 'video' && file.type.startsWith('video/')) { try { const video = document.createElement('video'); video.preload = 'metadata'; const metadataPromise = new Promise((resolve) => { video.onloadedmetadata = () => { const duration = video.duration; const minutes = Math.floor(duration / 60); const seconds = Math.floor(duration % 60); resolve({ ...baseMetadata, duration: `${minutes}:${seconds.toString().padStart(2, '0')}`, dimensions: `${video.videoWidth} × ${video.videoHeight}`, }); URL.revokeObjectURL(video.src); }; video.onerror = () => { resolve(baseMetadata); URL.revokeObjectURL(video.src); }; }); video.src = URL.createObjectURL(file); metadata[i] = await metadataPromise; } catch (error) { metadata[i] = baseMetadata; } } else if (inputFormat.category === 'audio' && file.type.startsWith('audio/')) { try { const audio = document.createElement('audio'); audio.preload = 'metadata'; const metadataPromise = new Promise((resolve) => { audio.onloadedmetadata = () => { const duration = audio.duration; const minutes = Math.floor(duration / 60); const seconds = Math.floor(duration % 60); resolve({ ...baseMetadata, duration: `${minutes}:${seconds.toString().padStart(2, '0')}`, }); URL.revokeObjectURL(audio.src); }; audio.onerror = () => { resolve(baseMetadata); URL.revokeObjectURL(audio.src); }; }); audio.src = URL.createObjectURL(file); metadata[i] = await metadataPromise; } catch (error) { metadata[i] = baseMetadata; } } else if (inputFormat.category === 'image' && file.type.startsWith('image/')) { try { const img = new Image(); const metadataPromise = new Promise((resolve) => { img.onload = () => { resolve({ ...baseMetadata, dimensions: `${img.width} × ${img.height}`, }); URL.revokeObjectURL(img.src); }; img.onerror = () => { resolve(baseMetadata); URL.revokeObjectURL(img.src); }; }); img.src = URL.createObjectURL(file); metadata[i] = await metadataPromise; } catch (error) { metadata[i] = baseMetadata; } } else { metadata[i] = baseMetadata; } } setFileMetadata(metadata); }; extractMetadata(); }, [selectedFiles, inputFormat]); const getCategoryIcon = () => { if (!inputFormat) return ; switch (inputFormat.category) { case 'video': return ; case 'audio': return ; case 'image': return ; default: return ; } }; const handleDragEnter = (e: React.DragEvent) => { e.preventDefault(); e.stopPropagation(); if (!disabled) { setIsDragging(true); } }; const handleDragLeave = (e: React.DragEvent) => { e.preventDefault(); e.stopPropagation(); setIsDragging(false); }; const handleDragOver = (e: React.DragEvent) => { e.preventDefault(); e.stopPropagation(); }; const handleDrop = (e: React.DragEvent) => { e.preventDefault(); e.stopPropagation(); setIsDragging(false); if (disabled) return; const files = Array.from(e.dataTransfer.files); if (files.length > 0) { handleFiles(files); } }; const handleFileInput = (e: React.ChangeEvent) => { const files = Array.from(e.target.files || []); if (files.length > 0) { handleFiles(files); } }; const handleFiles = (files: File[]) => { // Check file sizes const maxBytes = maxSizeMB * 1024 * 1024; const validFiles = files.filter(file => { if (file.size > maxBytes) { alert(`${file.name} exceeds ${maxSizeMB}MB limit and will be skipped.`); return false; } return true; }); if (validFiles.length > 0) { onFileSelect(validFiles); } // Reset input if (fileInputRef.current) { fileInputRef.current.value = ''; } }; const handleClick = () => { if (!disabled) { fileInputRef.current?.click(); } }; const handleRemove = (index: number) => (e: React.MouseEvent) => { e.stopPropagation(); onFileRemove(index); }; return (
{selectedFiles.length > 0 ? (
{selectedFiles.map((file, index) => { const metadata = fileMetadata[index]; return (
{getCategoryIcon()}

{file.name}

{metadata && (
{/* File Size */}
{metadata.size}
{/* Type */}
{metadata.type}
{/* Duration (for video/audio) */} {metadata.duration && (
{metadata.duration}
)} {/* Dimensions */} {metadata.dimensions && (
{inputFormat?.category === 'video' ? ( ) : ( )} {metadata.dimensions}
)}
)}
); })} {/* Add more files button */}
) : (

Drop your files here or click to browse

Maximum file size: {maxSizeMB}MB per file

)}
); }