From 25988af2c30eca4999aabf676a705abef3f93105 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8C=97=E6=9E=B3?= <7854742+wang_rumeng@user.noreply.gitee.com> Date: Thu, 14 Aug 2025 23:58:41 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=B7=A5=E4=BD=9C=E6=B5=81?= =?UTF-8?q?=E7=BB=84=E4=BB=B6=EF=BC=8C=E6=B7=BB=E5=8A=A0=E5=BD=93=E5=89=8D?= =?UTF-8?q?=E9=98=B6=E6=AE=B5=E7=8A=B6=E6=80=81=E7=AE=A1=E7=90=86=EF=BC=8C?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E8=A7=86=E9=A2=91=E7=94=9F=E6=88=90=E7=8A=B6?= =?UTF-8?q?=E6=80=81=E6=98=BE=E7=A4=BA=E9=80=BB=E8=BE=91=EF=BC=8C=E7=A1=AE?= =?UTF-8?q?=E4=BF=9D=E5=9C=A8=E4=B8=8D=E5=90=8C=E9=98=B6=E6=AE=B5=E4=B8=8B?= =?UTF-8?q?=E6=AD=A3=E7=A1=AE=E6=B8=B2=E6=9F=93=E7=BC=A9=E7=95=A5=E5=9B=BE?= =?UTF-8?q?=E5=92=8C=E8=A7=86=E9=A2=91=E5=86=85=E5=AE=B9=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/pages/work-flow.tsx | 44 +++-- components/pages/work-flow/media-viewer.tsx | 176 ++++++------------ components/pages/work-flow/task-info.tsx | 20 +- components/pages/work-flow/thumbnail-grid.tsx | 116 +++++------- .../pages/work-flow/use-workflow-data.tsx | 100 ++++++---- 5 files changed, 198 insertions(+), 258 deletions(-) diff --git a/components/pages/work-flow.tsx b/components/pages/work-flow.tsx index 32a425c..4684827 100644 --- a/components/pages/work-flow.tsx +++ b/components/pages/work-flow.tsx @@ -54,7 +54,8 @@ const WorkFlow = React.memo(function WorkFlow() { setAnyAttribute, applyScript, fallbackToStep, - originalText + originalText, + currentStage } = useWorkflowData(); const { @@ -184,6 +185,7 @@ const WorkFlow = React.memo(function WorkFlow() {
)}
-
- - - -
+ {currentStage !== 'final_video' && currentStage !== 'script' && ( +
+ + + +
+ )} + @@ -233,7 +239,7 @@ const WorkFlow = React.memo(function WorkFlow() { {/* 暂停/播放按钮 */} { - (currentStep !== '6' && currentStep !== '0') && ( + (currentStage !== 'final_video' && currentStage !== 'script') && (
(null); const finalVideoRef = useRef(null); @@ -470,110 +472,56 @@ export const MediaViewer = React.memo(function MediaViewer({ onMouseLeave={() => onControlsChange(false)} > {/* 只在生成过程中或没有视频时使用ProgressiveReveal */} - {(isGeneratingVideo || !taskVideos[currentSketchIndex]) ? ( - taskVideos[currentSketchIndex] ? ( - -
- {/* 背景模糊的图片 */} -
- background +
+ {/* 背景模糊的图片 */} +
+ {/* 生成中 */} + {taskVideos[currentSketchIndex].video_status === 0 && ( +
+
+ + Generating...
- - {/* 视频 */} - -
- - ) : ( -
- {`Sketch -
- ) - ) : ( - /* 生成完成后直接显示视频,不使用ProgressiveReveal */ -
- - {/* 视频 修复播放没有声音 */} -
- )} + + {/* 视频 多个 取第一个 */} + { taskVideos[currentSketchIndex].url && ( + + + )} + +
{/* 操作按钮组 */} @@ -610,21 +558,6 @@ export const MediaViewer = React.memo(function MediaViewer({ whileTap={{ scale: 0.9 }} className="relative" > - {/* 播放时的发光效果 */} - {isVideoPlaying && ( - - )} 2 && Number(currentStep) < 6) { + if (currentStage === 'video') { return renderVideoContent(); } - if (Number(currentStep) === 0) { + if (currentStage === 'script') { return renderScriptContent(); } diff --git a/components/pages/work-flow/task-info.tsx b/components/pages/work-flow/task-info.tsx index 75e71c6..6b3ec65 100644 --- a/components/pages/work-flow/task-info.tsx +++ b/components/pages/work-flow/task-info.tsx @@ -168,11 +168,11 @@ export function TaskInfo({ setCurrentStage(2); // 延迟8s 再次打开 - timerRef.current = setTimeout(() => { - setIsScriptModalOpen(true); - }, 8000); + // timerRef.current = setTimeout(() => { + // setIsScriptModalOpen(true); + // }, 8000); } else { - setIsScriptModalOpen(true); + // setIsScriptModalOpen(true); setCurrentStage(2); } } @@ -180,7 +180,7 @@ export function TaskInfo({ if (isScriptModalOpen) { setIsScriptModalOpen(false); } - setIsScriptModalOpen(true); + // setIsScriptModalOpen(true); setCurrentStage(2); } if (currentLoadingText.includes('sketch')) { @@ -190,11 +190,11 @@ export function TaskInfo({ setCurrentStage(1); // 延迟8s 再次打开 - timerRef.current = setTimeout(() => { - setIsScriptModalOpen(true); - }, 8000); + // timerRef.current = setTimeout(() => { + // setIsScriptModalOpen(true); + // }, 8000); } else { - setIsScriptModalOpen(true); + // setIsScriptModalOpen(true); setCurrentStage(1); } } @@ -202,7 +202,7 @@ export function TaskInfo({ if (isScriptModalOpen) { setIsScriptModalOpen(false); } - setIsScriptModalOpen(true); + // setIsScriptModalOpen(true); setCurrentStage(1); } if (currentLoadingText.includes('script')) { diff --git a/components/pages/work-flow/thumbnail-grid.tsx b/components/pages/work-flow/thumbnail-grid.tsx index 925129f..b45862b 100644 --- a/components/pages/work-flow/thumbnail-grid.tsx +++ b/components/pages/work-flow/thumbnail-grid.tsx @@ -4,6 +4,7 @@ import React, { useRef, useEffect, useState } from 'react'; import { motion } from 'framer-motion'; import { Skeleton } from '@/components/ui/skeleton'; import { ProgressiveReveal, presets } from '@/components/ui/progressive-reveal'; +import { Loader2, X } from 'lucide-react'; interface ThumbnailGridProps { isLoading: boolean; @@ -17,6 +18,7 @@ interface ThumbnailGridProps { sketchCount: number; totalSketchCount: number; onSketchSelect: (index: number) => void; + currentStage: string; } export function ThumbnailGrid({ @@ -30,7 +32,8 @@ export function ThumbnailGrid({ isGeneratingVideo, sketchCount, totalSketchCount, - onSketchSelect + onSketchSelect, + currentStage }: ThumbnailGridProps) { const thumbnailsRef = useRef(null); const [isDragging, setIsDragging] = useState(false); @@ -211,78 +214,47 @@ export function ThumbnailGrid({ ${currentSketchIndex === index ? 'ring-2 ring-blue-500 z-10' : 'hover:ring-2 hover:ring-blue-500/50'}`} onClick={() => !isDragging && onSketchSelect(index)} > - {/* 底层草图,始终显示 未生成对应的视频时显示的草图模糊掉 */} -
- {`Thumbnail -
- {/* 视频层,只在有视频时用ProgressiveReveal动画显示 */} - {taskVideos[index] && ( -
- {isGeneratingVideo ? ( - -
-
-
- ) : ( - /* 生成完成后直接显示视频,不使用ProgressiveReveal */ -
-
Scene {index + 1} @@ -381,7 +353,7 @@ export function ThumbnailGrid({ onFocus={() => setIsFocused(true)} onBlur={() => setIsFocused(false)} > - {Number(currentStep) > 2 && taskVideos.length > 0 && Number(currentStep) < 6 + {currentStage === 'video' ? renderVideoThumbnails() : renderSketchThumbnails() } diff --git a/components/pages/work-flow/use-workflow-data.tsx b/components/pages/work-flow/use-workflow-data.tsx index 5c3546e..6560121 100644 --- a/components/pages/work-flow/use-workflow-data.tsx +++ b/components/pages/work-flow/use-workflow-data.tsx @@ -82,19 +82,25 @@ export function useWorkflowData() { const [needStreamData, setNeedStreamData] = useState(false); const [isPauseWorkFlow, setIsPauseWorkFlow] = useState(false); const [mode, setMode] = useState<'automatic' | 'manual' | 'auto'>('automatic'); + const [currentStage, setCurrentStage] = useState<'script' | 'character' | 'sketch' | 'shot_sketch' | 'video' | 'final_video'>('script'); let taskData: any = { sketch: { data: [], total_count: -1 }, character: { data: [], total_count: -1 }, shot_sketch: { data: [], total_count: -1 }, video: { data: [], total_count: -1 }, - status: '0' + status: '0', + currentStage: 'script' }; let loadingText: any = LOADING_TEXT_MAP.initializing; const dispatch = useAppDispatch(); const { sketchCount, videoCount } = useAppSelector((state) => state.workflow); + useEffect(() => { + console.log('---------currentStage', currentStage); + }, [currentStage]); + const { scriptBlocksMemo, // 渲染剧本数据 initializeFromProject, @@ -233,6 +239,7 @@ export function useWorkflowData() { final?: any; needStreamData?: boolean; totalSketchCount?: number; + currentStage?: string; } = {}; for (const task of all_task_data) { @@ -241,6 +248,7 @@ export function useWorkflowData() { taskData.status = '1'; const realSketchResultData = task.task_result.data.filter((item: any) => item.image_path); if (realSketchResultData.length >= 0) { + taskData.currentStage = 'sketch'; // 正在生成草图中 替换 sketch 数据 const sketchList = []; for (const sketch of realSketchResultData) { @@ -270,6 +278,7 @@ export function useWorkflowData() { if (task.task_name === 'generate_character' && (task.task_status !== 'COMPLETED' || taskData.character.total_count !== taskData.character.data.length)) { if (task.task_result.data.length >= 0 && roles.length !== task.task_result.data.length) { + taskData.currentStage = 'character'; // 正在生成角色中 替换角色数据 const characterList = []; for (const character of task.task_result.data) { @@ -299,6 +308,7 @@ export function useWorkflowData() { if (task.task_name === 'generate_shot_sketch' && (task.task_status !== 'COMPLETED' || taskData.shot_sketch.total_count !== taskData.shot_sketch.data.length)) { const realShotResultData = task.task_result.data.filter((item: any) => item.url); if (realShotResultData.length >= 0) { + taskData.currentStage = 'shot_sketch'; taskData.status = '1'; console.log('----------正在生成草图中 替换 sketch 数据', taskShotSketch.length, realShotResultData.length); // 正在生成草图中 替换 sketch 数据 @@ -331,24 +341,32 @@ export function useWorkflowData() { } if (task.task_name === 'generate_videos' && (task.task_status !== 'COMPLETED' || taskData.video.total_count !== taskData.video.data.length)) { - const realTaskResultData = task.task_result.data.filter((item: any) => item.urls && item.urls.length > 0); - if (realTaskResultData.length >= 0) { - console.log('----------正在生成视频中', realTaskResultData.length); + if (task.task_result.data) { + const realTaskResultData = task.task_result.data.filter((item: any) => (item.urls || (item.video_status !== 0 && item.video_status !== undefined))); // 正在生成视频中 替换视频数据 const videoList = []; - for (const video of realTaskResultData) { + let video_status = 0; + for (const video of task.task_result.data) { + // 适配旧数据 + video_status = video.video_status === undefined ? (video.urls ? 1 : 0) : video.video_status; + video_status = task.task_status === 'COMPLETED' && video_status === 0 ? 2 : video_status; // 每一项 video 有多个视频 先默认取第一个 videoList.push({ - url: video.urls && video.urls.length > 0 ? video.urls.find((url: string) => url) : null, + url: video.urls, script: video.description, audio: null, video_id: video.video_id, + video_status: video_status, // 0 生成中 1 生成完成 2 生成失败 }); } + if (realTaskResultData.length > 0) { + taskData.currentStage = 'video'; + } + console.log('----------正在生成视频中', realTaskResultData.length); taskData.video.data = videoList; stateUpdates.taskVideos = videoList; stateUpdates.isGeneratingVideo = true; - loadingText = LOADING_TEXT_MAP.video(videoList.length, task.task_result.total_count); + loadingText = LOADING_TEXT_MAP.video(realTaskResultData.length, task.task_result.total_count); } if (task.task_status === 'COMPLETED') { console.log('----------视频生成完成'); @@ -368,6 +386,7 @@ export function useWorkflowData() { // 粗剪 if (task.task_name === 'generate_final_simple_video') { if (task.task_result && task.task_result.video) { + taskData.currentStage = 'final_video'; stateUpdates.final = { url: task.task_result.video, }; @@ -379,6 +398,7 @@ export function useWorkflowData() { // 最终剪辑 if (task.task_name === 'generate_final_video') { if (task.task_result && task.task_result.video) { + taskData.currentStage = 'final_video'; stateUpdates.final = { url: task.task_result.video, }; @@ -415,6 +435,7 @@ export function useWorkflowData() { if (stateUpdates.taskSketch) updateSketchCount(stateUpdates.taskSketch.length); if (stateUpdates.taskVideos) updateVideoCount(stateUpdates.taskVideos.length); + if (stateUpdates.currentStage) taskData.currentStage = stateUpdates.currentStage; // 更新 taskObject if (stateUpdates.currentStep) { setTaskObject(prev => { @@ -500,6 +521,7 @@ export function useWorkflowData() { // 如果有已完成的数据,同步到状态 if (data) { if (data.sketch && data.sketch.data) { + taskData.currentStage = 'sketch'; taskData.status = '1'; const realSketchResultData = data.sketch.data.filter((item: any) => item.image_path); const sketchList = []; @@ -526,6 +548,7 @@ export function useWorkflowData() { } } if (data.character && data.character.data && data.character.data.length > 0) { + taskData.currentStage = 'character'; const characterList = []; for (const character of data.character.data) { characterList.push({ @@ -549,6 +572,7 @@ export function useWorkflowData() { } } if (data.shot_sketch && data.shot_sketch.data) { + taskData.currentStage = 'shot_sketch'; const realShotResultData = data.shot_sketch.data.filter((item: any) => item.url); const sketchList = []; for (const sketch of realShotResultData) { @@ -575,43 +599,46 @@ export function useWorkflowData() { } } if (data.video.data) { - const realDataVideoData = data.video.data.filter((item: any) => item.urls && item.urls.length > 0); + const realDataVideoData = data.video.data.filter((item: any) => (item.urls || (item.video_status !== 0 && item.video_status !== undefined))); if (realDataVideoData.length === 0 && taskData.status === '3') { loadingText = LOADING_TEXT_MAP.video(0, data.video.total_count); } if (realDataVideoData.length > 0) { - const videoList = []; - console.log('----------data.video.data', data.video.data); - for (const video of realDataVideoData) { - // 每一项 video 有多个视频 默认取存在的项 - videoList.push({ - url: video.urls && video.urls.length > 0 ? video.urls.find((url: string) => url) : null, - script: video.description, - audio: null, - video_id: video.video_id, - }); - } - taskData.video.data = videoList; - taskData.video.total_count = data.video.total_count; - setTaskVideos(videoList); - updateVideoCount(videoList.length); - // 如果在视频步骤,设置为最后一个视频 - if (data.video.total_count > realDataVideoData.length) { - setIsGeneratingVideo(true); - loadingText = LOADING_TEXT_MAP.video(realDataVideoData.length, data.video.total_count); - } else { - taskData.status = '4'; - loadingText = LOADING_TEXT_MAP.audio; + taskData.currentStage = 'video'; + } + const videoList = []; + console.log('----------data.video.data', data.video.data); + for (const video of data.video.data) { + // 每一项 video 有多个视频 默认取存在的项 + videoList.push({ + url: video.urls, + script: video.description, + audio: null, + video_id: video.video_id, + video_status: video.video_status === undefined ? (video.urls ? 1 : 0) : video.video_status, // 0 生成中 1 生成完成 2 生成失败 + }); + } + taskData.video.data = videoList; + taskData.video.total_count = data.video.total_count; + setTaskVideos(videoList); + updateVideoCount(videoList.length); + // 如果在视频步骤,设置为最后一个视频 + if (data.video.total_count > realDataVideoData.length) { + setIsGeneratingVideo(true); + loadingText = LOADING_TEXT_MAP.video(realDataVideoData.length, data.video.total_count); + } else { + taskData.status = '4'; + loadingText = LOADING_TEXT_MAP.audio; - // 暂时没有音频生成 直接跳过 - taskData.status = '5'; - loadingText = LOADING_TEXT_MAP.postProduction('generating rough cut video...'); - } + // 暂时没有音频生成 直接跳过 + taskData.status = '5'; + loadingText = LOADING_TEXT_MAP.postProduction('generating rough cut video...'); } } // 粗剪 if ((data as any).final_simple_video && (data as any).final_simple_video.video) { + taskData.currentStage = 'final_video'; setFinal({ url: (data as any).final_simple_video.video }); @@ -620,6 +647,7 @@ export function useWorkflowData() { } if (data.final_video && data.final_video.video) { + taskData.currentStage = 'final_video'; setFinal({ url: data.final_video.video }); @@ -631,6 +659,7 @@ export function useWorkflowData() { console.log('---look-taskData', taskData); // 设置步骤 + setCurrentStage(taskData.currentStage); setCurrentStep(taskData.status); setTaskObject(prev => { if (!prev) return null; @@ -718,6 +747,7 @@ export function useWorkflowData() { setAnyAttribute, applyScript, fallbackToStep, - originalText + originalText, + currentStage }; }