import React, { useState } from 'react' import { Upload, Play, Folder, FileText, Clock, Cpu, Trash2, Download } from 'lucide-react' import { useAIVideoStore, useAIVideoJobs, useAIVideoProcessing, useAIVideoSettings } from '../stores/useAIVideoStore' import { invoke } from '@tauri-apps/api/core' interface AIVideoGeneratorProps { className?: string } const AIVideoGenerator: React.FC = ({ className = '' }) => { // State const [mode, setMode] = useState<'single' | 'batch'>('single') const [selectedImage, setSelectedImage] = useState('') const [selectedFolder, setSelectedFolder] = useState('') const [customPrompt, setCustomPrompt] = useState('') const [outputFolder, setOutputFolder] = useState('') const [duration, setDuration] = useState('5') const [modelType, setModelType] = useState('lite') // Store const { generateSingleVideo, batchGenerateVideos, removeJob, clearCompletedJobs, setDefaultDuration, setDefaultModelType } = useAIVideoStore() const jobs = useAIVideoJobs() const isProcessing = useAIVideoProcessing() const { defaultPrompts, defaultDuration, defaultModelType } = useAIVideoSettings() // Initialize settings React.useEffect(() => { setDuration(defaultDuration) setModelType(defaultModelType) }, [defaultDuration, defaultModelType]) // Handle file selection const handleImageSelect = async () => { try { console.log('Opening file dialog...') const filePath = await invoke('select_image_file') as string console.log('Selected image:', filePath) setSelectedImage(filePath) } catch (error) { console.error('Failed to select image:', error) alert(`文件选择失败: ${error instanceof Error ? error.message : '未知错误'}`) } } const handleFolderSelect = async () => { try { console.log('Opening folder dialog...') const folderPath = await invoke('select_folder') as string console.log('Selected folder:', folderPath) setSelectedFolder(folderPath) } catch (error) { console.error('Failed to select folder:', error) alert(`文件夹选择失败: ${error instanceof Error ? error.message : '未知错误'}`) } } // Handle generation const handleGenerate = async () => { try { if (mode === 'single') { if (!selectedImage || !customPrompt) { alert('请选择图片文件并输入提示词') return } // Validate file path if (!selectedImage.includes('\\') && !selectedImage.includes('/')) { alert('请选择完整的文件路径,而不是仅仅文件名。\n\n请点击"选择文件"按钮来选择图片文件。') return } console.log('Generating video with:', { image_path: selectedImage, prompt: customPrompt, duration, model_type: modelType, output_path: outputFolder }) await generateSingleVideo({ image_path: selectedImage, prompt: customPrompt, duration, model_type: modelType, output_path: outputFolder || undefined, timeout: 300 }) } else { if (!selectedFolder || !outputFolder) { alert('请选择图片文件夹和输出目录') return } const prompts = customPrompt ? customPrompt.split('\n').filter(p => p.trim()) : defaultPrompts await batchGenerateVideos({ image_folder: selectedFolder, prompts, output_folder: outputFolder, duration, model_type: modelType, timeout: 300 }) } } catch (error) { console.error('Generation failed:', error) let errorMessage = '未知错误' if (error instanceof Error) { errorMessage = error.message } else if (typeof error === 'string') { errorMessage = error } // Try to extract more details from the error const errorDetails = { message: errorMessage, type: error instanceof Error ? error.name : typeof error, stack: error instanceof Error ? error.stack : undefined, timestamp: new Date().toISOString() } console.error('Detailed error information:', errorDetails) alert(`生成失败: ${errorMessage}\n\n详细信息已输出到控制台,请检查开发者工具。`) } } // Format time const formatTime = (timestamp: number): string => { return new Date(timestamp).toLocaleTimeString() } // Format duration const formatDuration = (start: number, end?: number): string => { if (!end) return '进行中...' const duration = Math.round((end - start) / 1000) return `${duration}秒` } return (
{/* Header */}
{/* Input Section */}
{mode === 'single' ? (
{selectedImage ? `已选择: ${selectedImage.split(/[/\\]/).pop()}` : '未选择文件'}
setSelectedImage(e.target.value)} placeholder="或手动输入完整的图片文件路径 (例如: C:\Users\用户名\Pictures\image.jpg)" className="input w-full text-sm" />
) : ( <>
{selectedFolder || '未选择文件夹'}
setOutputFolder(e.target.value)} placeholder="输入保存目录路径" className="input flex-1" />
)} {/* Prompt Input */}