1→import dynamic from 'next/dynamic';
2→
3→const HomeClient = dynamic(() => import('./home-client'), { ssr: false });
4→
5→export default function Home() {
6→ return ;
7→}
8→
9→const STYLES = [
10→ { id: 'cinematic', label: 'سينمائي', icon: '🎬', desc: 'جودة سينمائية عالية' },
11→ { id: 'anime', label: 'أنمي', icon: '🎨', desc: 'رسوم أنمي يابانية' },
12→ { id: 'realistic', label: 'واقعي', icon: '📷', desc: 'تصوير واقعي' },
13→ { id: 'fantasy', label: 'خيالي', icon: '🧙', desc: 'عوالم خيالية' },
14→ { id: 'documentary', label: 'وثائقي', icon: '📹', desc: 'أسلوب وثائقي' },
15→ { id: 'watercolor', label: 'مائي', icon: '🖌️', desc: 'رسم مائي فني' },
16→];
17→
18→const EXAMPLE_PROMPTS = [
19→ 'رحلة عبر الغابات المطيرة الاستوائية مع أصوات الطيور والشلالات',
20→ 'مدينة مستقبلية في عام 2150 مع السيارات الطائرة والأبنية الشفافة',
21→ 'قصة فتاة صغيرة تكتشف عالماً سحرياً خلف خزانة قديمة',
22→ 'غروب الشمس على شاطئ هادئ مع الأمواج والنورس الأبيض',
23→];
24→
25→export default function Home() {
26→ return (
27→
34→ );
35→}
36→
37→/* ───────────────────── Header ───────────────────── */
38→function Header() {
39→ const { activeView, setActiveView, projects } = useVideoStore();
40→
41→ return (
42→
43→
44→
56→
57→
82→
83→
84→ );
85→}
86→
87→/* ───────────────────── App Content Router ───────────────────── */
88→function AppContent() {
89→ const { activeView, isGenerating } = useVideoStore();
90→
91→ return (
92→
93→ {isGenerating ? (
94→
101→
102→
103→ ) : activeView === 'player' ? (
104→
111→
112→
113→ ) : activeView === 'gallery' ? (
114→
121→
122→
123→ ) : (
124→
130→
131→
132→ )}
133→
134→ );
135→}
136→
137→/* ───────────────────── Create Section ───────────────────── */
138→function CreateSection() {
139→ const { prompt, setPrompt, selectedStyle, setSelectedStyle, sceneCount, setSceneCount, isGenerating, setIsGenerating, setGenerationProgress, setActiveView, setCurrentProject, addProject, projects, setProjects } = useVideoStore();
140→
141→ const handleGenerate = useCallback(async () => {
142→ if (!prompt.trim()) {
143→ toast.error('يرجى كتابة وصف للفيديو');
144→ return;
145→ }
146→
147→ setIsGenerating(true);
148→ setGenerationProgress({ step: 'جاري تحليل الوصف وإنشاء المشاهد...', progress: 10 });
149→
150→ try {
151→ const res = await fetch('/api/generate-video', {
152→ method: 'POST',
153→ headers: { 'Content-Type': 'application/json' },
154→ body: JSON.stringify({ prompt: prompt.trim(), style: selectedStyle, sceneCount }),
155→ });
156→
157→ if (!res.ok) {
158→ const err = await res.json();
159→ throw new Error(err.error || 'حدث خطأ أثناء التوليد');
160→ }
161→
162→ const project = await res.json();
163→ addProject(project);
164→ setCurrentProject(project);
165→ setActiveView('player');
166→ toast.success('تم إنشاء الفيديو بنجاح!');
167→ } catch (err: any) {
168→ toast.error(err.message || 'حدث خطأ غير متوقع');
169→ // Fetch updated project list
170→ try {
171→ const listRes = await fetch('/api/videos');
172→ if (listRes.ok) {
173→ const data = await listRes.json();
174→ setProjects(data.projects || []);
175→ }
176→ } catch { /* ignore */ }
177→ } finally {
178→ setIsGenerating(false);
179→ }
180→ }, [prompt, selectedStyle, sceneCount]);
181→
182→ const handleExampleClick = (example: string) => {
183→ setPrompt(example);
184→ };
185→
186→ return (
187→
188→ {/* Hero background */}
189→
190→

191→
192→
193→
194→
195→ {/* Hero Text */}
196→
202→
208→
209→ مدعوم بالذكاء الاصطناعي المتقدم
210→
211→
212→ حوّل أفكارك إلى{' '}
213→ فيديوهات مذهلة
214→
215→
216→ اكتب وصفاً لما تريده وسيقوم الذكاء الاصطناعي بإنشاء مشاهد وصور وسرد صوتي تلقائياً
217→
218→
219→
220→ {/* Main Creation Card */}
221→
226→
227→
228→ {/* Prompt Input */}
229→
230→
234→
247→
248→ {/* Example Prompts */}
249→
250→
أفكار سريعة:
251→
252→ {EXAMPLE_PROMPTS.map((ex, i) => (
253→
260→ ))}
261→
262→
263→
264→ {/* Style Selection */}
265→
266→
270→
271→ {STYLES.map((style) => (
272→
290→ ))}
291→
292→
293→
294→ {/* Scene Count & Generate Button */}
295→
296→
297→
301→
setSceneCount(v)}
304→ min={2}
305→ max={6}
306→ step={1}
307→ className="py-2"
308→ />
309→
310→ 2 مشاهد
311→ 6 مشاهد
312→
313→
314→
315→
327→
328→
329→
330→
331→
332→ {/* Recent projects preview */}
333→ {projects.length > 0 && (
334→
340→
341→
342→
343→ آخر الإبداعات
344→
345→
348→
349→
350→ {projects.slice(0, 4).map((project) => (
351→
352→ ))}
353→
354→
355→ )}
356→
357→
358→ );
359→}
360→
361→/* ───────────────────── Generation Progress ───────────────────── */
362→function GenerationProgress() {
363→ const { generationProgress, prompt, selectedStyle, sceneCount } = useVideoStore();
364→
365→ const steps = [
366→ { label: 'تحليل الوصف', icon: '🧠', threshold: 10 },
367→ { label: 'كتابة السيناريو', icon: '📝', threshold: 25 },
368→ { label: 'إنشاء المشاهد', icon: '🎨', threshold: 40 },
369→ { label: 'توليد الصور', icon: '🖼️', threshold: 70 },
370→ { label: 'تسجيل الصوت', icon: '🎙️', threshold: 90 },
371→ { label: 'المعالجة النهائية', icon: '✨', threshold: 100 },
372→ ];
373→
374→ return (
375→
376→ {/* Animated icon */}
377→
382→
383→
384→
385→
386→
387→
388→
جاري إنشاء الفيديو...
389→
{generationProgress.step}
390→
391→
392→ {/* Progress bar */}
393→
394→
395→
{generationProgress.progress}%
396→
397→
398→ {/* Steps */}
399→
400→ {steps.map((step, i) => {
401→ const isActive = generationProgress.progress >= step.threshold - 5;
402→ const isDone = generationProgress.progress >= step.threshold + 5;
403→ return (
404→
417→ {step.icon}
418→ {step.label}
419→ {isDone && (
420→ تم ✓
421→ )}
422→
423→ );
424→ })}
425→
426→
427→ {/* Summary */}
428→
429→ {STYLES.find(s => s.id === selectedStyle)?.label}
430→ {sceneCount} مشاهد
431→ سرد صوتي
432→
433→
434→ );
435→}
436→
437→/* ───────────────────── Video Player ───────────────────── */
438→function VideoPlayer() {
439→ const { currentProject, currentSceneIndex, setCurrentSceneIndex, isPlaying, setIsPlaying, setActiveView } = useVideoStore();
440→ const audioRef = useRef(null);
441→ const timerRef = useRef(null);
442→
443→ const scenes = currentProject?.scenes || [];
444→ const currentScene = scenes[currentSceneIndex];
445→ const totalDuration = scenes.reduce((sum, s) => sum + (s.duration || 5), 0);
446→
447→ // Auto-advance scenes
448→ useEffect(() => {
449→ if (!isPlaying || !currentScene) return;
450→
451→ // Play audio if available
452→ if (audioRef.current && currentScene.audioPath) {
453→ audioRef.current.src = currentScene.audioPath;
454→ audioRef.current.play().catch(() => {});
455→ }
456→
457→ timerRef.current = setTimeout(() => {
458→ if (currentSceneIndex < scenes.length - 1) {
459→ setCurrentSceneIndex(currentSceneIndex + 1);
460→ } else {
461→ setIsPlaying(false);
462→ setCurrentSceneIndex(0);
463→ }
464→ }, (currentScene.duration || 5) * 1000);
465→
466→ return () => {
467→ if (timerRef.current) clearTimeout(timerRef.current);
468→ };
469→ }, [currentSceneIndex, isPlaying, scenes.length]);
470→
471→ const togglePlay = () => {
472→ if (isPlaying) {
473→ setIsPlaying(false);
474→ if (timerRef.current) clearTimeout(timerRef.current);
475→ if (audioRef.current) audioRef.current.pause();
476→ } else {
477→ setIsPlaying(true);
478→ }
479→ };
480→
481→ const goToScene = (index: number) => {
482→ if (timerRef.current) clearTimeout(timerRef.current);
483→ if (audioRef.current) audioRef.current.pause();
484→ setCurrentSceneIndex(index);
485→ setIsPlaying(false);
486→ };
487→
488→ const prevScene = () => {
489→ if (currentSceneIndex > 0) goToScene(currentSceneIndex - 1);
490→ };
491→
492→ const nextScene = () => {
493→ if (currentSceneIndex < scenes.length - 1) goToScene(currentSceneIndex + 1);
494→ };
495→
496→ if (!currentProject || scenes.length === 0) {
497→ return (
498→
499→
لم يتم العثور على الفيديو
500→
503→
504→ );
505→ }
506→
507→ return (
508→
509→ {/* Back button & Title */}
510→
511→
514→
515→
{currentProject.title}
516→
{currentProject.prompt}
517→
518→
519→
520→ {/* Video Player */}
521→
522→
523→ {currentScene?.imagePath ? (
524→

530→ ) : (
531→
532→ )}
533→
534→ {/* Scene info overlay */}
535→
536→
537→ {currentScene?.narrationText || currentScene?.description}
538→
539→
540→
541→ مشهد {currentSceneIndex + 1} من {scenes.length}
542→
543→ {currentScene?.duration && (
544→
545→
546→ {currentScene.duration} ثانية
547→
548→ )}
549→
550→
551→
552→ {/* Play/Pause overlay button */}
553→
563→
564→
565→ {/* Controls */}
566→
567→ {/* Progress bar */}
568→
569→
570→ {currentSceneIndex + 1}/{scenes.length}
571→
572→
573→
578→
579→
580→ {totalDuration} ثانية
581→
582→
583→
584→ {/* Buttons */}
585→
586→
589→
592→
595→
598→
599→
600→
601→
602→ {/* Scene Thumbnails */}
603→
604→
المشاهد
605→
606→ {scenes.map((scene, i) => (
607→
627→ ))}
628→
629→
630→
631→ {/* Audio element */}
632→
633→
634→ {/* Project Info */}
635→
636→
637→
638→
639→
النمط
640→
{STYLES.find(s => s.id === currentProject.style)?.label || currentProject.style}
641→
642→
643→
المشاهد
644→
{scenes.length}
645→
646→
647→
المدة
648→
{totalDuration} ثانية
649→
650→
651→
تاريخ الإنشاء
652→
{new Date(currentProject.createdAt).toLocaleDateString('ar-EG')}
653→
654→
655→
656→
657→
658→ );
659→}
660→
661→/* ───────────────────── Video Gallery ───────────────────── */
662→function VideoGallery() {
663→ const { projects, setProjects, removeProject, setActiveView, setCurrentProject } = useVideoStore();
664→
665→ const fetchProjects = async () => {
666→ try {
667→ const res = await fetch('/api/videos');
668→ if (res.ok) {
669→ const data = await res.json();
670→ setProjects(data.projects || []);
671→ }
672→ } catch {
673→ // silently fail
674→ }
675→ };
676→
677→ useEffect(() => {
678→ fetchProjects();
679→ }, []);
680→
681→ const handleDelete = async (id: string) => {
682→ try {
683→ await fetch(`/api/videos/${id}`, { method: 'DELETE' });
684→ removeProject(id);
685→ toast.success('تم حذف الفيديو');
686→ } catch {
687→ toast.error('فشل في حذف الفيديو');
688→ }
689→ };
690→
691→ const handlePlay = async (project: VideoProject) => {
692→ try {
693→ const res = await fetch(`/api/videos/${project.id}`);
694→ if (res.ok) {
695→ const data = await res.json();
696→ setCurrentProject(data);
697→ setActiveView('player');
698→ }
699→ } catch {
700→ toast.error('فشل في تحميل الفيديو');
701→ }
702→ };
703→
704→ if (projects.length === 0) {
705→ return (
706→
707→
708→
709→
710→
لا توجد فيديوهات بعد
711→
ابدأ بإنشاء أول فيديو لك بالذكاء الاصطناعي
712→
716→
717→ );
718→ }
719→
720→ return (
721→
722→
723→
مكتبة الفيديوهات
724→
728→
729→
730→
731→ {projects.map((project) => (
732→
handlePlay(project)}
736→ onDelete={() => handleDelete(project.id)}
737→ />
738→ ))}
739→
740→
741→ );
742→}
743→
744→/* ───────────────────── Project Card ───────────────────── */
745→function ProjectCard({
746→ project,
747→ compact = false,
748→ onPlay,
749→ onDelete,
750→}: {
751→ project: VideoProject;
752→ compact?: boolean;
753→ onPlay?: () => void;
754→ onDelete?: () => void;
755→}) {
756→ const { setActiveView, setCurrentProject } = useVideoStore();
757→
758→ const handleClick = async () => {
759→ if (onPlay) {
760→ onPlay();
761→ } else {
762→ try {
763→ const res = await fetch(`/api/videos/${project.id}`);
764→ if (res.ok) {
765→ const data = await res.json();
766→ setCurrentProject(data);
767→ setActiveView('player');
768→ }
769→ } catch {
770→ toast.error('فشل في تحميل الفيديو');
771→ }
772→ }
773→ };
774→
775→ const handleDeleteClick = (e: React.MouseEvent) => {
776→ e.stopPropagation();
777→ if (onDelete) onDelete();
778→ };
779→
780→ const statusLabel = project.status === 'completed' ? 'مكتمل' : project.status === 'generating' ? 'قيد الإنشاء' : 'فشل';
781→ 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';
782→
783→ return (
784→
788→
792→ {/* Thumbnail */}
793→
794→ {project.thumbnail ? (
795→

796→ ) : (
797→
798→ )}
799→ {/* Overlay */}
800→
805→ {/* Status badge */}
806→
807→
808→ {statusLabel}
809→
810→
811→ {/* Scene count */}
812→
813→
814→ {project.sceneCount} مشاهد
815→
816→
817→
818→
819→ {/* Info */}
820→
821→ {project.title}
822→ {!compact && (
823→ {project.prompt}
824→ )}
825→
826→
827→ {new Date(project.createdAt).toLocaleDateString('ar-EG')}
828→
829→
830→
831→ {STYLES.find(s => s.id === project.style)?.label || project.style}
832→
833→ {onDelete && (
834→
842→ )}
843→
844→
845→
846→
847→
848→ );
849→}
850→
851→/* ───────────────────── Footer ───────────────────── */
852→function Footer() {
853→ return (
854→
867→ );
868→}