"use client"; import { useState, useEffect, useRef } from 'react'; import { Card } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { ArrowLeft, ChevronDown, ChevronUp, Video, ListOrdered, Play, Loader2, Pause, MoreHorizontal, Edit2, Check, X, RefreshCw, Lightbulb, Package, Crown, ArrowUp } from 'lucide-react'; import { useRouter, useSearchParams } from 'next/navigation'; import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger, SheetClose } from "@/components/ui/sheet"; import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; import './style/create-to-video2.css'; import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from "@/components/ui/resizable"; import { Skeleton } from "@/components/ui/skeleton"; import LiquidGlass from '@/plugins/liquid-glass/index' import { Dropdown } from 'antd'; import type { MenuProps } from 'antd'; import Image from 'next/image'; import dynamic from 'next/dynamic'; import { ProjectTypeEnum, ModeEnum, ResolutionEnum } from "@/api/enums"; import { createScriptEpisode, CreateScriptEpisodeRequest, updateScriptEpisode, UpdateScriptEpisodeRequest } from "@/api/script_episode"; import { getUploadToken, uploadToQiniu } from "@/api/common"; import { convertScriptToScene, convertVideoToScene } from "@/api/video_flow"; import { EmptyStateAnimation } from '@/components/common/EmptyStateAnimation'; const JoyrideNoSSR = dynamic(() => import('react-joyride'), { ssr: false, }); // 导入Step类型 import type { Step } from 'react-joyride'; // interface Step { // target: string; // content: string; // placement?: 'top' | 'bottom' | 'left' | 'right'; // } // 添加自定义滚动条样式 const scrollbarStyles = ` .custom-scrollbar::-webkit-scrollbar { width: 4px; } .custom-scrollbar::-webkit-scrollbar-track { background: rgba(255, 255, 255, 0.05); border-radius: 2px; } .custom-scrollbar::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.1); border-radius: 2px; } .custom-scrollbar::-webkit-scrollbar-thumb:hover { background: rgba(255, 255, 255, 0.2); } `; interface SceneVideo { id: number; video_url: string; script: any; } const ideaText = 'a cute capybara with an orange on its head, staring into the distance and walking forward'; export function CreateToVideo2() { const router = useRouter(); const searchParams = useSearchParams(); const projectId = searchParams.get('projectId') ? parseInt(searchParams.get('projectId')!) : 0; const [isClient, setIsClient] = useState(false); const [isExpanded, setIsExpanded] = useState(false); const [videoUrl, setVideoUrl] = useState(''); const [isUploading, setIsUploading] = useState(false); const containerRef = useRef(null); const [activeTab, setActiveTab] = useState('script'); const [isFocus, setIsFocus] = useState(false); const [selectedMode, setSelectedMode] = useState(ModeEnum.AUTOMATIC); const [selectedResolution, setSelectedResolution] = useState(ResolutionEnum.HD_720P); const [script, setInputText] = useState(''); const editorRef = useRef(null); const [runTour, setRunTour] = useState(true); const [episodeId, setEpisodeId] = useState(0); const [isCreating, setIsCreating] = useState(false); const [generatedVideoList, setGeneratedVideoList] = useState([]); const [projectName, setProjectName] = useState('默认名称'); // 在客户端挂载后读取localStorage useEffect(() => { if (typeof window !== 'undefined') { const savedProjectName = localStorage.getItem('projectName'); if (savedProjectName) { setProjectName(savedProjectName); } } }, []); const handleUploadVideo = async () => { console.log('upload video'); // 打开文件选择器 const input = document.createElement('input'); input.type = 'file'; input.accept = 'video/*'; input.onchange = async (e) => { const file = (e.target as HTMLInputElement).files?.[0]; if (file) { try { setIsUploading(true); // 获取上传token const { token } = await getUploadToken(); // 上传到七牛云 const videoUrl = await uploadToQiniu(file, token); // 上传成功,设置视频URL setVideoUrl(videoUrl); console.log('视频上传成功:', videoUrl); } catch (error) { console.error('上传错误:', error); alert('上传失败,请稍后重试'); } finally { setIsUploading(false); } } } input.click(); } const handleCreateVideo = async () => { // 创建剧集数据 const episodeData: CreateScriptEpisodeRequest = { title: "episode default", script_id: projectId, status: 1, summary: script }; // 调用创建剧集API const episodeResponse = await createScriptEpisode(episodeData); if (episodeResponse.code !== 0) { console.error(`创建剧集失败: ${episodeResponse.message}`); alert(`创建剧集失败: ${episodeResponse.message}`); return; } let episodeId = episodeResponse.data.id; if (videoUrl || script) { try { setIsCreating(true); let convertResponse; // 根据选中的选项卡调用相应的API if (activeTab === 'script') { // 剧本模式:调用convertScriptToScene (第43-56行) if (!script.trim()) { alert('请输入剧本内容'); return; } convertResponse = await convertScriptToScene(script, episodeId, projectId); } else { // 视频模式:调用convertVideoToScene (第56-69行) if (!videoUrl) { alert('请先上传视频'); return; } if (!episodeId) { alert('Episode ID not available'); return; } convertResponse = await convertVideoToScene(videoUrl, episodeId, projectId); } // 更新剧集 const updateEpisodeData: UpdateScriptEpisodeRequest = { id: episodeId, atmosphere: convertResponse.data.atmosphere, summary: convertResponse.data.summary, scene: convertResponse.data.scene, characters: convertResponse.data.characters, }; const updateEpisodeResponse = await updateScriptEpisode(updateEpisodeData); // 检查转换结果 if (convertResponse.code === 0) { // 成功创建后跳转到work-flow页面, 并设置episodeId 和 projectType router.push(`/create/work-flow?episodeId=${episodeResponse.data.id}`); } else { alert(`转换失败: ${convertResponse.message}`); } } catch (error) { console.error('创建过程出错:', error); alert("创建项目时发生错误,请稍后重试"); } finally { setIsCreating(false); } } } // 下拉菜单项配置 const modeItems: MenuProps['items'] = [ { type: 'group', label: (
Mode
), children: [ { key: ModeEnum.AUTOMATIC, label: (
Auto
Automatically selects the best model for optimal efficiency
), }, { key: ModeEnum.MANUAL, label: (
Manual
Offers reliable, consistent performance every time
), }, ], }, ]; // 分辨率选项配置 const resolutionItems: MenuProps['items'] = [ { type: 'group', label: (
Resolution
), children: [ { key: ResolutionEnum.HD_720P, label: (
720P
), }, { key: ResolutionEnum.FULL_HD_1080P, label: (
1080P
), }, { key: ResolutionEnum.UHD_2K, label: (
2K
), }, { key: ResolutionEnum.UHD_4K, label: (
4K
), }, ], }, ]; // 处理模式选择 const handleModeSelect: MenuProps['onClick'] = ({ key }) => { setSelectedMode(Number(key) as ModeEnum); }; // 处理分辨率选择 const handleResolutionSelect: MenuProps['onClick'] = ({ key }) => { setSelectedResolution(Number(key) as ResolutionEnum); }; const handleStartCreating = () => { setActiveTab('script'); setInputText(ideaText); } // 处理编辑器聚焦 const handleEditorFocus = () => { setIsFocus(true); if (editorRef.current && script) { // 创建范围对象 const range = document.createRange(); const selection = window.getSelection(); // 获取编辑器内的文本节点 const textNode = Array.from(editorRef.current.childNodes).find( node => node.nodeType === Node.TEXT_NODE ) || editorRef.current.appendChild(document.createTextNode(script)); // 设置范围到文本末尾 range.setStart(textNode, script.length); range.setEnd(textNode, script.length); // 应用选择 selection?.removeAllRanges(); selection?.addRange(range); } }; // 处理编辑器内容变化 const handleEditorChange = (e: React.FormEvent) => { const script = e.currentTarget.textContent || ''; setInputText(script); }; // 引导步骤 const steps: Step[] = [ { target: '.video-storyboard-tools', content: 'Welcome to AI Video Creation Tool! This is the main creation area.', placement: 'top', }, { target: '.storyboard-tools-tab', content: 'Choose between Script mode to create videos from text, or Clone mode to recreate existing videos.', placement: 'bottom', }, { target: '.video-prompt-editor', content: 'Describe your video content here. Our AI will generate videos based on your description.', placement: 'top', }, { target: '.tool-operation-button', content: 'Select different creation modes and video resolutions to customize your output.', placement: 'top', }, { target: '.tool-submit-button', content: 'Click here to start creating your video once you are ready!', placement: 'left', }, ]; // 处理引导结束 const handleJoyrideCallback = (data: any) => { const { status } = data; if (status === 'finished' || status === 'skipped') { setRunTour(false); // 可以在这里存储用户已完成引导的状态 if (typeof window !== 'undefined') { localStorage.setItem('hasCompletedTour', 'true'); } } }; // 检查是否需要显示引导 useEffect(() => { if (typeof window !== 'undefined') { const hasCompletedTour = localStorage.getItem('hasCompletedTour'); if (hasCompletedTour) { setRunTour(false); } } }, []); useEffect(() => { setIsClient(true); }, []); return (
{isClient && ( )}
{/* 空状态 */} {/* 工具栏 */}
{isExpanded ? (
setIsExpanded(false)}> {/* 图标 展开按钮 */} Click to create
) : (
setIsExpanded(true)}> {/* 图标 折叠按钮 */}
)}
setActiveTab('script')}> script
setActiveTab('clone')}> clone
{activeTab === 'clone' && (
{/* 图标 添加视频 */}
Add Video
{videoUrl && (
)}
)} {activeTab === 'script' && (
setIsFocus(false)} onInput={handleEditorChange} suppressContentEditableWarning > {script}
Describe the content you want to create. Get an setInputText(ideaText)} > idea
)}
{selectedMode === ModeEnum.AUTOMATIC ? 'Auto' : 'Manual'}
{isCreating ? ( <> Creating... ) : ( <> Create )}
); }