diff --git a/components/pages/work-flow.tsx b/components/pages/work-flow.tsx index 10293ba..124691b 100644 --- a/components/pages/work-flow.tsx +++ b/components/pages/work-flow.tsx @@ -81,76 +81,6 @@ export default function WorkFlow() { } }, [taskSketch.length, currentSketchIndex]); - // 第一个分镜视频生成完成时停止循环播放并切换到第一个 - useEffect(() => { - if (taskVideos.length === 1 && isPlaying) { - console.log('第一个分镜视频生成完成,停止循环播放并切换到第一个分镜'); - setIsPlaying(false); // 停止循环播放 - setCurrentSketchIndex(0); // 切换到第一个分镜 - } - }, [taskVideos.length, isPlaying, setIsPlaying, setCurrentSketchIndex]); - - // 分镜草图生成完毕后自动开始播放 - useEffect(() => { - if ( - !isGeneratingSketch && // 分镜草图生成完毕 - taskSketch.length > 0 && // 有分镜草图数据 - sketchCount === totalSketchCount && // 确保所有分镜草图都生成完毕 - (currentStep === '1' || currentStep === '2') && // 允许在步骤1或步骤2初期触发 - !hasAutoStartedRef.current && // 还没有自动开始过播放 - !isPlaying // 当前没有播放 - ) { - console.log('所有分镜草图生成完毕,自动开始播放'); - // 添加小延迟确保状态完全更新 - setTimeout(() => { - hasAutoStartedRef.current = true; - setIsPlaying(true); // 自动开始播放 - }, 500); - } - - // 当切换到步骤3及以后时重置标记 - if (Number(currentStep) >= 3) { - hasAutoStartedRef.current = false; - } - }, [isGeneratingSketch, taskSketch.length, sketchCount, totalSketchCount, currentStep, isPlaying, setIsPlaying]); - - // 处理自动播放的分镜切换逻辑 仅循环一轮 - useEffect(() => { - if (isPlaying && taskSketch.length > 0) { - console.log('开始自动切换分镜,总数:', taskSketch.length); - - // 记录循环开始时的索引 - if (loopStartIndexRef.current === null) { - loopStartIndexRef.current = currentSketchIndex; - console.log('记录循环起始索引:', currentSketchIndex); - } - - const interval = setInterval(() => { - setCurrentSketchIndex((prev: number) => { - const nextIndex = (prev + 1) % taskSketch.length; - - // 检查是否完成了一轮循环(回到起始索引) - if (nextIndex === loopStartIndexRef.current && prev !== loopStartIndexRef.current) { - console.log('完成一轮循环,停止自动播放'); - setTimeout(() => { - setIsPlaying(false); - loopStartIndexRef.current = null; // 重置循环起始索引 - }, 1000); // 延迟1秒停止,让最后一个分镜显示完整 - } - - return nextIndex; - }); - }, 1000); - - return () => { - clearInterval(interval); - }; - } else { - // 当停止播放时重置循环起始索引 - loopStartIndexRef.current = null; - } - }, [isPlaying, taskSketch.length, setCurrentSketchIndex, currentSketchIndex]); - // 模拟 AI 建议 英文 const mockSuggestions = [ "Refine scene transitions", diff --git a/components/pages/work-flow/task-info.tsx b/components/pages/work-flow/task-info.tsx index f46c2c4..c1385ce 100644 --- a/components/pages/work-flow/task-info.tsx +++ b/components/pages/work-flow/task-info.tsx @@ -20,6 +20,10 @@ interface TaskInfoProps { taskObject: any; currentLoadingText: string; dataLoadError?: string | null; + currentStep: string; + sketchCount: number; + isGeneratingVideo: boolean; + taskVideos: any[]; } // 根据加载文本返回对应的图标 @@ -47,10 +51,134 @@ const getStageIcon = (loadingText: string) => { const TAG_COLORS = ['#FF5733', '#126821', '#8d3913', '#FF33A1', '#A133FF', '#FF3333', '#3333FF', '#A1A1A1', '#a1115e', '#30527f']; -export function TaskInfo({ isLoading, taskObject, currentLoadingText, dataLoadError }: TaskInfoProps) { +export function TaskInfo({ + isLoading, + taskObject, + currentLoadingText, + dataLoadError, + currentStep, + sketchCount, + isGeneratingVideo, + taskVideos +}: TaskInfoProps) { const StageIcon = getStageIcon(currentLoadingText); const [isScriptModalOpen, setIsScriptModalOpen] = useState(false); - + const [currentStage, setCurrentStage] = useState(0); + const prevSketchCountRef = useRef(sketchCount); + const prevIsGeneratingVideoRef = useRef(isGeneratingVideo); + const prevTaskVideosLengthRef = useRef(taskVideos?.length || 0); + const timerRef = useRef(null); + + // 清理定时器 + useEffect(() => { + return () => { + if (timerRef.current) { + clearTimeout(timerRef.current); + } + }; + }, []); + + // 监听 currentStep 为 '1' 的情况 + useEffect(() => { + console.log('Current Step Effect:', { currentStep, hasTitle: !!taskObject?.title }); + + // 清除之前的定时器 + if (timerRef.current) { + clearTimeout(timerRef.current); + timerRef.current = null; + } + + if (currentStep === '1' && taskObject?.title) { + console.log('Setting up timer for script modal...'); + timerRef.current = setTimeout(() => { + console.log('Opening script modal with stage 0'); + setIsScriptModalOpen(true); + setCurrentStage(0); + }, 5000); + } + }, [currentStep, taskObject?.title]); + + // 监听 sketchCount 变化 + useEffect(() => { + console.log('SketchCount Effect:', { + prevCount: prevSketchCountRef.current, + currentCount: sketchCount, + isModalOpen: isScriptModalOpen + }); + + // 从 0 变为 1 时关闭弹窗 + if (prevSketchCountRef.current === 0 && sketchCount === 1) { + console.log('Closing modal: sketchCount changed from 0 to 1'); + setIsScriptModalOpen(false); + } + // 从 1 变为其他非 0 值时切换弹窗 + else if (prevSketchCountRef.current === 1 && sketchCount !== 0 && sketchCount !== 1) { + console.log('Toggling modal: sketchCount changed from 1 to other value'); + setIsScriptModalOpen(prev => { + const newState = !prev; + if (newState) { + setCurrentStage(1); + } + return newState; + }); + } + + prevSketchCountRef.current = sketchCount; + }, [sketchCount]); + + // 监听视频生成状态变化 + useEffect(() => { + const currentTaskVideosLength = taskVideos?.length || 0; + console.log('Video Generation Effect:', { + prevGenerating: prevIsGeneratingVideoRef.current, + currentGenerating: isGeneratingVideo, + prevVideosLength: prevTaskVideosLengthRef.current, + currentVideosLength: currentTaskVideosLength, + isModalOpen: isScriptModalOpen + }); + + if ( + (prevIsGeneratingVideoRef.current === false && isGeneratingVideo === true) || + prevTaskVideosLengthRef.current !== currentTaskVideosLength + ) { + console.log('Toggling modal due to video generation changes'); + setIsScriptModalOpen(prev => { + const newState = !prev; + if (newState) { + setCurrentStage(2); + } + return newState; + }); + } + + prevIsGeneratingVideoRef.current = isGeneratingVideo; + prevTaskVideosLengthRef.current = currentTaskVideosLength; + }, [isGeneratingVideo, taskVideos]); + + // 监听最终步骤 + useEffect(() => { + console.log('Final Steps Effect:', { currentStep, isModalOpen: isScriptModalOpen }); + + if (currentStep === '5') { + console.log('Opening modal for final review'); + setIsScriptModalOpen(true); + setCurrentStage(3); + } else if (currentStep === '6') { + console.log('Closing modal at completion'); + setIsScriptModalOpen(false); + } + }, [currentStep]); + + // 弹窗状态变化时的日志 + useEffect(() => { + console.log('Modal State Changed:', { + isOpen: isScriptModalOpen, + currentStage, + currentStep, + sketchCount + }); + }, [isScriptModalOpen, currentStage, currentStep, sketchCount]); + // 使用 useMemo 缓存标签颜色映射 const tagColors = useMemo(() => { if (!taskObject?.tags) return {}; @@ -61,13 +189,13 @@ export function TaskInfo({ isLoading, taskObject, currentLoadingText, dataLoadEr }, [taskObject?.tags]); // 只在 tags 改变时重新计算 // 自动触发打开 剧本 弹窗 延迟5秒 - useEffect(() => { - if (taskObject?.title && currentLoadingText !== 'Task completed') { - setTimeout(() => { - setIsScriptModalOpen(true); - }, 5000); - } - }, [taskObject?.title]); + // useEffect(() => { + // if (taskObject?.title && currentLoadingText !== 'Task completed') { + // setTimeout(() => { + // setIsScriptModalOpen(true); + // }, 5000); + // } + // }, [taskObject?.title]); if (isLoading) { return ( @@ -181,7 +309,7 @@ export function TaskInfo({ isLoading, taskObject, currentLoadingText, dataLoadEr {taskObject?.title || 'loading project info...'} - {/*
+
-
*/} +
) : 'loading project info...'} @@ -209,7 +337,11 @@ export function TaskInfo({ isLoading, taskObject, currentLoadingText, dataLoadEr setIsScriptModalOpen(false)} + onClose={() => { + console.log('Modal manually closed'); + setIsScriptModalOpen(false); + }} + currentStage={currentStage} /> {currentLoadingText === 'Task completed' ? ( diff --git a/components/pages/work-flow/use-workflow-data.tsx b/components/pages/work-flow/use-workflow-data.tsx index 4904dd1..eb7902b 100644 --- a/components/pages/work-flow/use-workflow-data.tsx +++ b/components/pages/work-flow/use-workflow-data.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useState, useEffect } from 'react'; +import { useState, useEffect, useCallback } from 'react'; import { useSearchParams } from 'next/navigation'; import { detailScriptEpisodeNew, getScriptTitle, getRunningStreamData } from '@/api/video_flow'; @@ -55,6 +55,7 @@ export function useWorkflowData() { // 更新 taskObject 的类型 const [taskObject, setTaskObject] = useState(null); const [taskSketch, setTaskSketch] = useState([]); + const [taskShotSketch, setTaskShotSketch] = useState([]); const [taskVideos, setTaskVideos] = useState([]); const [sketchCount, setSketchCount] = useState(0); const [isLoading, setIsLoading] = useState(true); @@ -70,6 +71,49 @@ export function useWorkflowData() { const [dataLoadError, setDataLoadError] = useState(null); const [needStreamData, setNeedStreamData] = useState(false); + // 自动开始播放一轮 + const autoPlaySketch = useCallback(() => { + return new Promise((resolve) => { + let currentIndex = 0; + const interval = 2000; // 每个草图显示2秒 + + const playNext = () => { + if (currentIndex < taskSketch.length) { + setCurrentSketchIndex(currentIndex); + currentIndex++; + setTimeout(playNext, interval); + } else { + // 播放完成后重置到第一个 + setTimeout(() => { + setCurrentSketchIndex(0); + resolve(); + }, 500); // 短暂延迟后重置 + } + }; + + // 开始播放 + playNext(); + }); + }, [taskSketch.length]); + + // 草图生成完毕后自动播放一轮 + useEffect(() => { + const handleAutoPlay = async () => { + if (!isGeneratingSketch && taskSketch.length > 0 && sketchCount === totalSketchCount && currentStep === '3' && !taskVideos.length) { + await autoPlaySketch(); + } + }; + + handleAutoPlay(); + }, [sketchCount, totalSketchCount, isGeneratingSketch, autoPlaySketch]); + + // 添加手动播放控制 + const handleManualPlay = useCallback(async () => { + if (!isGeneratingSketch && taskSketch.length > 0) { + await autoPlaySketch(); + } + }, [isGeneratingSketch, taskSketch.length, autoPlaySketch]); + // 获取流式数据 const fetchStreamData = async () => { if (!episodeId || !needStreamData) return; @@ -143,8 +187,8 @@ export function useWorkflowData() { } } if (task.task_name === 'generate_shot_sketch' && task.task_result) { - if (task.task_result.data.length >= 0 && taskSketch.length < task.task_result.data.length) { - console.log('----------正在生成草图中 替换 sketch 数据'); + if (task.task_result.data.length >= 0 && taskShotSketch.length < task.task_result.data.length) { + console.log('----------正在生成草图中 替换 sketch 数据', taskShotSketch.length, task.task_result.data.length); // 正在生成草图中 替换 sketch 数据 const sketchList = []; for (const sketch of task.task_result.data) { @@ -154,6 +198,7 @@ export function useWorkflowData() { }); } setTaskSketch(sketchList); + setTaskShotSketch(sketchList); setSketchCount(sketchList.length); setIsGeneratingSketch(true); setCurrentSketchIndex(sketchList.length - 1); @@ -162,19 +207,23 @@ export function useWorkflowData() { if (task.task_status === 'COMPLETED') { // 草图生成完成 setIsGeneratingSketch(false); + setIsGeneratingVideo(true); sketchCount = task.task_result.total_count; console.log('----------草图生成完成', sketchCount); - loadingText = LOADING_TEXT_MAP.video(0, task.task_result.total_count); - finalStep = '2'; + loadingText = LOADING_TEXT_MAP.getVideoStatus; + finalStep = '3'; + } else { + } setTotalSketchCount(task.task_result.total_count); } if (task.task_name === 'generate_videos' && task.task_result) { - if (task.task_result.data.length >= 0 && taskVideos.length !== task.task_result.data.length) { + const realTaskResultData = task.task_result.data.filter((item: any) => item.urls && item.urls.length > 0); + if (realTaskResultData.length >= 0 && taskVideos.length !== realTaskResultData.length) { console.log('----------正在生成视频中-发生变化才触发'); // 正在生成视频中 替换视频数据 const videoList = []; - for (const video of task.task_result.data) { + for (const video of realTaskResultData) { // 每一项 video 有多个视频 先默认取第一个 videoList.push({ url: video.urls[0], @@ -349,6 +398,7 @@ export function useWorkflowData() { }); } setTaskSketch(sketchList); + setTaskShotSketch(sketchList); setSketchCount(sketchList.length); setTotalSketchCount(data.shot_sketch.total_count); // 设置为最后一个草图 @@ -357,28 +407,31 @@ export function useWorkflowData() { setCurrentSketchIndex(data.shot_sketch.data.length - 1); loadingText = LOADING_TEXT_MAP.shotSketch(data.shot_sketch.data.length, data.shot_sketch.total_count); } else { - finalStep = '2'; + finalStep = '3'; + setIsGeneratingVideo(true); if (!data.character || !data.character.data || !data.character.data.length) { - loadingText = LOADING_TEXT_MAP.video(0, data.shot_sketch.data.length); + loadingText = LOADING_TEXT_MAP.getVideoStatus; } } } - if (data.video && data.video.data && data.video.data.length > 0) { + const realDataVideoData = data.video.data.filter((item: any) => item.urls && item.urls.length > 0); + if (realDataVideoData.length > 0) { const videoList = []; - for (const video of data.video.data) { - // 每一项 video 有多个视频 先默认取第一个 + console.log('----------data.video.data', data.video.data); + for (const video of realDataVideoData) { + // 每一项 video 有多个视频 默认取存在的项 videoList.push({ - url: video.urls[0], + url: video.urls && video.urls.length > 0 ? video.urls.find((url: string) => url) : null, script: video.description, audio: null, }); } setTaskVideos(videoList); // 如果在视频步骤,设置为最后一个视频 - if (data.video.total_count > data.video.data.length) { + if (data.video.total_count > realDataVideoData.length) { setIsGeneratingVideo(true); - setCurrentSketchIndex(data.video.data.length - 1); - loadingText = LOADING_TEXT_MAP.video(data.video.data.length, data.video.total_count); + setCurrentSketchIndex(realDataVideoData.length - 1); + loadingText = LOADING_TEXT_MAP.video(realDataVideoData.length, data.video.total_count); } else { finalStep = '4'; loadingText = LOADING_TEXT_MAP.audio; @@ -459,5 +512,6 @@ export function useWorkflowData() { dataLoadError, setCurrentSketchIndex, retryLoadData, + handleManualPlay, }; } \ No newline at end of file diff --git a/components/ui/script-modal.tsx b/components/ui/script-modal.tsx index 03da18d..15f12d7 100644 --- a/components/ui/script-modal.tsx +++ b/components/ui/script-modal.tsx @@ -7,9 +7,10 @@ import WorkOffice from '@/components/workflow/work-office/work-office'; interface ScriptModalProps { isOpen: boolean; onClose: () => void; + currentStage?: number; } -export function ScriptModal({ isOpen, onClose }: ScriptModalProps) { +export function ScriptModal({ isOpen, onClose, currentStage = 0 }: ScriptModalProps) { return ( {isOpen && ( @@ -76,7 +77,7 @@ export function ScriptModal({ isOpen, onClose }: ScriptModalProps) { animate={{ opacity: 1 }} transition={{ delay: 0.1, duration: 0.2 }} > - + diff --git a/components/workflow/work-office/work-office.tsx b/components/workflow/work-office/work-office.tsx index 6ffd70d..d632707 100644 --- a/components/workflow/work-office/work-office.tsx +++ b/components/workflow/work-office/work-office.tsx @@ -80,8 +80,12 @@ const ThinkingDots = ({ show, text, color }: { show: boolean; text: string; colo ); }; -const WorkOffice: React.FC = () => { - const [currentStage, setCurrentStage] = useState(0); +interface WorkOfficeProps { + initialStage?: number; +} + +const WorkOffice: React.FC = ({ initialStage = 0 }) => { + const [currentStage, setCurrentStage] = useState(initialStage); const [isPlaying, setIsPlaying] = useState(true); const [progress, setProgress] = useState(0); const [currentContent, setCurrentContent] = useState>(scriptwriterData);