This commit is contained in:
海龙 2025-08-14 16:35:51 +08:00
parent 90fb57f0fd
commit 7d6c60ac42
3 changed files with 123 additions and 68 deletions

View File

@ -1,5 +1,5 @@
"use client" "use client"
import React, { useRef, useEffect } from "react"; import React, { useRef, useEffect, useCallback } from "react";
import "./style/work-flow.css"; import "./style/work-flow.css";
import { Skeleton } from "@/components/ui/skeleton"; import { Skeleton } from "@/components/ui/skeleton";
import { AISuggestionBar } from "@/components/ai-suggestion-bar"; import { AISuggestionBar } from "@/components/ai-suggestion-bar";
@ -16,7 +16,7 @@ import { GlassIconButton } from '@/components/ui/glass-icon-button';
import { SaveEditUseCase } from "@/app/service/usecase/SaveEditUseCase"; import { SaveEditUseCase } from "@/app/service/usecase/SaveEditUseCase";
import { useSearchParams } from "next/navigation"; import { useSearchParams } from "next/navigation";
export default function WorkFlow() { const WorkFlow = React.memo(function WorkFlow() {
console.log('WorkFlow--0294877777777777') console.log('WorkFlow--0294877777777777')
const containerRef = useRef<HTMLDivElement>(null); const containerRef = useRef<HTMLDivElement>(null);
const [isEditModalOpen, setIsEditModalOpen] = React.useState(false); const [isEditModalOpen, setIsEditModalOpen] = React.useState(false);
@ -85,7 +85,7 @@ export default function WorkFlow() {
sketchCount, sketchCount,
totalSketchCount totalSketchCount
}); });
}, [isGeneratingSketch, taskSketch.length, sketchCount, totalSketchCount, currentStep, isPlaying]); }, [currentStep, isGeneratingSketch, isGeneratingVideo, isPlaying, taskSketch.length, sketchCount, totalSketchCount]);
// 专门监控isPlaying状态变化 // 专门监控isPlaying状态变化
useEffect(() => { useEffect(() => {
@ -108,18 +108,18 @@ export default function WorkFlow() {
"Adjust lens language" "Adjust lens language"
]; ];
const handleEditModalOpen = (tab: string) => { const handleEditModalOpen = useCallback((tab: string) => {
setActiveEditTab(tab); setActiveEditTab(tab);
setIsEditModalOpen(true); setIsEditModalOpen(true);
}; }, []);
const handleSuggestionClick = (suggestion: string) => { const handleSuggestionClick = useCallback((suggestion: string) => {
console.log('Selected suggestion:', suggestion); console.log('Selected suggestion:', suggestion);
}; }, []);
const handleSubmit = (text: string) => { const handleSubmit = useCallback((text: string) => {
console.log('Submitted text:', text); console.log('Submitted text:', text);
}; }, []);
return ( return (
<ErrorBoundary> <ErrorBoundary>
@ -239,7 +239,7 @@ export default function WorkFlow() {
icon={isPauseWorkFlow ? Play : Pause} icon={isPauseWorkFlow ? Play : Pause}
size='lg' size='lg'
tooltip={isPauseWorkFlow ? "Play" : "Pause"} tooltip={isPauseWorkFlow ? "Play" : "Pause"}
onClick={() => setIsPauseWorkFlow(!isPauseWorkFlow)} onClick={useCallback(() => setIsPauseWorkFlow(!isPauseWorkFlow), [isPauseWorkFlow])}
/> />
{ mode !== 'automatic' && ( { mode !== 'automatic' && (
<GlassIconButton <GlassIconButton
@ -289,4 +289,6 @@ export default function WorkFlow() {
</div> </div>
</ErrorBoundary> </ErrorBoundary>
) )
} });
export default WorkFlow;

View File

@ -1,6 +1,6 @@
'use client'; 'use client';
import React, { useRef, useEffect, useState, SetStateAction } from 'react'; import React, { useRef, useEffect, useState, SetStateAction, useMemo } from 'react';
import { motion, AnimatePresence } from 'framer-motion'; import { motion, AnimatePresence } from 'framer-motion';
import { Edit3, Play, Pause, Volume2, VolumeX, Maximize, Minimize } from 'lucide-react'; import { Edit3, Play, Pause, Volume2, VolumeX, Maximize, Minimize } from 'lucide-react';
import { ProgressiveReveal, presets } from '@/components/ui/progressive-reveal'; import { ProgressiveReveal, presets } from '@/components/ui/progressive-reveal';
@ -32,7 +32,7 @@ interface MediaViewerProps {
mode: string; mode: string;
} }
export function MediaViewer({ export const MediaViewer = React.memo(function MediaViewer({
scriptData, scriptData,
currentStep, currentStep,
currentSketchIndex, currentSketchIndex,
@ -146,6 +146,29 @@ export function MediaViewer({
} }
}; };
// 使用 useMemo 缓存最终视频元素,避免重复创建和请求
const memoizedFinalVideoElement = useMemo(() => {
console.log('final', final);
if (!final?.url) return null;
return (
<video
ref={finalVideoRef}
className="w-full h-full object-cover rounded-lg"
src={final.url}
autoPlay={isFinalVideoPlaying}
loop
playsInline
preload="metadata"
poster={`${final.url}?vframe/jpg/offset/1`}
onLoadedData={handleFinalVideoLoaded}
onPlay={() => setIsFinalVideoPlaying(true)}
onPause={() => setIsFinalVideoPlaying(false)}
onClick={handleVideoClick}
/>
);
}, [final?.url, isFinalVideoPlaying, handleFinalVideoLoaded, handleVideoClick]);
// 包装编辑按钮点击事件 // 包装编辑按钮点击事件
const handleEditClick = (tab: string, from?: string) => { const handleEditClick = (tab: string, from?: string) => {
if (from === 'final') { if (from === 'final') {
@ -323,20 +346,7 @@ export function MediaViewer({
}} }}
className="relative z-10 w-full h-full" className="relative z-10 w-full h-full"
> >
<video {memoizedFinalVideoElement}
ref={finalVideoRef}
className="w-full h-full object-cover rounded-lg"
src={finalVideo.url}
autoPlay={isFinalVideoPlaying}
loop
playsInline
preload="metadata"
poster={`${finalVideo.url}?vframe/jpg/offset/1`}
onLoadedData={handleFinalVideoLoaded}
onPlay={() => setIsFinalVideoPlaying(true)}
onPause={() => setIsFinalVideoPlaying(false)}
onClick={handleVideoClick}
/>
</motion.div> </motion.div>
{/* 操作按钮组 */} {/* 操作按钮组 */}
@ -848,6 +858,7 @@ export function MediaViewer({
// 根据当前步骤渲染对应内容 // 根据当前步骤渲染对应内容
if (Number(currentStep) === 6 || Number(currentStep) === 5.5) { if (Number(currentStep) === 6 || Number(currentStep) === 5.5) {
console.log('1111111111111111', 1111111111111111)
return renderFinalVideo(currentStep); return renderFinalVideo(currentStep);
} }
@ -860,4 +871,4 @@ export function MediaViewer({
} }
return renderSketchContent(); return renderSketchContent();
} });

View File

@ -200,7 +200,7 @@ export function useWorkflowData() {
}, [isGeneratingSketch, taskSketch.length, autoPlaySketch]); }, [isGeneratingSketch, taskSketch.length, autoPlaySketch]);
// 获取流式数据 // 获取流式数据
const fetchStreamData = async () => { const fetchStreamData = useCallback(async () => {
if (!episodeId || !needStreamData) return; if (!episodeId || !needStreamData) return;
try { try {
@ -209,7 +209,6 @@ export function useWorkflowData() {
throw new Error(response.message); throw new Error(response.message);
} }
let sketchCount = 0; let sketchCount = 0;
const all_task_data = response.data; const all_task_data = response.data;
// all_task_data 下标0 和 下标1 换位置 // all_task_data 下标0 和 下标1 换位置
@ -219,8 +218,24 @@ export function useWorkflowData() {
console.log('---look-all_task_data', all_task_data); console.log('---look-all_task_data', all_task_data);
console.log('---look-taskData', taskData); console.log('---look-taskData', taskData);
for (const task of all_task_data) {
// 收集所有需要更新的状态
let stateUpdates: {
taskSketch?: any[];
taskScenes?: any[];
taskShotSketch?: any[];
roles?: any[];
taskVideos?: any[];
isGeneratingSketch?: boolean;
isGeneratingVideo?: boolean;
currentStep?: string;
currentLoadingText?: string;
final?: any;
needStreamData?: boolean;
totalSketchCount?: number;
} = {};
for (const task of all_task_data) {
// 如果有已完成的数据,同步到状态 // 如果有已完成的数据,同步到状态
if (task.task_name === 'generate_sketch' && (task.task_status !== 'COMPLETED' || taskData.sketch.total_count !== taskData.sketch.data.length)) { if (task.task_name === 'generate_sketch' && (task.task_status !== 'COMPLETED' || taskData.sketch.total_count !== taskData.sketch.data.length)) {
taskData.status = '1'; taskData.status = '1';
@ -235,24 +250,24 @@ export function useWorkflowData() {
}); });
} }
taskData.sketch.data = sketchList; taskData.sketch.data = sketchList;
setTaskSketch(sketchList); stateUpdates.taskSketch = sketchList;
setTaskScenes(sketchList); stateUpdates.taskScenes = sketchList;
updateSketchCount(sketchList.length); stateUpdates.isGeneratingSketch = true;
setIsGeneratingSketch(true); stateUpdates.totalSketchCount = task.task_result.total_count;
loadingText = LOADING_TEXT_MAP.sketch(sketchList.length, task.task_result.total_count); loadingText = LOADING_TEXT_MAP.sketch(sketchList.length, task.task_result.total_count);
} }
if (task.task_status === 'COMPLETED') { if (task.task_status === 'COMPLETED') {
// 草图生成完成 // 草图生成完成
taskData.sketch.total_count = taskData.sketch.data.length; taskData.sketch.total_count = taskData.sketch.data.length;
setIsGeneratingSketch(false); stateUpdates.isGeneratingSketch = false;
sketchCount = task.task_result.total_count; sketchCount = task.task_result.total_count;
console.log('----------草图生成完成', sketchCount);
loadingText = LOADING_TEXT_MAP.sketchComplete; loadingText = LOADING_TEXT_MAP.sketchComplete;
taskData.status = '2'; taskData.status = '2';
} }
setTotalSketchCount(task.task_result.total_count); stateUpdates.currentStep = taskData.status;
break; break;
} }
if (task.task_name === 'generate_character' && (task.task_status !== 'COMPLETED' || taskData.character.total_count !== taskData.character.data.length)) { 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) { if (task.task_result.data.length >= 0 && roles.length !== task.task_result.data.length) {
// 正在生成角色中 替换角色数据 // 正在生成角色中 替换角色数据
@ -267,7 +282,7 @@ export function useWorkflowData() {
}); });
} }
taskData.character.data = characterList; taskData.character.data = characterList;
setRoles(characterList); stateUpdates.roles = characterList;
loadingText = LOADING_TEXT_MAP.newCharacter(characterList.length, task.task_result.total_count); loadingText = LOADING_TEXT_MAP.newCharacter(characterList.length, task.task_result.total_count);
} }
if (task.task_status === 'COMPLETED') { if (task.task_status === 'COMPLETED') {
@ -275,11 +290,12 @@ export function useWorkflowData() {
// 角色生成完成 // 角色生成完成
taskData.character.total_count = taskData.character.data.length; taskData.character.total_count = taskData.character.data.length;
taskData.status = '3'; taskData.status = '3';
loadingText = LOADING_TEXT_MAP.getShotSketchStatus; loadingText = LOADING_TEXT_MAP.getShotSketchStatus;
} }
stateUpdates.currentStep = taskData.status;
break; break;
} }
if (task.task_name === 'generate_shot_sketch' && (task.task_status !== 'COMPLETED' || taskData.shot_sketch.total_count !== taskData.shot_sketch.data.length)) { 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); const realShotResultData = task.task_result.data.filter((item: any) => item.url);
if (realShotResultData.length >= 0) { if (realShotResultData.length >= 0) {
@ -294,25 +310,26 @@ export function useWorkflowData() {
}); });
} }
taskData.shot_sketch.data = sketchList; taskData.shot_sketch.data = sketchList;
setTaskSketch(sketchList); stateUpdates.taskSketch = sketchList;
setTaskShotSketch(sketchList); stateUpdates.taskShotSketch = sketchList;
updateSketchCount(sketchList.length); stateUpdates.isGeneratingSketch = true;
setIsGeneratingSketch(true); stateUpdates.totalSketchCount = task.task_result.total_count;
loadingText = LOADING_TEXT_MAP.shotSketch(sketchList.length, task.task_result.total_count); loadingText = LOADING_TEXT_MAP.shotSketch(sketchList.length, task.task_result.total_count);
} }
if (task.task_status === 'COMPLETED') { if (task.task_status === 'COMPLETED') {
// 草图生成完成 // 草图生成完成
taskData.shot_sketch.total_count = taskData.shot_sketch.data.length; taskData.shot_sketch.total_count = taskData.shot_sketch.data.length;
setIsGeneratingSketch(false); stateUpdates.isGeneratingSketch = false;
setIsGeneratingVideo(true); stateUpdates.isGeneratingVideo = true;
sketchCount = task.task_result.total_count; sketchCount = task.task_result.total_count;
console.log('----------草图生成完成', sketchCount); console.log('----------草图生成完成', sketchCount);
loadingText = LOADING_TEXT_MAP.getVideoStatus; loadingText = LOADING_TEXT_MAP.getVideoStatus;
taskData.status = '3'; taskData.status = '3';
} }
setTotalSketchCount(task.task_result.total_count); stateUpdates.currentStep = taskData.status;
break; break;
} }
if (task.task_name === 'generate_videos' && (task.task_status !== 'COMPLETED' || taskData.video.total_count !== taskData.video.data.length)) { 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); const realTaskResultData = task.task_result.data.filter((item: any) => item.urls && item.urls.length > 0);
if (realTaskResultData.length >= 0) { if (realTaskResultData.length >= 0) {
@ -329,65 +346,90 @@ export function useWorkflowData() {
}); });
} }
taskData.video.data = videoList; taskData.video.data = videoList;
setTaskVideos(videoList); stateUpdates.taskVideos = videoList;
updateVideoCount(videoList.length); stateUpdates.isGeneratingVideo = true;
setIsGeneratingVideo(true);
loadingText = LOADING_TEXT_MAP.video(videoList.length, task.task_result.total_count); loadingText = LOADING_TEXT_MAP.video(videoList.length, task.task_result.total_count);
} }
if (task.task_status === 'COMPLETED') { if (task.task_status === 'COMPLETED') {
console.log('----------视频生成完成'); console.log('----------视频生成完成');
// 视频生成完成 // 视频生成完成
taskData.video.total_count = taskData.video.data.length; taskData.video.total_count = taskData.video.data.length;
setIsGeneratingVideo(false); stateUpdates.isGeneratingVideo = false;
taskData.status = '4'; taskData.status = '4';
// 暂时没有音频生成 直接跳过 // 暂时没有音频生成 直接跳过
taskData.status = '5'; taskData.status = '5';
loadingText = LOADING_TEXT_MAP.postProduction('generating rough cut video...'); loadingText = LOADING_TEXT_MAP.postProduction('generating rough cut video...');
} }
stateUpdates.currentStep = taskData.status;
break; break;
} }
// 粗剪 // 粗剪
if (task.task_name === 'generate_final_simple_video') { if (task.task_name === 'generate_final_simple_video') {
if (task.task_result && task.task_result.video) { if (task.task_result && task.task_result.video) {
setFinal({ stateUpdates.final = {
url: task.task_result.video, url: task.task_result.video,
}) };
taskData.status = '5.5'; taskData.status = '5.5';
loadingText = LOADING_TEXT_MAP.postProduction('generating fine-grained video clips...'); loadingText = LOADING_TEXT_MAP.postProduction('generating fine-grained video clips...');
} }
} }
// 最终剪辑 // 最终剪辑
if (task.task_name === 'generate_final_video') { if (task.task_name === 'generate_final_video') {
if (task.task_result && task.task_result.video) { if (task.task_result && task.task_result.video) {
setFinal({ stateUpdates.final = {
url: task.task_result.video, url: task.task_result.video,
}) };
taskData.status = '6'; taskData.status = '6';
loadingText = LOADING_TEXT_MAP.complete; loadingText = LOADING_TEXT_MAP.complete;
// 停止轮询 // 停止轮询
setNeedStreamData(false); stateUpdates.needStreamData = false;
} }
} }
} }
console.log('-----look-finalStep-----', taskData.status); console.log('-----look-finalStep-----', taskData.status);
// 设置步骤
setCurrentStep(taskData.status); // 设置最终的状态更新
setTaskObject(prev => { stateUpdates.currentStep = taskData.status;
if (!prev) return null; stateUpdates.currentLoadingText = loadingText;
return {
...prev, // 批量更新所有状态 因为上面的循环 会执行很多次更新 影响性能
taskStatus: taskData.status if (stateUpdates.taskSketch) setTaskSketch(stateUpdates.taskSketch);
}; if (stateUpdates.taskScenes) setTaskScenes(stateUpdates.taskScenes);
}); if (stateUpdates.taskShotSketch) setTaskShotSketch(stateUpdates.taskShotSketch);
setCurrentLoadingText(loadingText); if (stateUpdates.roles) setRoles(stateUpdates.roles);
if (stateUpdates.taskVideos) setTaskVideos(stateUpdates.taskVideos);
if (stateUpdates.isGeneratingSketch !== undefined) setIsGeneratingSketch(stateUpdates.isGeneratingSketch);
if (stateUpdates.isGeneratingVideo !== undefined) setIsGeneratingVideo(stateUpdates.isGeneratingVideo);
if (stateUpdates.currentStep) setCurrentStep(stateUpdates.currentStep);
if (stateUpdates.currentLoadingText) setCurrentLoadingText(stateUpdates.currentLoadingText);
if (stateUpdates.final) setFinal(stateUpdates.final);
if (stateUpdates.needStreamData !== undefined) setNeedStreamData(stateUpdates.needStreamData);
if (stateUpdates.totalSketchCount) setTotalSketchCount(stateUpdates.totalSketchCount);
// Redux 更新放在最后,避免触发额外的 useEffect
if (stateUpdates.taskSketch) updateSketchCount(stateUpdates.taskSketch.length);
if (stateUpdates.taskVideos) updateVideoCount(stateUpdates.taskVideos.length);
// 更新 taskObject
if (stateUpdates.currentStep) {
setTaskObject(prev => {
if (!prev) return null;
return {
...prev,
taskStatus: stateUpdates.currentStep!
};
});
}
} catch (error) { } catch (error) {
console.error('获取数据失败:', error); console.error('获取数据失败:', error);
} }
}; }, [episodeId, needStreamData, roles.length, taskShotSketch.length]);
// 轮询获取流式数据 // 轮询获取流式数据
useEffect(() => { useEffect(() => {
@ -403,7 +445,7 @@ export function useWorkflowData() {
clearInterval(interval); clearInterval(interval);
} }
}; };
}, [needStreamData]); }, [needStreamData, fetchStreamData]);
// 初始化数据 // 初始化数据
const initializeWorkflow = async () => { const initializeWorkflow = async () => {