1→'use client';
2→
3→import { useEffect, useCallback, useRef } from 'react';
4→import { motion, AnimatePresence } from 'framer-motion';
5→import {
6→ Sparkles, Play, Square, Trash2, ArrowLeft, Film,
7→ Image, Mic, Loader2, Wand2, Clapperboard, Clock,
8→ ChevronLeft, ChevronRight, Pause, Volume2, VolumeX,
9→ RotateCcw, LayoutGrid, MonitorPlay
10→} from 'lucide-react';
11→import { Button } from '@/components/ui/button';
12→import { Textarea } from '@/components/ui/textarea';
13→import { Slider } from '@/components/ui/slider';
14→import { Card, CardContent } from '@/components/ui/card';
15→import { Badge } from '@/components/ui/badge';
16→import { Progress } from '@/components/ui/progress';
17→import { Skeleton } from '@/components/ui/skeleton';
18→import { useVideoStore, type VideoProject } from '@/store/video-store';
19→import { toast } from 'sonner';
20→
21→const STYLES = [
22→ { id: 'cinematic', label: 'سينمائي', icon: '🎬', desc: 'جودة سينمائية عالية' },
23→ { id: 'anime', label: 'أنمي', icon: '🎨', desc: 'رسوم أنمي يابانية' },
24→ { id: 'realistic', label: 'واقعي', icon: '📷', desc: 'تصوير واقعي' },
25→ { id: 'fantasy', label: 'خيالي', icon: '🧙', desc: 'عوالم خيالية' },
26→ { id: 'documentary', label: 'وثائقي', icon: '📹', desc: 'أسلوب وثائقي' },
27→ { id: 'watercolor', label: 'مائي', icon: '🖌️', desc: 'رسم مائي فني' },
28→];
29→
30→const EXAMPLE_PROMPTS = [
31→ 'رحلة عبر الغابات المطيرة الاستوائية مع أصوات الطيور والشلالات',
32→ 'مدينة مستقبلية في عام 2150 مع السيارات الطائرة والأبنية الشفافة',
33→ 'قصة فتاة صغيرة تكتشف عالماً سحرياً خلف خزانة قديمة',
34→ 'غروب الشمس على شاطئ هادئ مع الأمواج والنورس الأبيض',
35→];
36→
37→export default function Home() {
38→ return (
39→
46→ );
47→}
48→
49→/* ───────────────────── Header ───────────────────── */
50→function Header() {
51→ const { activeView, setActiveView, projects } = useVideoStore();
52→
53→ return (
54→
55→
56→
68→
69→
94→
95→
96→ );
97→}
98→
99→/* ───────────────────── App Content Router ───────────────────── */
100→function AppContent() {
101→ const { activeView, isGenerating } = useVideoStore();
102→
103→ return (
104→
105→ {isGenerating ? (
106→
113→
114→
115→ ) : activeView === 'player' ? (
116→
123→
124→
125→ ) : activeView === 'gallery' ? (
126→
133→
134→
135→ ) : (
136→
142→
143→
144→ )}
145→
146→ );
147→}
148→
149→/* ───────────────────── Create Section ───────────────────── */
150→function CreateSection() {
151→ const { prompt, setPrompt, selectedStyle, setSelectedStyle, sceneCount, setSceneCount, isGenerating, setIsGenerating, setGenerationProgress, setActiveView, setCurrentProject, addProject, projects, setProjects } = useVideoStore();
152→
153→ const handleGenerate = useCallback(async () => {
154→ if (!prompt.trim()) {
155→ toast.error('يرجى كتابة وصف للفيديو');
156→ return;
157→ }
158→
159→ setIsGenerating(true);
160→ setGenerationProgress({ step: 'جاري تحليل الوصف وإنشاء المشاهد...', progress: 10 });
161→
162→ try {
163→ const res = await fetch('/api/generate-video', {
164→ method: 'POST',
165→ headers: { 'Content-Type': 'application/json' },
166→ body: JSON.stringify({ prompt: prompt.trim(), style: selectedStyle, sceneCount }),
167→ });
168→
169→ if (!res.ok) {
170→ const err = await res.json();
171→ throw new Error(err.error || 'حدث خطأ أثناء التوليد');
172→ }
173→
174→ const project = await res.json();
175→ addProject(project);
176→ setCurrentProject(project);
177→ setActiveView('player');
178→ toast.success('تم إنشاء الفيديو بنجاح!');
179→ } catch (err: any) {
180→ toast.error(err.message || 'حدث خطأ غير متوقع');
181→ // Fetch updated project list
182→ try {
183→ const listRes = await fetch('/api/videos');
184→ if (listRes.ok) {
185→ const data = await listRes.json();
186→ setProjects(data.projects || []);
187→ }
188→ } catch { /* ignore */ }
189→ } finally {
190→ setIsGenerating(false);
191→ }
192→ }, [prompt, selectedStyle, sceneCount]);
193→
194→ const handleExampleClick = (example: string) => {
195→ setPrompt(example);
196→ };
197→
198→ return (
199→
200→ {/* Hero background */}
201→
202→

203→
204→
205→
206→
207→ {/* Hero Text */}
208→
214→
220→
221→ مدعوم بالذكاء الاصطناعي المتقدم
222→
223→
224→ حوّل أفكارك إلى{' '}
225→ فيديوهات مذهلة
226→
227→
228→ اكتب وصفاً لما تريده وسيقوم الذكاء الاصطناعي بإنشاء مشاهد وصور وسرد صوتي تلقائياً
229→
230→
231→
232→ {/* Main Creation Card */}
233→
238→
239→
240→ {/* Prompt Input */}
241→
242→
246→
259→
260→ {/* Example Prompts */}
261→
262→
أفكار سريعة:
263→
264→ {EXAMPLE_PROMPTS.map((ex, i) => (
265→
272→ ))}
273→
274→
275→
276→ {/* Style Selection */}
277→
278→
282→
283→ {STYLES.map((style) => (
284→
302→ ))}
303→
304→
305→
306→ {/* Scene Count & Generate Button */}
307→
308→
309→
313→
setSceneCount(v)}
316→ min={2}
317→ max={6}
318→ step={1}
319→ className="py-2"
320→ />
321→
322→ 2 مشاهد
323→ 6 مشاهد
324→
325→
326→
327→
339→
340→
341→
342→
343→
344→ {/* Recent projects preview */}
345→ {projects.length > 0 && (
346→
352→
353→
354→
355→ آخر الإبداعات
356→
357→
360→
361→
362→ {projects.slice(0, 4).map((project) => (
363→
364→ ))}
365→
366→
367→ )}
368→
369→
370→ );
371→}
372→
373→/* ───────────────────── Generation Progress ───────────────────── */
374→function GenerationProgress() {
375→ const { generationProgress, prompt, selectedStyle, sceneCount } = useVideoStore();
376→
377→ const steps = [
378→ { label: 'تحليل الوصف', icon: '🧠', threshold: 10 },
379→ { label: 'كتابة السيناريو', icon: '📝', threshold: 25 },
380→ { label: 'إنشاء المشاهد', icon: '🎨', threshold: 40 },
381→ { label: 'توليد الصور', icon: '🖼️', threshold: 70 },
382→ { label: 'تسجيل الصوت', icon: '🎙️', threshold: 90 },
383→ { label: 'المعالجة النهائية', icon: '✨', threshold: 100 },
384→ ];
385→
386→ return (
387→
388→ {/* Animated icon */}
389→
394→
395→
396→
397→
398→
399→
400→
جاري إنشاء الفيديو...
401→
{generationProgress.step}
402→
403→
404→ {/* Progress bar */}
405→
406→
407→
{generationProgress.progress}%
408→
409→
410→ {/* Steps */}
411→
412→ {steps.map((step, i) => {
413→ const isActive = generationProgress.progress >= step.threshold - 5;
414→ const isDone = generationProgress.progress >= step.threshold + 5;
415→ return (
416→
429→ {step.icon}
430→ {step.label}
431→ {isDone && (
432→ تم ✓
433→ )}
434→
435→ );
436→ })}
437→
438→
439→ {/* Summary */}
440→
441→ {STYLES.find(s => s.id === selectedStyle)?.label}
442→ {sceneCount} مشاهد
443→ سرد صوتي
444→
445→
446→ );
447→}
448→
449→/* ───────────────────── Video Player ───────────────────── */
450→function VideoPlayer() {
451→ const { currentProject, currentSceneIndex, setCurrentSceneIndex, isPlaying, setIsPlaying, setActiveView } = useVideoStore();
452→ const audioRef = useRef(null);
453→ const timerRef = useRef(null);
454→
455→ const scenes = currentProject?.scenes || [];
456→ const currentScene = scenes[currentSceneIndex];
457→ const totalDuration = scenes.reduce((sum, s) => sum + (s.duration || 5), 0);
458→
459→ // Auto-advance scenes
460→ useEffect(() => {
461→ if (!isPlaying || !currentScene) return;
462→
463→ // Play audio if available
464→ if (audioRef.current && currentScene.audioPath) {
465→ audioRef.current.src = currentScene.audioPath;
466→ audioRef.current.play().catch(() => {});
467→ }
468→
469→ timerRef.current = setTimeout(() => {
470→ if (currentSceneIndex < scenes.length - 1) {
471→ setCurrentSceneIndex(currentSceneIndex + 1);
472→ } else {
473→ setIsPlaying(false);
474→ setCurrentSceneIndex(0);
475→ }
476→ }, (currentScene.duration || 5) * 1000);
477→
478→ return () => {
479→ if (timerRef.current) clearTimeout(timerRef.current);
480→ };
481→ }, [currentSceneIndex, isPlaying, scenes.length]);
482→
483→ const togglePlay = () => {
484→ if (isPlaying) {
485→ setIsPlaying(false);
486→ if (timerRef.current) clearTimeout(timerRef.current);
487→ if (audioRef.current) audioRef.current.pause();
488→ } else {
489→ setIsPlaying(true);
490→ }
491→ };
492→
493→ const goToScene = (index: number) => {
494→ if (timerRef.current) clearTimeout(timerRef.current);
495→ if (audioRef.current) audioRef.current.pause();
496→ setCurrentSceneIndex(index);
497→ setIsPlaying(false);
498→ };
499→
500→ const prevScene = () => {
501→ if (currentSceneIndex > 0) goToScene(currentSceneIndex - 1);
502→ };
503→
504→ const nextScene = () => {
505→ if (currentSceneIndex < scenes.length - 1) goToScene(currentSceneIndex + 1);
506→ };
507→
508→ if (!currentProject || scenes.length === 0) {
509→ return (
510→
511→
لم يتم العثور على الفيديو
512→
515→
516→ );
517→ }
518→
519→ return (
520→
521→ {/* Back button & Title */}
522→
523→
526→
527→
{currentProject.title}
528→
{currentProject.prompt}
529→
530→
531→
532→ {/* Video Player */}
533→
534→
535→ {currentScene?.imagePath ? (
536→

542→ ) : (
543→
544→ )}
545→
546→ {/* Scene info overlay */}
547→
548→
549→ {currentScene?.narrationText || currentScene?.description}
550→
551→
552→
553→ مشهد {currentSceneIndex + 1} من {scenes.length}
554→
555→ {currentScene?.duration && (
556→
557→
558→ {currentScene.duration} ثانية
559→
560→ )}
561→
562→
563→
564→ {/* Play/Pause overlay button */}
565→
575→
576→
577→ {/* Controls */}
578→
579→ {/* Progress bar */}
580→
581→
582→ {currentSceneIndex + 1}/{scenes.length}
583→
584→
585→
590→
591→
592→ {totalDuration} ثانية
593→
594→
595→
596→ {/* Buttons */}
597→
598→
601→
604→
607→
610→
611→
612→
613→
614→ {/* Scene Thumbnails */}
615→
616→
المشاهد
617→
618→ {scenes.map((scene, i) => (
619→
639→ ))}
640→
641→
642→
643→ {/* Audio element */}
644→
645→
646→ {/* Project Info */}
647→
648→
649→
650→
651→
النمط
652→
{STYLES.find(s => s.id === currentProject.style)?.label || currentProject.style}
653→
654→
655→
المشاهد
656→
{scenes.length}
657→
658→
659→
المدة
660→
{totalDuration} ثانية
661→
662→
663→
تاريخ الإنشاء
664→
{new Date(currentProject.createdAt).toLocaleDateString('ar-EG')}
665→
666→
667→
668→
669→
670→ );
671→}
672→
673→/* ───────────────────── Video Gallery ───────────────────── */
674→function VideoGallery() {
675→ const { projects, setProjects, removeProject, setActiveView, setCurrentProject } = useVideoStore();
676→
677→ const fetchProjects = async () => {
678→ try {
679→ const res = await fetch('/api/videos');
680→ if (res.ok) {
681→ const data = await res.json();
682→ setProjects(data.projects || []);
683→ }
684→ } catch {
685→ // silently fail
686→ }
687→ };
688→
689→ useEffect(() => {
690→ fetchProjects();
691→ }, []);
692→
693→ const handleDelete = async (id: string) => {
694→ try {
695→ await fetch(`/api/videos/${id}`, { method: 'DELETE' });
696→ removeProject(id);
697→ toast.success('تم حذف الفيديو');
698→ } catch {
699→ toast.error('فشل في حذف الفيديو');
700→ }
701→ };
702→
703→ const handlePlay = async (project: VideoProject) => {
704→ try {
705→ const res = await fetch(`/api/videos/${project.id}`);
706→ if (res.ok) {
707→ const data = await res.json();
708→ setCurrentProject(data);
709→ setActiveView('player');
710→ }
711→ } catch {
712→ toast.error('فشل في تحميل الفيديو');
713→ }
714→ };
715→
716→ if (projects.length === 0) {
717→ return (
718→
719→
720→
721→
722→
لا توجد فيديوهات بعد
723→
ابدأ بإنشاء أول فيديو لك بالذكاء الاصطناعي
724→
728→
729→ );
730→ }
731→
732→ return (
733→
734→
735→
مكتبة الفيديوهات
736→
740→
741→
742→
743→ {projects.map((project) => (
744→
handlePlay(project)}
748→ onDelete={() => handleDelete(project.id)}
749→ />
750→ ))}
751→
752→
753→ );
754→}
755→
756→/* ───────────────────── Project Card ───────────────────── */
757→function ProjectCard({
758→ project,
759→ compact = false,
760→ onPlay,
761→ onDelete,
762→}: {
763→ project: VideoProject;
764→ compact?: boolean;
765→ onPlay?: () => void;
766→ onDelete?: () => void;
767→}) {
768→ const { setActiveView, setCurrentProject } = useVideoStore();
769→
770→ const handleClick = async () => {
771→ if (onPlay) {
772→ onPlay();
773→ } else {
774→ try {
775→ const res = await fetch(`/api/videos/${project.id}`);
776→ if (res.ok) {
777→ const data = await res.json();
778→ setCurrentProject(data);
779→ setActiveView('player');
780→ }
781→ } catch {
782→ toast.error('فشل في تحميل الفيديو');
783→ }
784→ }
785→ };
786→
787→ const handleDeleteClick = (e: React.MouseEvent) => {
788→ e.stopPropagation();
789→ if (onDelete) onDelete();
790→ };
791→
792→ const statusLabel = project.status === 'completed' ? 'مكتمل' : project.status === 'generating' ? 'قيد الإنشاء' : 'فشل';
793→ const statusColor = project.status === 'completed' ? 'bg-emerald-500/20 text-emerald-400' : project.status === 'generating' ? 'bg-amber-500/20 text-amber-400' : 'bg-red-500/20 text-red-400';
794→
795→ return (
796→
800→
804→ {/* Thumbnail */}
805→
806→ {project.thumbnail ? (
807→

808→ ) : (
809→
810→ )}
811→ {/* Overlay */}
812→
817→ {/* Status badge */}
818→
819→
820→ {statusLabel}
821→
822→
823→ {/* Scene count */}
824→
825→
826→ {project.sceneCount} مشاهد
827→
828→
829→
830→
831→ {/* Info */}
832→
833→ {project.title}
834→ {!compact && (
835→ {project.prompt}
836→ )}
837→
838→
839→ {new Date(project.createdAt).toLocaleDateString('ar-EG')}
840→
841→
842→
843→ {STYLES.find(s => s.id === project.style)?.label || project.style}
844→
845→ {onDelete && (
846→
854→ )}
855→
856→
857→
858→
859→
860→ );
861→}
862→
863→/* ───────────────────── Footer ───────────────────── */
864→function Footer() {
865→ return (
866→
879→ );
880→}