From 3f61f0ef7eff2f4f64d2d38ab0874da2e04cf60e Mon Sep 17 00:00:00 2001 From: Xin Wang Date: Wed, 2 Jul 2025 21:30:25 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A4=84=E7=90=86=E7=BC=96=E8=AF=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/layout.tsx | 5 +- components/pages/history-page.tsx | 224 +++++++++++++++++++++++++ components/pages/script-to-video.tsx | 168 +++++++++++++++++++ components/pages/video-to-video.tsx | 233 +++++++++++++++++++++++++++ components/ui/audio-visualizer.tsx | 32 +--- 5 files changed, 630 insertions(+), 32 deletions(-) create mode 100644 components/pages/history-page.tsx create mode 100644 components/pages/script-to-video.tsx create mode 100644 components/pages/video-to-video.tsx diff --git a/app/layout.tsx b/app/layout.tsx index 83cfb3c..6566ec0 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,12 +1,9 @@ import './globals.css'; import type { Metadata } from 'next'; -import { Inter } from 'next/font/google'; import { ThemeProvider } from '@/components/theme-provider'; import { Toaster } from '@/components/ui/sonner'; import dynamic from 'next/dynamic'; -const inter = Inter({ subsets: ['latin'] }); - // Import the OAuthCallbackHandler dynamically to ensure it only runs on the client const OAuthCallbackHandler = dynamic( () => import('@/components/ui/oauth-callback-handler'), @@ -25,7 +22,7 @@ export default function RootLayout({ }) { return ( - + ([]); + const [filteredItems, setFilteredItems] = useState([]); + + // Mock data - replace with actual API call + useEffect(() => { + const mockData: HistoryItem[] = [ + { + id: 1, + title: "Sample Script Video", + type: "script-to-video", + status: "completed", + createdAt: "2024-01-15", + duration: "2:30", + thumbnail: "/assets/empty_video.png" + }, + { + id: 2, + title: "Video Enhancement Project", + type: "video-to-video", + status: "processing", + createdAt: "2024-01-14", + duration: "1:45" + }, + { + id: 3, + title: "Marketing Video", + type: "script-to-video", + status: "completed", + createdAt: "2024-01-13", + duration: "3:15", + thumbnail: "/assets/empty_video.png" + } + ]; + setHistoryItems(mockData); + setFilteredItems(mockData); + }, []); + + // Filter items based on search term + useEffect(() => { + const filtered = historyItems.filter(item => + item.title.toLowerCase().includes(searchTerm.toLowerCase()) + ); + setFilteredItems(filtered); + }, [searchTerm, historyItems]); + + const getStatusBadgeVariant = (status: string) => { + switch (status) { + case 'completed': + return 'default'; + case 'processing': + return 'secondary'; + case 'failed': + return 'destructive'; + default: + return 'default'; + } + }; + + const getTypeIcon = (type: string) => { + return type === 'script-to-video' ? '📝' : '🎥'; + }; + + return ( +
+ {/* Header */} +
+
+

Project History

+

View and manage your video projects

+
+ + {/* Search and Filter */} +
+
+ + setSearchTerm(e.target.value)} + className="pl-10 bg-white/5 border-white/20 text-white placeholder:text-white/40" + /> +
+ +
+
+ + {/* Project Grid */} + {filteredItems.length === 0 ? ( + + + ) : ( +
+ {filteredItems.map((item) => ( + + {/* Thumbnail */} +
+ {item.thumbnail ? ( + {item.title} + ) : ( +
+
+ )} + + {/* Play overlay */} +
+ +
+ + {/* Type indicator */} +
+ {getTypeIcon(item.type)} +
+ + {/* Duration */} + {item.duration && ( +
+ {item.duration} +
+ )} +
+ + {/* Content */} +
+
+

+ {item.title} +

+ + + + + + + + View + + + + Edit + + + + Delete + + + +
+ +
+
+ + {item.createdAt} +
+ + {item.status} + +
+
+
+ ))} +
+ )} +
+ ); +} \ No newline at end of file diff --git a/components/pages/script-to-video.tsx b/components/pages/script-to-video.tsx new file mode 100644 index 0000000..6d8c485 --- /dev/null +++ b/components/pages/script-to-video.tsx @@ -0,0 +1,168 @@ +"use client"; + +import { useState } from 'react'; +import { Button } from '@/components/ui/button'; +import { Card } from '@/components/ui/card'; +import { Textarea } from '@/components/ui/textarea'; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; +import { ArrowLeft, Loader2 } from 'lucide-react'; +import { useRouter } from 'next/navigation'; +import { ModeEnum, ResolutionEnum } from "@/api/enums"; +import { createScriptEpisode, CreateScriptEpisodeRequest, updateScriptEpisode, UpdateScriptEpisodeRequest } from "@/api/script_episode"; +import { convertScriptToScene } from "@/api/video_flow"; + +export function ScriptToVideo() { + const router = useRouter(); + const [script, setScript] = useState(''); + const [selectedMode, setSelectedMode] = useState(ModeEnum.AUTOMATIC); + const [selectedResolution, setSelectedResolution] = useState(ResolutionEnum.HD_720P); + const [isCreating, setIsCreating] = useState(false); + + const handleBack = () => { + router.push('/create'); + }; + + const handleCreate = async () => { + if (!script.trim()) { + alert('请输入剧本内容'); + return; + } + + try { + setIsCreating(true); + + // Create episode + const episodeData: CreateScriptEpisodeRequest = { + title: "Script Episode", + script_id: 0, // This should come from a project + status: 1, + summary: script + }; + + const episodeResponse = await createScriptEpisode(episodeData); + if (episodeResponse.code !== 0) { + alert(`创建剧集失败: ${episodeResponse.message}`); + return; + } + + const episodeId = episodeResponse.data.id; + + // Convert script to scenes + const convertResponse = await convertScriptToScene(script, episodeId, 0); + + if (convertResponse.code === 0) { + // Update episode with generated data + const updateEpisodeData: UpdateScriptEpisodeRequest = { + id: episodeId, + atmosphere: convertResponse.data.atmosphere, + summary: convertResponse.data.summary, + scene: convertResponse.data.scene, + characters: convertResponse.data.characters, + }; + + await updateScriptEpisode(updateEpisodeData); + + // Navigate to workflow + router.push(`/create/work-flow?episodeId=${episodeId}`); + } else { + alert(`转换失败: ${convertResponse.message}`); + } + } catch (error) { + console.error('创建过程出错:', error); + alert("创建项目时发生错误,请稍后重试"); + } finally { + setIsCreating(false); + } + }; + + return ( +
+
+ {/* Header */} +
+ +

Script to Video

+
+ + {/* Main Content */} + +
+ {/* Script Input */} +
+ +