import { useState, useEffect, useCallback } from 'react'; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, } from '@/components/ui/dialog'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { Input } from '@/components/ui/input'; import { Button } from '@/components/ui/button'; import { Switch } from '@/components/ui/switch'; import { Label } from '@/components/ui/label'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { useAppStore } from '@/stores/app-store'; import { ExportDialogFooter } from './ExportDialogFooter'; import { toast } from '@/lib/toast'; type ToolKey = 'imagemagick' | 'ffmpeg'; interface FormatOption { value: string; label: string; } const converterFormats: Record = { imagemagick: { input: [ { value: 'png', label: 'PNG' }, { value: 'jpg', label: 'JPEG' }, { value: 'webp', label: 'WebP' }, { value: 'gif', label: 'GIF' }, { value: 'bmp', label: 'BMP' }, { value: 'tiff', label: 'TIFF' }, { value: 'svg', label: 'SVG' }, { value: 'ico', label: 'ICO' }, ], output: [ { value: 'png', label: 'PNG' }, { value: 'jpg', label: 'JPEG' }, { value: 'webp', label: 'WebP' }, { value: 'gif', label: 'GIF' }, { value: 'pdf', label: 'PDF' }, { value: 'bmp', label: 'BMP' }, { value: 'ico', label: 'ICO' }, ], }, ffmpeg: { input: [ { value: 'mp4', label: 'MP4' }, { value: 'mp3', label: 'MP3' }, { value: 'wav', label: 'WAV' }, { value: 'avi', label: 'AVI' }, { value: 'mkv', label: 'MKV' }, { value: 'flac', label: 'FLAC' }, { value: 'ogg', label: 'OGG' }, { value: 'mov', label: 'MOV' }, ], output: [ { value: 'mp4', label: 'MP4' }, { value: 'mp3', label: 'MP3' }, { value: 'wav', label: 'WAV' }, { value: 'avi', label: 'AVI' }, { value: 'mkv', label: 'MKV' }, { value: 'flac', label: 'FLAC' }, { value: 'ogg', label: 'OGG' }, { value: 'gif', label: 'GIF' }, { value: 'webm', label: 'WebM' }, ], }, }; const toolLabels: Record = { imagemagick: 'ImageMagick', ffmpeg: 'FFmpeg', }; export function BatchMediaConverterDialog() { const closeModal = useAppStore((s) => s.closeModal); const [tool, setTool] = useState('imagemagick'); const [fromFormat, setFromFormat] = useState(''); const [toFormat, setToFormat] = useState(''); const [inputFolder, setInputFolder] = useState(''); const [outputFolder, setOutputFolder] = useState(''); const [includeSubfolders, setIncludeSubfolders] = useState(false); const [converting, setConverting] = useState(false); const [progress, setProgress] = useState(0); const [error, setError] = useState(null); const inputFormats = converterFormats[tool].input; const outputFormats = converterFormats[tool].output; useEffect(() => { setFromFormat(''); setToFormat(''); }, [tool]); const handleBrowseInputFolder = useCallback(async () => { const result = await window.electronAPI?.file?.pickFolder?.(); if (typeof result === 'string') { setInputFolder(result); } }, []); const handleBrowseOutputFolder = useCallback(async () => { const result = await window.electronAPI?.file?.pickFolder?.(); if (typeof result === 'string') { setOutputFolder(result); } }, []); useEffect(() => { if (typeof window === 'undefined') return; const handlers: Array<() => void> = []; const batchProgressHandler = (_event: unknown, data: { current?: number; total?: number }) => { if (typeof data?.current === 'number' && typeof data?.total === 'number') { setProgress(Math.round((data.current / data.total) * 100)); } }; const completeHandler = (_event: unknown, data: { outputPath?: string; error?: string }) => { setConverting(false); setProgress(100); if (data?.error) { setError(data.error); toast.error(`Batch conversion failed: ${data.error}`); } else { toast.success('Batch conversion complete'); closeModal(); } }; const unsubBatch = window.electronAPI?.on?.('batch-progress', batchProgressHandler) ?? (() => {}); const unsubComplete = window.electronAPI?.on?.('conversion-complete', completeHandler) ?? (() => {}); handlers.push(unsubBatch, unsubComplete); return () => handlers.forEach((h) => h()); }, [closeModal]); const handleConvert = async () => { if (!fromFormat || !toFormat) { setError('Select both source and target formats'); return; } if (!inputFolder || !outputFolder) { setError('Select both input and output folders'); return; } setConverting(true); setProgress(0); setError(null); try { await window.electronAPI?.converter?.convertBatch?.({ tool, fromFormat, toFormat, inputFolder, outputFolder, includeSubfolders, }); } catch (err) { const msg = err instanceof Error ? err.message : String(err); setError(msg); toast.error(`Batch conversion failed: ${msg}`); setConverting(false); } }; return ( !o && closeModal()}> Batch Media Converter Convert multiple media files between formats using ImageMagick or FFmpeg
setTool(v as ToolKey)}> ImageMagick FFmpeg
setInputFolder(e.target.value)} placeholder="Input folder" className="flex-1" aria-label="Input folder" />
setOutputFolder(e.target.value)} placeholder="Output folder" className="flex-1" aria-label="Output folder" />
{converting && (

{progress}%

)} {error && (
{error}
)}
); }