diff --git a/api/DTO/movieEdit.ts b/api/DTO/movieEdit.ts index 989013a..ded289a 100644 --- a/api/DTO/movieEdit.ts +++ b/api/DTO/movieEdit.ts @@ -642,6 +642,24 @@ export interface ShotVideo { video_status: number; } +// 执行loading文字映射 +export const LOADING_TEXT_MAP = { + initializing: 'initializing...', + script: 'Generating script...', + getSketchStatus: 'Getting sketch status...', + sketch: (count: number, total: number) => `Generating sketch ${count}/${total}...`, + character: 'Getting character status...', + newCharacter: (count: number, total: number) => `Drawing character ${count}/${total}...`, + getShotSketchStatus: 'Getting shot sketch status...', + shotSketch: (count: number, total: number) => `Generating shot sketch ${count}/${total}...`, + getVideoStatus: 'Getting video status...', + video: (count: number, total: number) => `Generating video ${count}/${total}...`, + audio: 'Generating background audio...', + postProduction: (step: string) => `Post-production: ${step}...`, + final: 'Generating final product...', + complete: 'Task completed' +} as const; + export type Status = 'IN_PROGRESS' | 'COMPLETED' | 'FAILED'; export type Stage = 'script' | 'character' | 'scene' | 'shot_sketch' | 'video' | 'final_video'; // 添加 TaskObject 接口 diff --git a/components/pages/work-flow.tsx b/components/pages/work-flow.tsx index e29b966..c2de686 100644 --- a/components/pages/work-flow.tsx +++ b/components/pages/work-flow.tsx @@ -33,17 +33,9 @@ const WorkFlow = React.memo(function WorkFlow() { const { taskObject, scriptData, - taskSketch, - taskVideos, - sketchCount, isLoading, - currentStep, currentSketchIndex, - isGeneratingSketch, - isGeneratingVideo, currentLoadingText, - totalSketchCount, - final, dataLoadError, setCurrentSketchIndex, retryLoadData, @@ -58,9 +50,8 @@ const WorkFlow = React.memo(function WorkFlow() { const { isVideoPlaying, - togglePlay, toggleVideoPlay, - } = usePlaybackControls(taskSketch, taskVideos, currentStep); + } = usePlaybackControls(taskObject.videos.data, taskObject.currentStage); useEffect(() => { console.log('changedIndex_work-flow', currentSketchIndex, taskObject); @@ -97,10 +88,8 @@ const WorkFlow = React.memo(function WorkFlow() {
@@ -170,14 +159,7 @@ const WorkFlow = React.memo(function WorkFlow() {
diff --git a/components/pages/work-flow/task-info.tsx b/components/pages/work-flow/task-info.tsx index b972cb0..d7d2a1d 100644 --- a/components/pages/work-flow/task-info.tsx +++ b/components/pages/work-flow/task-info.tsx @@ -10,12 +10,11 @@ import { Film, Scissors } from 'lucide-react'; +import { TaskObject } from '@/api/DTO/movieEdit'; interface TaskInfoProps { - isLoading: boolean; - taskObject: any; + taskObject: TaskObject; currentLoadingText: string; - dataLoadError?: string | null; roles: any[]; isPauseWorkFlow: boolean; } @@ -120,17 +119,14 @@ const StageIcons = ({ currentStage, isExpanded, isPauseWorkFlow }: { currentStag ); }; -export function TaskInfo({ - isLoading, - taskObject, +export function TaskInfo({ + taskObject, currentLoadingText, - dataLoadError, roles, isPauseWorkFlow }: TaskInfoProps) { const [isScriptModalOpen, setIsScriptModalOpen] = useState(false); const [currentStage, setCurrentStage] = useState(0); - const [isShowScriptIcon, setIsShowScriptIcon] = useState(true); const [isStageIconsExpanded, setIsStageIconsExpanded] = useState(false); const timerRef = useRef(null); @@ -146,72 +142,39 @@ export function TaskInfo({ timerRef.current = null; } + // 统一更新currentStage + if (currentLoadingText.includes('initializing') || currentLoadingText.includes('script') || currentLoadingText.includes('character')) { + setCurrentStage(0); + } else if (currentLoadingText.includes('sketch') && !currentLoadingText.includes('shot sketch')) { + setCurrentStage(1); + } else if (!currentLoadingText.includes('Post-production') && (currentLoadingText.includes('shot sketch') || currentLoadingText.includes('video'))) { + setCurrentStage(2); + } else if (currentLoadingText.includes('Post-production')) { + setCurrentStage(3); + } + if (currentLoadingText.includes('Task completed')) { console.log('Closing modal at completion'); setIsScriptModalOpen(false); - setIsShowScriptIcon(false); } - if (currentLoadingText.includes('Post-production')) { - if (isScriptModalOpen) { - setIsScriptModalOpen(false); - } - setCurrentStage(3); + if (currentLoadingText.includes('Post-production') || currentLoadingText.includes('status')) { console.log('isScriptModalOpen-Post-production', currentLoadingText, isScriptModalOpen); - timerRef.current = setTimeout(() => { - setIsScriptModalOpen(true); - }, 8000); - } - if (currentLoadingText.includes('Generating video')) { - console.log('isScriptModalOpen-video', currentLoadingText, isScriptModalOpen); if (isScriptModalOpen) { setIsScriptModalOpen(false); - setCurrentStage(2); - - // 延迟8s 再次打开 - timerRef.current = setTimeout(() => { - setIsScriptModalOpen(true); - }, 8000); } else { setIsScriptModalOpen(true); - setCurrentStage(2); - } - } - if (currentLoadingText.includes('video status')) { - if (isScriptModalOpen) { - setIsScriptModalOpen(false); - } - setCurrentStage(2); - } - if (currentLoadingText.includes('Generating sketch') || currentLoadingText.includes('Generating shot sketch')) { - console.log('isScriptModalOpen-sketch', currentLoadingText, isScriptModalOpen); - if (isScriptModalOpen) { - setIsScriptModalOpen(false); - setCurrentStage(1); - - // 延迟8s 再次打开 timerRef.current = setTimeout(() => { - setIsScriptModalOpen(true); + setIsScriptModalOpen(false); }, 8000); - } else { - setIsScriptModalOpen(true); - setCurrentStage(1); } } - if (currentLoadingText.includes('sketch status')) { - if (isScriptModalOpen) { - setIsScriptModalOpen(false); - } - setCurrentStage(1); - } if (currentLoadingText.includes('script')) { console.log('isScriptModalOpen-script', currentLoadingText, isScriptModalOpen); setIsScriptModalOpen(true); - setCurrentStage(0); } if (currentLoadingText.includes('initializing')) { console.log('isScriptModalOpen-initializing', currentLoadingText, isScriptModalOpen); setIsScriptModalOpen(true); - setCurrentStage(0); } return () => { if (timerRef.current) { diff --git a/components/pages/work-flow/thumbnail-grid.tsx b/components/pages/work-flow/thumbnail-grid.tsx index fdd86f6..3a82032 100644 --- a/components/pages/work-flow/thumbnail-grid.tsx +++ b/components/pages/work-flow/thumbnail-grid.tsx @@ -10,28 +10,14 @@ import { TaskObject } from '@/api/DTO/movieEdit'; interface ThumbnailGridProps { isDisabledFocus: boolean; taskObject: TaskObject; - isLoading: boolean; currentSketchIndex: number; - taskSketch: any[]; - taskVideos: any[]; - isGeneratingSketch: boolean; - isGeneratingVideo: boolean; - sketchCount: number; - totalSketchCount: number; onSketchSelect: (index: number) => void; } export function ThumbnailGrid({ isDisabledFocus, taskObject, - isLoading, currentSketchIndex, - taskSketch, - taskVideos, - isGeneratingSketch, - isGeneratingVideo, - sketchCount, - totalSketchCount, onSketchSelect }: ThumbnailGridProps) { const thumbnailsRef = useRef(null); @@ -166,89 +152,11 @@ export function ThumbnailGrid({ console.log('taskObject.currentStage_thumbnail-grid', taskObject.currentStage); }, [taskObject.currentStage]); - // 渲染加载状态 - if (isLoading) { - return ( - <> - - - - - - ); - } - // 粗剪/精剪最终成片阶段不显示缩略图 if (taskObject.currentStage === 'final_video') { return null; } - // 渲染生成中的缩略图 - const renderGeneratingThumbnail = () => { - const currentSketch = taskSketch[currentSketchIndex]; - const defaultBgColors = ['RGB(45, 50, 70)', 'RGB(75, 80, 100)', 'RGB(105, 110, 130)']; - const bgColors = currentSketch?.bg_rgb || defaultBgColors; - - return ( - - {/* 动态渐变背景 */} - - {/* 动态光效 */} - -
-
- -
-
-
- Scene {sketchCount + 1} -
-
- ); - }; - // 渲染视频阶段的缩略图 const renderVideoThumbnails = () => ( taskObject.videos.data.map((video, index) => { @@ -354,7 +262,6 @@ export function ThumbnailGrid({ ); })} - {isGeneratingSketch && sketchCount < totalSketchCount && renderGeneratingThumbnail()} ); diff --git a/components/pages/work-flow/use-playback-controls.tsx b/components/pages/work-flow/use-playback-controls.tsx index 166a403..740377e 100644 --- a/components/pages/work-flow/use-playback-controls.tsx +++ b/components/pages/work-flow/use-playback-controls.tsx @@ -2,7 +2,7 @@ import { useState, useRef, useEffect, useCallback } from 'react'; -export function usePlaybackControls(taskSketch: any[], taskVideos: any[], currentStep: string) { +export function usePlaybackControls(taskVideos: any[], currentStage: string) { const [isPlaying, setIsPlaying] = useState(false); const [isVideoPlaying, setIsVideoPlaying] = useState(true); const [showControls, setShowControls] = useState(false); @@ -19,24 +19,6 @@ export function usePlaybackControls(taskSketch: any[], taskVideos: any[], curren setIsVideoPlaying(prev => !prev); }, []); - // 自动播放逻辑 - 分镜草图(移除重复的定时器逻辑,由主组件处理) - // useEffect(() => { - // if (isPlaying && taskSketch.length > 0) { - // playTimerRef.current = setInterval(() => { - // // 这里的切换逻辑需要在父组件中处理 - // // 因为需要访问 setCurrentSketchIndex - // }, 1000); - // } else if (playTimerRef.current) { - // clearInterval(playTimerRef.current); - // } - - // return () => { - // if (playTimerRef.current) { - // clearInterval(playTimerRef.current); - // } - // }; - // }, [isPlaying, taskSketch.length]); - // 视频自动播放逻辑 useEffect(() => { if (isVideoPlaying && taskVideos.length > 0) { @@ -55,20 +37,12 @@ export function usePlaybackControls(taskSketch: any[], taskVideos: any[], curren }; }, [isVideoPlaying, taskVideos.length]); - // 当切换到视频模式时,停止分镜草图播放(注释掉,让用户手动控制) - // useEffect(() => { - // if (Number(currentStep) >= 3) { - // console.log('切换到步骤3+,停止分镜草图播放'); - // setIsPlaying(false); - // } - // }, [currentStep]); - // 当切换到分镜草图模式时,停止视频播放 useEffect(() => { - if (currentStep !== '3') { + if (currentStage !== 'video') { setIsVideoPlaying(false); } - }, [currentStep]); + }, [currentStage]); return { isPlaying, diff --git a/components/pages/work-flow/use-workflow-data.tsx b/components/pages/work-flow/use-workflow-data.tsx index a6939fa..df92ee4 100644 --- a/components/pages/work-flow/use-workflow-data.tsx +++ b/components/pages/work-flow/use-workflow-data.tsx @@ -3,43 +3,9 @@ import { useState, useEffect, useCallback, useRef, useMemo } from 'react'; import { useSearchParams } from 'next/navigation'; import { detailScriptEpisodeNew, getScriptTitle, getRunningStreamData, pauseMovieProjectPlan, resumeMovieProjectPlan } from '@/api/video_flow'; -import { useAppDispatch, useAppSelector } from '@/lib/store/hooks'; -import { setSketchCount, setVideoCount } from '@/lib/store/workflowSlice'; import { useScriptService } from "@/app/service/Interaction/ScriptService"; import { useUpdateEffect } from '@/app/hooks/useUpdateEffect'; -import { TaskObject, Status, Stage } from '@/api/DTO/movieEdit'; - -// 步骤映射 -const STEP_MAP = { - 'initializing': '0', - 'sketch': '1', - 'character': '2', - 'video': '3', - 'music': '4', - 'final_video': '6' -} as const; -// 执行loading文字映射 -const LOADING_TEXT_MAP = { - initializing: 'initializing...', - script: 'Generating script...', - getSketchStatus: 'Getting sketch status...', - sketch: (count: number, total: number) => `Generating sketch ${count}/${total}...`, - sketchComplete: 'Sketch generation complete', - character: 'Drawing characters...', - newCharacter: (count: number, total: number) => `Drawing character ${count}/${total}...`, - getShotSketchStatus: 'Getting shot sketch status...', - shotSketch: (count: number, total: number) => `Generating shot sketch ${count}/${total}...`, - getVideoStatus: 'Getting video status...', - video: (count: number, total: number) => `Generating video ${count}/${total}...`, - videoComplete: 'Video generation complete', - audio: 'Generating background audio...', - postProduction: (step: string) => `Post-production: ${step}...`, - final: 'Generating final product...', - complete: 'Task completed' -} as const; - -type ApiStep = keyof typeof STEP_MAP; - +import { LOADING_TEXT_MAP, TaskObject, Status, Stage } from '@/api/DTO/movieEdit'; export function useWorkflowData() { useEffect(() => { @@ -53,7 +19,7 @@ export function useWorkflowData() { let tempTaskObject = useRef({ title: '', tags: [], - currentStage: 'script', + currentStage: 'script' as Stage, status: 'IN_PROGRESS' as Status, roles: { data: [], @@ -81,19 +47,8 @@ export function useWorkflowData() { // 更新 taskObject 的类型 const [taskObject, setTaskObject] = useState(tempTaskObject.current); - const [taskSketch, setTaskSketch] = useState([]); - const [taskScenes, setTaskScenes] = useState([]); - const [taskShotSketch, setTaskShotSketch] = useState([]); - const [taskVideos, setTaskVideos] = useState([]); - const [currentStep, setCurrentStep] = useState('0'); const [currentSketchIndex, setCurrentSketchIndex] = useState(0); - const [isGeneratingSketch, setIsGeneratingSketch] = useState(false); - const [isGeneratingVideo, setIsGeneratingVideo] = useState(false); const [currentLoadingText, setCurrentLoadingText] = useState('loading project info...'); - const [totalSketchCount, setTotalSketchCount] = useState(0); - const [roles, setRoles] = useState([]); - const [music, setMusic] = useState([]); - const [final, setFinal] = useState(null); const [dataLoadError, setDataLoadError] = useState(null); const [needStreamData, setNeedStreamData] = useState(false); const [isPauseWorkFlow, setIsPauseWorkFlow] = useState(false); @@ -103,10 +58,6 @@ export function useWorkflowData() { isLoading: true }); - - const dispatch = useAppDispatch(); - const { sketchCount, videoCount } = useAppSelector((state) => state.workflow); - const { scriptBlocksMemo, // 渲染剧本数据 initializeFromProject, @@ -170,24 +121,24 @@ export function useWorkflowData() { console.log('应用剧本'); // 自动模式下 应用剧本;手动模式 需要点击 下一步 触发 // 确保仅自动触发一次 - state.mode.includes('auto') && loadingText.current !== LOADING_TEXT_MAP.getSketchStatus && applyScript(); - loadingText.current = LOADING_TEXT_MAP.getSketchStatus; + state.mode.includes('auto') && loadingText.current !== LOADING_TEXT_MAP.character && applyScript(); + loadingText.current = LOADING_TEXT_MAP.character; } else { loadingText.current = LOADING_TEXT_MAP.script; } } - if (taskObject.currentStage === 'scene') { - const realSketchResultData = taskObject.scenes.data.filter((item: any) => item.status !== 0); - if (taskObject.scenes.total_count > realSketchResultData.length) { - loadingText.current = LOADING_TEXT_MAP.sketch(realSketchResultData.length, taskObject.scenes.total_count); - } else { - loadingText.current = LOADING_TEXT_MAP.character; - } - } if (taskObject.currentStage === 'character') { const realCharacterResultData = taskObject.roles.data.filter((item: any) => item.status !== 0); if (taskObject.roles.total_count > realCharacterResultData.length) { loadingText.current = LOADING_TEXT_MAP.newCharacter(realCharacterResultData.length, taskObject.roles.total_count); + } else { + loadingText.current = LOADING_TEXT_MAP.getSketchStatus; + } + } + if (taskObject.currentStage === 'scene') { + const realSketchResultData = taskObject.scenes.data.filter((item: any) => item.status !== 0); + if (taskObject.scenes.total_count > realSketchResultData.length) { + loadingText.current = LOADING_TEXT_MAP.sketch(realSketchResultData.length, taskObject.scenes.total_count); } else { loadingText.current = LOADING_TEXT_MAP.getShotSketchStatus; } @@ -217,16 +168,6 @@ export function useWorkflowData() { setCurrentLoadingText(loadingText.current); }, [scriptBlocksMemo, taskObject.currentStage, taskObject.scenes.data, taskObject.roles.data, taskObject.shot_sketch.data, taskObject.videos.data, taskObject.status], {mode: 'none'}); - // 更新 setSketchCount - const updateSketchCount = useCallback((count: number) => { - dispatch(setSketchCount(count)); - }, [dispatch]); - - // 更新 setVideoCount - const updateVideoCount = useCallback((count: number) => { - dispatch(setVideoCount(count)); - }, [dispatch]); - // 将 sketchCount 和 videoCount 放到 redux 中 每一次变化也要更新 // 添加手动播放控制 @@ -249,13 +190,7 @@ export function useWorkflowData() { throw new Error(response.message); } - let sketchCount = 0; const all_task_data = response.data; - // all_task_data 下标0 和 下标1 换位置 - const temp = all_task_data[0]; - all_task_data[0] = all_task_data[1]; - all_task_data[1] = temp; - const { current: taskCurrent } = tempTaskObject; console.log('---look-all_task_data', all_task_data); @@ -266,32 +201,6 @@ export function useWorkflowData() { for (const task of all_task_data) { // 如果有已完成的数据,同步到状态 - if (task.task_name === 'generate_sketch' && task.task_result && task.task_result.data) { - let realSketchResultData = task.task_result.data.filter((item: any) => item.image_path); - if (task.task_status === 'COMPLETED') { - realSketchResultData = taskCurrent.scenes.data.filter((item: any) => item.status !== 0); - } - console.log('---look-realSketchResultData', realSketchResultData); - taskCurrent.scenes.total_count = task.task_result.total_count; - if (task.task_status !== 'COMPLETED' || taskCurrent.scenes.total_count !== realSketchResultData.length) { - taskCurrent.currentStage = 'scene'; - // 正在生成草图中 替换 sketch 数据 - const sketchList = []; - for (const sketch of task.task_result.data) { - sketchList.push({ - url: sketch.image_path, - script: sketch.sketch_name, - status: sketch.image_path ? 1 : (task.task_status === 'COMPLETED' ? 2 : 0) - }); - } - taskCurrent.scenes.data = sketchList; - if (task.task_status === 'COMPLETED') { - // 草图生成完成 - } - break; - } - } - if (task.task_name === 'generate_character' && task.task_result && task.task_result.data) { let realCharacterResultData = task.task_result.data.filter((item: any) => item.image_path); if (task.task_status === 'COMPLETED') { @@ -317,6 +226,32 @@ export function useWorkflowData() { } } + + if (task.task_name === 'generate_sketch' && task.task_result && task.task_result.data) { + let realSketchResultData = task.task_result.data.filter((item: any) => item.image_path); + if (task.task_status === 'COMPLETED') { + realSketchResultData = taskCurrent.scenes.data.filter((item: any) => item.status !== 0); + } + console.log('---look-realSketchResultData', realSketchResultData); + taskCurrent.scenes.total_count = task.task_result.total_count; + if (task.task_status !== 'COMPLETED' || taskCurrent.scenes.total_count !== realSketchResultData.length) { + taskCurrent.currentStage = 'scene'; + // 正在生成草图中 替换 sketch 数据 + const sketchList = []; + for (const sketch of task.task_result.data) { + sketchList.push({ + url: sketch.image_path, + script: sketch.sketch_name, + status: sketch.image_path ? 1 : (task.task_status === 'COMPLETED' ? 2 : 0) + }); + } + taskCurrent.scenes.data = sketchList; + if (task.task_status === 'COMPLETED') { + // 草图生成完成 + } + break; + } + } // debugger; if (task.task_name === 'generate_shot_sketch' && task.task_result && task.task_result.data) { @@ -485,27 +420,6 @@ export function useWorkflowData() { // 如果有已完成的数据,同步到状态 if (data) { - if (data.sketch && data.sketch.data) { - taskCurrent.currentStage = 'scene'; - const realSketchResultData = data.sketch.data.filter((item: any) => item.image_path); - const sketchList = []; - for (const sketch of data.sketch.data) { - sketchList.push({ - url: sketch.image_path, - script: sketch.sketch_name, - status: sketch.image_path ? 1 : (data.sketch.task_status === 'COMPLETED' ? 2 : 0) - }); - } - taskCurrent.scenes.data = sketchList; - taskCurrent.scenes.total_count = data.sketch.total_count; - // 设置为最后一个草图 - if (data.sketch.total_count > realSketchResultData.length) { - // 场景生成中 - setIsGeneratingSketch(true); - } else { - // 场景生成完成 - } - } if (data.character && data.character.data && data.character.data.length > 0) { taskCurrent.currentStage = 'character'; const characterList = []; @@ -524,6 +438,26 @@ export function useWorkflowData() { // 角色生成完成 } } + if (data.sketch && data.sketch.data) { + taskCurrent.currentStage = 'scene'; + const realSketchResultData = data.sketch.data.filter((item: any) => item.image_path); + const sketchList = []; + for (const sketch of data.sketch.data) { + sketchList.push({ + url: sketch.image_path, + script: sketch.sketch_name, + status: sketch.image_path ? 1 : (data.sketch.task_status === 'COMPLETED' ? 2 : 0) + }); + } + taskCurrent.scenes.data = sketchList; + taskCurrent.scenes.total_count = data.sketch.total_count; + // 设置为最后一个草图 + if (data.sketch.total_count > realSketchResultData.length) { + // 场景生成中 + } else { + // 场景生成完成 + } + } if (data.shot_sketch && data.shot_sketch.data) { taskCurrent.currentStage = 'shot_sketch'; const realShotResultData = data.shot_sketch.data.filter((item: any) => item.url); @@ -629,17 +563,7 @@ export function useWorkflowData() { // 重试加载数据 const retryLoadData = () => { setDataLoadError(null); - // 重置所有状态 - setTaskSketch([]); - setTaskScenes([]); - setTaskVideos([]); - updateSketchCount(0); - updateVideoCount(0); - setRoles([]); - setMusic([]); - setFinal(null); setCurrentSketchIndex(0); - setCurrentStep('0'); // 重新初始化 initializeWorkflow(); }; @@ -652,21 +576,9 @@ export function useWorkflowData() { return { taskObject, scriptData, - taskSketch, - taskScenes, - taskShotSketch, - taskVideos, - sketchCount, isLoading: state.isLoading, - currentStep, currentSketchIndex, - isGeneratingSketch, - isGeneratingVideo, currentLoadingText, - totalSketchCount, - roles, - music, - final, dataLoadError, setCurrentSketchIndex, retryLoadData,