Spaces:
Build error
Build error
| import React, { useState, useCallback } from 'react'; | |
| const VideoGeneratorForm = ({ onGenerate }) => { | |
| const [prompt, setPrompt] = useState(''); | |
| const [duration, setDuration] = useState(15); // Default 15 seconds | |
| const [loading, setLoading] = useState(false); | |
| const handleSubmit = useCallback(async (e) => { | |
| e.preventDefault(); | |
| if (!prompt.trim()) { | |
| alert("Please enter a text prompt."); | |
| return; | |
| } | |
| setLoading(true); | |
| onGenerate({ status: 'loading' }); | |
| try { | |
| const response = await fetch('/api/generate', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| }, | |
| body: JSON.stringify({ prompt, duration }), | |
| }); | |
| const data = await response.json(); | |
| if (response.ok) { | |
| onGenerate({ status: 'success', data }); | |
| } else { | |
| onGenerate({ status: 'error', error: data.error || 'Generation failed.' }); | |
| } | |
| } catch (error) { | |
| console.error('API Error:', error); | |
| onGenerate({ status: 'error', error: 'Network error or service unavailable.' }); | |
| } finally { | |
| setLoading(false); | |
| } | |
| }, [prompt, duration, onGenerate]); | |
| return ( | |
| <form onSubmit={handleSubmit} className="p-6 bg-gray-800 rounded-xl shadow-2xl space-y-6"> | |
| <h2 className="text-2xl font-semibold text-white">Generate Video</h2> | |
| {/* Text Prompt */} | |
| <div> | |
| <label htmlFor="prompt" className="block text-sm font-medium text-gray-300 mb-2"> | |
| Text Prompt (The scene description) | |
| </label> | |
| <textarea | |
| id="prompt" | |
| value={prompt} | |
| onChange={(e) => setPrompt(e.target.value)} | |
| rows="3" | |
| placeholder="A majestic golden retriever wearing a tiny astronaut helmet floating through space, viewed in cinematic 4K." | |
| className="w-full p-3 border border-gray-600 rounded-lg bg-gray-700 text-white focus:ring-purple-500 focus:border-purple-500 transition duration-150 ease-in-out" | |
| required | |
| aria-describedby="prompt-description" | |
| /> | |
| <p id="prompt-description" className="text-xs text-gray-500 mt-1"> | |
| Describe your desired video, including style, subjects, and movement. | |
| </p> | |
| </div> | |
| {/* Duration Slider */} | |
| <div> | |
| <label htmlFor="duration" className="block text-sm font-medium text-gray-300 mb-2"> | |
| Video Duration: <span className="font-bold text-purple-400">{duration} seconds</span> (4s to 120s) | |
| </label> | |
| <input | |
| type="range" | |
| id="duration" | |
| min="4" | |
| max="120" | |
| value={duration} | |
| onChange={(e) => setDuration(Number(e.target.value))} | |
| className="w-full h-2 bg-gray-700 rounded-lg appearance-none cursor-pointer range-lg focus:outline-none focus:ring-2 focus:ring-purple-500" | |
| /> | |
| <div className="flex justify-between text-xs text-gray-500 mt-1"> | |
| <span>4s</span> | |
| <span>120s</span> | |
| </div> | |
| </div> | |
| {/* Image Upload (Placeholder) */} | |
| <div className="border-2 border-dashed border-gray-600 p-6 rounded-lg text-center bg-gray-700/50"> | |
| <p className="text-gray-400 font-medium">Image-to-Video Input (Optional)</p> | |
| <p className="text-sm text-gray-500 mt-1"> | |
| Drag and drop an image or click to upload (Feature placeholder). | |
| </p> | |
| </div> | |
| <button | |
| type="submit" | |
| disabled={loading || !prompt.trim()} | |
| className="w-full py-3 px-4 rounded-lg text-white font-semibold transition-all duration-200 ease-in-out | |
| bg-purple-600 hover:bg-purple-700 focus:ring-4 focus:ring-purple-500 focus:ring-opacity-50 | |
| disabled:bg-gray-500 disabled:cursor-not-allowed flex justify-center items-center" | |
| aria-live="polite" | |
| > | |
| {loading ? ( | |
| <> | |
| <svg className="animate-spin -ml-1 mr-3 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"> | |
| <circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle> | |
| <path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path> | |
| </svg> | |
| Generating Video... | |
| </> | |
| ) : ( | |
| 'Start Sora 2 Generation' | |
| )} | |
| </button> | |
| </form> | |
| ); | |
| }; | |
| export default VideoGeneratorForm; |