From c2ee01b7b7e6278d61395b55ee272043e4b287de Mon Sep 17 00:00:00 2001 From: "Khoa.vo" Date: Tue, 6 Jan 2026 14:51:08 +0700 Subject: [PATCH] feat: enable Subject upload for Meta AI video generation - Subject button now enabled for Meta AI (Scene/Style still disabled) - Video button appears when Subject reference is uploaded - Uses Subject image for image-to-video generation - Added handleGenerateVideo() in PromptHero - Tooltip: 'Upload image to animate into video' --- components/PromptHero.tsx | 121 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 117 insertions(+), 4 deletions(-) diff --git a/components/PromptHero.tsx b/components/PromptHero.tsx index 1b448e4..67f51ac 100644 --- a/components/PromptHero.tsx +++ b/components/PromptHero.tsx @@ -3,7 +3,7 @@ import React, { useRef, useState, useEffect } from "react"; import { useStore, ReferenceCategory } from "@/lib/store"; import { cn } from "@/lib/utils"; -import { Sparkles, Maximize2, X, Hash, AlertTriangle, Upload, Zap, Brain, Settings, Settings2 } from "lucide-react"; +import { Sparkles, Maximize2, X, Hash, AlertTriangle, Upload, Zap, Brain, Settings, Settings2, Video } from "lucide-react"; const IMAGE_COUNTS = [1, 2, 4]; @@ -19,6 +19,8 @@ export function PromptHero() { } = useStore(); const [isGenerating, setLocalIsGenerating] = useState(false); + const [isGeneratingVideo, setIsGeneratingVideo] = useState(false); + const { addVideo } = useStore(); const [uploadingRefs, setUploadingRefs] = useState>({}); const [errorNotification, setErrorNotification] = useState<{ message: string; type: 'error' | 'warning' } | null>(null); @@ -205,6 +207,80 @@ export function PromptHero() { } }; + // Handle video generation (Meta AI only) - uses Subject reference image + const handleGenerateVideo = async () => { + const subjectRefs = references.subject || []; + if (subjectRefs.length === 0) { + setErrorNotification({ message: '📷 Please upload a Subject image first', type: 'warning' }); + setTimeout(() => setErrorNotification(null), 4000); + return; + } + + const finalPrompt = prompt.trim() || "Animate this image with natural, cinematic movement"; + + if (!settings.metaCookies) { + setShowCookieExpired(true); + return; + } + + setIsGeneratingVideo(true); + setIsGenerating(true); + + try { + const imageBase64 = subjectRefs[0].thumbnail; + console.log('[PromptHero] Starting Meta AI image-to-video...'); + + const res = await fetch('/api/meta/video', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + prompt: finalPrompt, + cookies: settings.metaCookies, + imageBase64: imageBase64 + }) + }); + + const data = await res.json(); + + if (data.error) throw new Error(data.error); + + if (data.success && data.videos?.length > 0) { + for (const video of data.videos) { + addVideo({ + id: crypto.randomUUID(), + url: video.url, + prompt: video.prompt || finalPrompt, + thumbnail: imageBase64, + createdAt: Date.now() + }); + } + setErrorNotification({ + message: `🎬 Video generated! Check the gallery.`, + type: 'warning' + }); + setTimeout(() => setErrorNotification(null), 5000); + } else { + throw new Error('No videos generated'); + } + } catch (e: any) { + console.error('[Video Gen]', e); + const errorMessage = e.message || 'Video generation failed'; + + if (errorMessage.includes('401') || errorMessage.includes('cookies')) { + setShowCookieExpired(true); + } + + setErrorNotification({ + message: `🎬 Video Error: ${errorMessage}`, + type: 'error' + }); + setTimeout(() => setErrorNotification(null), 8000); + } finally { + setIsGeneratingVideo(false); + setIsGenerating(false); + } + }; + const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) { e.preventDefault(); @@ -488,18 +564,26 @@ export function PromptHero() {
{/* Left Controls: References */} - {/* For Meta AI: References are disabled (generate images first, video from gallery) */} -
+ {/* For Meta AI: Only Subject is enabled (for video generation), Scene/Style disabled */} +
{(['subject', 'scene', 'style'] as ReferenceCategory[]).map((cat) => { const refs = references[cat] || []; const hasRefs = refs.length > 0; const isUploading = uploadingRefs[cat]; + // For Meta AI: only Subject is enabled (for image-to-video), Scene/Style disabled + const isDisabledForMeta = settings.provider === 'meta' && cat !== 'subject'; return ( -
+
+ + {/* Generate Video Button - Only for Meta AI when Subject is uploaded */} + {settings.provider === 'meta' && (references.subject?.length ?? 0) > 0 && ( + + )}