'use client'; import { useState, useEffect, useCallback } 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'; // 步骤映射 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 + 1 > total ? total : count + 1}/${total}...`, sketchComplete: 'Sketch generation complete', character: 'Drawing characters...', newCharacter: (count: number, total: number) => `Drawing character ${count + 1 > total ? total : count + 1}/${total}...`, getShotSketchStatus: 'Getting shot sketch status...', shotSketch: (count: number, total: number) => `Generating shot sketch ${count + 1 > total ? total : count + 1}/${total}...`, getVideoStatus: 'Getting video status...', video: (count: number, total: number) => `Generating video ${count + 1 > total ? total : count + 1}/${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; // 添加 TaskObject 接口 interface TaskObject { taskStatus: string; title: string; currentLoadingText: string; sketchCount?: number; totalSketchCount?: number; isGeneratingSketch?: boolean; isGeneratingVideo?: boolean; roles?: any[]; music?: any[]; final?: any; tags?: any[]; } export function useWorkflowData() { console.log('98877766777777888888990') const searchParams = useSearchParams(); const episodeId = searchParams.get('episodeId') || ''; // 更新 taskObject 的类型 const [taskObject, setTaskObject] = useState(null); const [originalText, setOriginalText] = useState(''); const [scriptData, setScriptData] = useState(null); const [taskSketch, setTaskSketch] = useState([]); const [taskScenes, setTaskScenes] = useState([]); const [taskShotSketch, setTaskShotSketch] = useState([]); const [taskVideos, setTaskVideos] = useState([]); const [isLoading, setIsLoading] = useState(true); 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); const [mode, setMode] = useState<'automatic' | 'manual' | 'auto'>('automatic'); const 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' }; let loadingText: any = LOADING_TEXT_MAP.initializing; const dispatch = useAppDispatch(); const { sketchCount, videoCount } = useAppSelector((state) => state.workflow); const { scriptBlocksMemo, // 渲染剧本数据 initializeFromProject, setAnyAttribute, applyScript } = useScriptService(); // 初始化剧本 useUpdateEffect(() => { if (currentStep !== '0') { console.log('开始初始化剧本', originalText,episodeId); // TODO 为什么一开始没项目id originalText && initializeFromProject(episodeId, originalText).then(() => { console.log('应用剧本'); // 自动模式下 应用剧本;手动模式 需要点击 下一步 触发 mode.includes('auto') && applyScript(); }); } }, [originalText], {mode: 'none'}); // 监听剧本加载完毕 useEffect(() => { if (scriptBlocksMemo.length > 0) { console.log('scriptBlocksMemo 更新:', scriptBlocksMemo); setScriptData(scriptBlocksMemo); setCurrentLoadingText(LOADING_TEXT_MAP.script); } }, [scriptBlocksMemo]); // 监听继续 请求更新数据 useUpdateEffect(() => { if (currentStep === '6') { return; } if (isPauseWorkFlow) { pauseMovieProjectPlan({ project_id: episodeId }); } else { resumeMovieProjectPlan({ project_id: episodeId }); } }, [isPauseWorkFlow], { mode: "debounce", delay: 1000 }); // 自动开始播放一轮 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]); // 更新 setSketchCount const updateSketchCount = useCallback((count: number) => { dispatch(setSketchCount(count)); }, [dispatch]); // 更新 setVideoCount const updateVideoCount = useCallback((count: number) => { dispatch(setVideoCount(count)); }, [dispatch]); // 替换原有的 setSketchCount 和 setVideoCount 调用 useEffect(() => { console.log('sketchCount 已更新:', sketchCount); currentStep !== '3' && setCurrentSketchIndex(sketchCount - 1); }, [sketchCount]); useEffect(() => { console.log('videoCount 已更新:', videoCount); setCurrentSketchIndex(videoCount - 1); }, [videoCount]); // 将 sketchCount 和 videoCount 放到 redux 中 每一次变化也要更新 // 添加手动播放控制 const handleManualPlay = useCallback(async () => { if (!isGeneratingSketch && taskSketch.length > 0) { await autoPlaySketch(); } }, [isGeneratingSketch, taskSketch.length, autoPlaySketch]); // 获取流式数据 const fetchStreamData = async () => { if (!episodeId || !needStreamData) return; try { const response = await getRunningStreamData({ project_id: episodeId }); if (!response.successful) { 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; console.log('---look-all_task_data', all_task_data); console.log('---look-taskData', taskData); 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'; const realSketchResultData = task.task_result.data.filter((item: any) => item.image_path); if (realSketchResultData.length >= 0) { // 正在生成草图中 替换 sketch 数据 const sketchList = []; for (const sketch of realSketchResultData) { sketchList.push({ url: sketch.image_path, script: sketch.sketch_name }); } taskData.sketch.data = sketchList; setTaskSketch(sketchList); setTaskScenes(sketchList); updateSketchCount(sketchList.length); setIsGeneratingSketch(true); 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); sketchCount = task.task_result.total_count; console.log('----------草图生成完成', sketchCount); loadingText = LOADING_TEXT_MAP.sketchComplete; taskData.status = '2'; } setTotalSketchCount(task.task_result.total_count); 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) { // 正在生成角色中 替换角色数据 const characterList = []; for (const character of task.task_result.data) { characterList.push({ name: character.character_name, url: character.image_path, sound: null, soundDescription: '', roleDescription: character.character_description }); } taskData.character.data = characterList; setRoles(characterList); loadingText = LOADING_TEXT_MAP.newCharacter(characterList.length, task.task_result.total_count); } if (task.task_status === 'COMPLETED') { console.log('----------角色生成完成,有几个分镜', sketchCount); // 角色生成完成 taskData.character.total_count = taskData.character.data.length; taskData.status = '3'; loadingText = LOADING_TEXT_MAP.getShotSketchStatus; } 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) { taskData.status = '1'; console.log('----------正在生成草图中 替换 sketch 数据', taskShotSketch.length, realShotResultData.length); // 正在生成草图中 替换 sketch 数据 const sketchList = []; for (const sketch of realShotResultData) { sketchList.push({ url: sketch.url, script: sketch.description }); } taskData.shot_sketch.data = sketchList; setTaskSketch(sketchList); setTaskShotSketch(sketchList); updateSketchCount(sketchList.length); setIsGeneratingSketch(true); 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); sketchCount = task.task_result.total_count; console.log('----------草图生成完成', sketchCount); loadingText = LOADING_TEXT_MAP.getVideoStatus; taskData.status = '3'; } setTotalSketchCount(task.task_result.total_count); 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) { console.log('----------正在生成视频中', realTaskResultData.length); // 正在生成视频中 替换视频数据 const videoList = []; for (const video of realTaskResultData) { // 每一项 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; setTaskVideos(videoList); updateVideoCount(videoList.length); setIsGeneratingVideo(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); taskData.status = '4'; // 暂时没有音频生成 直接跳过 taskData.status = '5'; loadingText = LOADING_TEXT_MAP.postProduction('generating rough cut video...'); } break; } // 粗剪 if (task.task_name === 'generate_final_simple_video') { if (task.task_result && task.task_result.video) { setFinal({ 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({ url: task.task_result.video, }) taskData.status = '6'; loadingText = LOADING_TEXT_MAP.complete; // 停止轮询 setNeedStreamData(false); } } } console.log('-----look-finalStep-----', taskData.status); // 设置步骤 setCurrentStep(taskData.status); setTaskObject(prev => { if (!prev) return null; return { ...prev, taskStatus: taskData.status }; }); setCurrentLoadingText(loadingText); } catch (error) { console.error('获取数据失败:', error); } }; // 轮询获取流式数据 useEffect(() => { let interval: NodeJS.Timeout; if (needStreamData) { interval = setInterval(fetchStreamData, 10000); fetchStreamData(); // 立即执行一次 } return () => { if (interval) { clearInterval(interval); } }; }, [needStreamData]); // 初始化数据 const initializeWorkflow = async () => { if (!episodeId) { setDataLoadError('缺少必要的参数'); setIsLoading(false); return; } try { setIsLoading(true); setCurrentLoadingText('loading project info...'); // 获取剧集详情 const response = await detailScriptEpisodeNew({ project_id: episodeId }); if (!response.successful) { throw new Error(response.message); } const { name, status, data, tags, mode, original_text } = response.data; setMode(mode); setOriginalText(original_text); setIsLoading(false); // 设置初始数据 setTaskObject({ taskStatus: '0', title: name || 'generating...', currentLoadingText: status === 'COMPLETED' ? LOADING_TEXT_MAP.complete : LOADING_TEXT_MAP.initializing, tags: tags || [] }); // 设置标题 if (!name) { // 如果没有标题,轮询获取 const titleResponse = await getScriptTitle({ project_id: episodeId }); console.log('titleResponse', titleResponse); if (titleResponse.successful) { setTaskObject((prev: TaskObject | null) => ({ ...(prev || {}), title: titleResponse.data.title, tags: titleResponse.data.tags || [] } as TaskObject)); } } if (status === 'COMPLETED') { loadingText = LOADING_TEXT_MAP.complete; taskData.status = '6'; } // 如果有已完成的数据,同步到状态 if (data) { if (data.sketch && data.sketch.data) { taskData.status = '1'; const realSketchResultData = data.sketch.data.filter((item: any) => item.image_path); const sketchList = []; for (const sketch of realSketchResultData) { sketchList.push({ url: sketch.image_path, script: sketch.sketch_name, }); } taskData.sketch.data = sketchList; taskData.sketch.total_count = data.sketch.total_count; setTaskSketch(sketchList); setTaskScenes(sketchList); updateSketchCount(sketchList.length); // 设置为最后一个草图 if (data.sketch.total_count > realSketchResultData.length) { setIsGeneratingSketch(true); loadingText = LOADING_TEXT_MAP.sketch(realSketchResultData.length, data.sketch.total_count); } else { taskData.status = '2'; if (!data.character || !data.character.data || !data.character.data.length) { loadingText = LOADING_TEXT_MAP.newCharacter(0, data.character.total_count); } } } if (data.character && data.character.data && data.character.data.length > 0) { const characterList = []; for (const character of data.character.data) { characterList.push({ name: character.character_name, url: character.image_path, sound: null, soundDescription: '', roleDescription: character.character_description }); } taskData.character.data = characterList; taskData.character.total_count = data.character.total_count; setRoles(characterList); if (data.character.total_count > data.character.data.length) { loadingText = LOADING_TEXT_MAP.newCharacter(data.character.data.length, data.character.total_count); } else { taskData.status = '3'; if (!data.video || !data.video.data || !data.video.data.length) { loadingText = LOADING_TEXT_MAP.getShotSketchStatus; } } } if (data.shot_sketch && data.shot_sketch.data) { const realShotResultData = data.shot_sketch.data.filter((item: any) => item.url); const sketchList = []; for (const sketch of realShotResultData) { sketchList.push({ url: sketch.url, script: sketch.description, }); } taskData.shot_sketch.data = sketchList; taskData.shot_sketch.total_count = data.shot_sketch.total_count; setTaskSketch(sketchList); setTaskShotSketch(sketchList); updateSketchCount(sketchList.length); // 设置为最后一个草图 if (data.shot_sketch.total_count > realShotResultData.length) { setIsGeneratingSketch(true); loadingText = LOADING_TEXT_MAP.shotSketch(realShotResultData.length, data.shot_sketch.total_count); } else { taskData.status = '3'; setIsGeneratingVideo(true); if (!data.character || !data.character.data || !data.character.data.length) { loadingText = LOADING_TEXT_MAP.getVideoStatus; } } } if (data.video.data) { const realDataVideoData = data.video.data.filter((item: any) => item.urls && item.urls.length > 0); 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.status = '5'; loadingText = LOADING_TEXT_MAP.postProduction('generating rough cut video...'); } } } // 粗剪 if (data.final_simple_video && data.final_simple_video.video) { setFinal({ url: data.final_simple_video.video }); taskData.status = '5.5'; loadingText = LOADING_TEXT_MAP.postProduction('generating fine-grained video clips...'); } if (data.final_video && data.final_video.video) { setFinal({ url: data.final_video.video }); taskData.status = '6'; loadingText = LOADING_TEXT_MAP.complete; } } // 设置步骤 setCurrentStep(taskData.status); setTaskObject(prev => { if (!prev) return null; return { ...prev, taskStatus: taskData.status }; }); console.log('---------loadingText', loadingText); setCurrentLoadingText(loadingText); // 设置是否需要获取流式数据 setNeedStreamData(status !== 'COMPLETED' && taskData.status !== '6'); } catch (error) { console.error('初始化失败:', error); setDataLoadError('加载失败,请重试'); setIsLoading(false); } }; // 回退到 指定状态 重新获取数据 const fallbackToStep = (step: string) => { setCurrentStep(step); setNeedStreamData(true); } // 重试加载数据 const retryLoadData = () => { setDataLoadError(null); // 重置所有状态 setTaskSketch([]); setTaskScenes([]); setTaskVideos([]); updateSketchCount(0); updateVideoCount(0); setRoles([]); setMusic([]); setFinal(null); setCurrentSketchIndex(0); setCurrentStep('0'); // 重新初始化 initializeWorkflow(); }; // 初始化 useEffect(() => { initializeWorkflow(); }, [episodeId]); return { taskObject, scriptData, taskSketch, taskScenes, taskShotSketch, taskVideos, sketchCount, isLoading, currentStep, currentSketchIndex, isGeneratingSketch, isGeneratingVideo, currentLoadingText, totalSketchCount, roles, music, final, dataLoadError, setCurrentSketchIndex, retryLoadData, handleManualPlay, isPauseWorkFlow, mode, setIsPauseWorkFlow, setAnyAttribute, applyScript, fallbackToStep, originalText }; }