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

View File

@ -1,6 +1,6 @@
'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 { Edit3, Play, Pause, Volume2, VolumeX, Maximize, Minimize } from 'lucide-react';
import { ProgressiveReveal, presets } from '@/components/ui/progressive-reveal';
@ -32,7 +32,7 @@ interface MediaViewerProps {
mode: string;
}
export function MediaViewer({
export const MediaViewer = React.memo(function MediaViewer({
scriptData,
currentStep,
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) => {
if (from === 'final') {
@ -323,20 +346,7 @@ export function MediaViewer({
}}
className="relative z-10 w-full h-full"
>
<video
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}
/>
{memoizedFinalVideoElement}
</motion.div>
{/* 操作按钮组 */}
@ -848,6 +858,7 @@ export function MediaViewer({
// 根据当前步骤渲染对应内容
if (Number(currentStep) === 6 || Number(currentStep) === 5.5) {
console.log('1111111111111111', 1111111111111111)
return renderFinalVideo(currentStep);
}
@ -860,4 +871,4 @@ export function MediaViewer({
}
return renderSketchContent();
}
});

View File

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