'use client'; import { useState, useEffect, useCallback, useRef, useMemo } from 'react'; import { useSearchParams } from 'next/navigation'; import { detailScriptEpisodeNew, getScriptTitle, getRunningStreamData, pauseMovieProjectPlan, resumeMovieProjectPlan, getGenerateEditPlan, regenerateVideoNew } from '@/api/video_flow'; import { useScriptService } from "@/app/service/Interaction/ScriptService"; import { useUpdateEffect } from '@/app/hooks/useUpdateEffect'; import { LOADING_TEXT_MAP, TaskObject, Status, Stage } from '@/api/DTO/movieEdit'; import { AspectRatioValue } from '@/components/ChatInputBox/AspectRatioSelector'; import { useDeviceType } from '@/hooks/useDeviceType'; import { cutUrlTo, errorConfig } from '@/lib/env'; interface UseWorkflowDataProps { onEditPlanGenerated?: () => void; editingStatus?: string; onExportFailed?: () => void; } export function useWorkflowData({ onEditPlanGenerated, editingStatus, onExportFailed }: UseWorkflowDataProps = {}) { const searchParams = useSearchParams(); const episodeId = searchParams.get('episodeId') || ''; const from = searchParams.get('from') || ''; const token = localStorage.getItem('token') || ''; const useid = JSON.parse(localStorage.getItem("currentUser") || '{}').id || NaN; // H5进度提示事件桥接 const emitToastShow = (params: { title?: string; progress?: number }) => { if (typeof window !== 'undefined') { window.dispatchEvent(new CustomEvent('h5Toast:show', { detail: params })); } }; const emitToastUpdate = (params: { title?: string; progress?: number }) => { if (typeof window !== 'undefined') { window.dispatchEvent(new CustomEvent('h5Toast:update', { detail: params })); } }; const emitToastHide = () => { if (typeof window !== 'undefined') { window.dispatchEvent(new CustomEvent('h5Toast:hide')); } }; const { isMobile, isTablet, isDesktop } = useDeviceType(); const cutUrl = cutUrlTo; console.log('cutUrl', cutUrl); useEffect(() => { console.log("init-useWorkflowData"); return () => { console.log("unmount-useWorkflowData"); // 组件卸载时隐藏H5进度提示 // emitToastHide(); }; }, []); // 查看缓存中 是否已经 加载过 这个项目的 剪辑计划 let isLoadedRef = useRef(localStorage.getItem(`isLoaded_plan_${episodeId}`)); let tempTaskObject = useRef({ title: '', tags: [], currentStage: 'init' as Stage, status: 'IN_PROGRESS' as Status, roles: { data: [], total_count: -1 }, scenes: { data: [], total_count: -1 }, videos: { data: [], total_count: -1 }, final: { url: '', note: '' } }); let loadingText: any = useRef(LOADING_TEXT_MAP.getInfo); // 更新 taskObject 的类型 const [taskObject, setTaskObject] = useState(tempTaskObject.current); const [currentSketchIndex, setCurrentSketchIndex] = useState(0); const [currentLoadingText, setCurrentLoadingText] = useState(LOADING_TEXT_MAP.getInfo); const [dataLoadError, setDataLoadError] = useState(null); const [needStreamData, setNeedStreamData] = useState(false); const [isPauseWorkFlow, setIsPauseWorkFlow] = useState(false); const [canGoToCut, setCanGoToCut] = useState(false); const [isShowError, setIsShowError] = useState(false); const [isAnalyzing, setIsAnalyzing] = useState(false); const [isGenerateEditPlan, setIsGenerateEditPlan] = useState(false); const [retryCount, setRetryCount] = useState(0); const [isLoadingGenerateEditPlan, setIsLoadingGenerateEditPlan] = useState(false); const [state, setState] = useState({ mode: 'automatic' as 'automatic' | 'manual' | 'auto', aspectRatio: 'VIDEO_ASPECT_RATIO_LANDSCAPE' as AspectRatioValue, originalText: '', isLoading: true }); const { scriptBlocksMemo, // 渲染剧本数据 initializeFromProject, setAnyAttribute, applyScript } = useScriptService(); // 监听剧本加载完毕 const scriptData = useMemo(() => { console.log('scriptData', scriptBlocksMemo); return scriptBlocksMemo.length > 0 ? scriptBlocksMemo : null; }, [scriptBlocksMemo]); // 监听继续 请求更新数据 useUpdateEffect(() => { if (taskObject.status === 'COMPLETED' || taskObject.status === 'FAILED') { return; } if (isPauseWorkFlow) { pauseMovieProjectPlan({ project_id: episodeId }); } else { resumeMovieProjectPlan({ project_id: episodeId }); } }, [isPauseWorkFlow], { mode: "debounce", delay: 1000 }); // 自动开始播放一轮 const autoPlaySketch = useCallback((length: number) => { return new Promise((resolve) => { let currentIndex = 0; const interval = 2000; // 每个草图显示2秒 const playNext = () => { if (currentIndex < length) { console.log('自动播放设置索引:', currentIndex); setCurrentSketchIndex(currentIndex); currentIndex++; setTimeout(playNext, interval); } else { // 播放完成后重置到第一个 setTimeout(() => { console.log('自动播放完成,重置索引到0'); setCurrentSketchIndex(0); resolve(); }, 500); // 短暂延迟后重置 } }; // 开始播放 playNext(); }); }, []); useEffect(() => { if (['video', 'sketch'].includes(taskObject.currentStage)) { setCurrentSketchIndex(0); } }, [taskObject.currentStage]); const generateEditPlan = useCallback(async (retryCount: number) => { if (isLoadedRef.current) { return; } // 显示生成剪辑计划进度提示 // !emitToastShow({ title: isMobile ? 'Preparing for editing...' : `Generating intelligent editing plan... ${retryCount ? 'Retry Time: ' + retryCount : ''}`, progress: 0 }); // 平滑推进到 80%,后续阶段接管 const start = Date.now(); const duration = 3 * 60 * 1000; // 3分钟推进到 80% let interval: NodeJS.Timeout | null = null; const stop = () => { if (interval) { clearInterval(interval); interval = null; } }; interval = setInterval(() => { const elapsed = Date.now() - start; const pct = Math.min(80, Math.floor((elapsed / duration) * 80)); // emitToastUpdate({ progress: pct }); if (pct >= 80) stop(); }, 300); // 先停止轮询 await new Promise(resolve => { setNeedStreamData(false); resolve(true); }); setIsLoadingGenerateEditPlan(true); try { const response = await getGenerateEditPlan({ project_id: episodeId }); if (!response.data.editing_plan) { throw new Error(response.message); } console.error('生成剪辑计划成功'); setIsGenerateEditPlan(true); isLoadedRef.current = 'true'; setNeedStreamData(true); // 触发回调,通知父组件计划生成完成 console.log('📞 calling onEditPlanGenerated callback'); onEditPlanGenerated?.(); setIsLoadingGenerateEditPlan(false); stop(); } catch (error) { console.error('生成剪辑计划失败:', error); setNeedStreamData(true); setIsGenerateEditPlan(false); setTimeout(() => { // emitToastHide(); setIsLoadingGenerateEditPlan(false); }, 8000); stop(); } }, [episodeId, onEditPlanGenerated]); const openEditPlan = useCallback(async () => { window.open(`${cutUrl}/ai-editor/${episodeId}?token=${token}&user_id=${useid}`, '_target'); }, [episodeId, cutUrl, token, useid]); // useEffect(() => { // // 主动触发剪辑 // if (canGoToCut && taskObject.currentStage === 'video' && !isShowError) { // generateEditPlan(retryCount - 1); // } // }, [canGoToCut, taskObject.currentStage, isShowError, generateEditPlan, retryCount]); useEffect(() => { // 加载剪辑计划结束 并且 失败了 重试 if (!isLoadingGenerateEditPlan && !isGenerateEditPlan) { setRetryCount((r) => r + 1); } }, [isLoadingGenerateEditPlan, isGenerateEditPlan]); useEffect(() => { if (isShowError) { window.msg.error('Too many failed storyboards, unable to execute automatic editing.', 8000); setCurrentLoadingText(LOADING_TEXT_MAP.toManyFailed); // 停止轮询 setNeedStreamData(false); // emitToastHide(); } if (editingStatus === 'error') { window.msg.error('Editing failed, Please click the scissors button to go to the intelligent editing platform.', 8000); setCurrentLoadingText(LOADING_TEXT_MAP.editingError); // 停止轮询 setNeedStreamData(false); // emitToastHide(); } }, [isShowError, editingStatus]); useUpdateEffect(() => { console.log('-----look-taskObject_find_changed-----', taskObject); if (taskObject.currentStage === 'script') { if (scriptBlocksMemo.length > 0) { console.log('应用剧本'); // 自动模式下 应用剧本;手动模式 需要点击 下一步 触发 // 确保仅自动触发一次 // 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 === '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.getVideoStatus; } } if (taskObject.currentStage === 'video') { const realTaskResultData = taskObject.videos.data.filter((item: any) => item.video_status !== 0); if (taskObject.videos.total_count > realTaskResultData.length) { loadingText.current = LOADING_TEXT_MAP.video(realTaskResultData.length, taskObject.videos.total_count); } else { loadingText.current = LOADING_TEXT_MAP.postProduction('AI-powered video editing in progress…'); } } if (taskObject.currentStage === 'final_video' && taskObject.status !== 'COMPLETED') { loadingText.current = LOADING_TEXT_MAP.postProduction('generating fine-grained video clips...'); } if (taskObject.status === 'COMPLETED') { loadingText.current = LOADING_TEXT_MAP.complete; } setCurrentLoadingText(loadingText.current); }, [scriptBlocksMemo, taskObject.currentStage, taskObject.scenes.data, taskObject.roles.data, taskObject.videos.data, taskObject.status], {mode: 'none'}); // 将 sketchCount 和 videoCount 放到 redux 中 每一次变化也要更新 // 添加手动播放控制 const handleManualPlay = useCallback(async () => { if (taskObject.currentStage === 'scene' && taskObject.scenes.data.length > 0) { await autoPlaySketch(taskObject.scenes.data.length); } }, [taskObject.currentStage, taskObject.scenes.data, autoPlaySketch]); // 获取流式数据 const fetchStreamData = useCallback(async () => { if (!episodeId || !needStreamData) return; try { const response = await getRunningStreamData({ project_id: episodeId }); if (!response.successful) { throw new Error(response.message); } const all_task_data = response.data; const { current: taskCurrent } = tempTaskObject; let combinerVideoUrl = ''; console.log('---look-all_task_data', all_task_data); console.log('---look-tempTaskObject', taskCurrent); // 收集所有需要更新的状态 let stateUpdates = JSON.stringify(taskCurrent); for (const task of all_task_data) { // 如果有已完成的数据,同步到状态 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') { realCharacterResultData = taskCurrent.roles.data.filter((item: any) => item.status !== 0); } taskCurrent.roles.total_count = task.task_result.total_count; if (task.task_status !== 'COMPLETED' || taskCurrent.roles.total_count !== realCharacterResultData.length) { taskCurrent.currentStage = 'character'; // 正在生成角色中 替换角色数据 const characterList = []; for (const character of task.task_result.data) { characterList.push({ name: character.character_name, url: character.image_path, status: character.image_path ? 1 : (task.task_status === 'COMPLETED' ? 2 : 0), type: 'role' }); } taskCurrent.roles.data = characterList; if (task.task_status === 'COMPLETED') { // 角色生成完成 } break; } } 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), type: 'scene' }); } taskCurrent.scenes.data = sketchList; if (task.task_status === 'COMPLETED') { // 草图生成完成 } break; } } if (task.task_name === 'generate_videos' && task.task_result && task.task_result.data) { let realTaskResultData = task.task_result.data.filter((item: any) => (item.urls || (item.video_status !== 0 && item.video_status !== undefined))); taskCurrent.videos.total_count = task.task_result.total_count; if (task.task_status === 'COMPLETED') { realTaskResultData = taskCurrent.videos.data.filter((item: any) => item.video_status !== 0); } if (task.task_status !== 'COMPLETED' || taskCurrent.videos.total_count !== realTaskResultData.length) { taskCurrent.currentStage = 'video'; // 正在生成视频中 替换视频数据 const videoList = []; let videoUrls: string[] = []; let video_status = 0; for (const video of task.task_result.data) { videoUrls = video.urls ? video.urls.filter((url: null | string) => url !== null) : []; // 适配旧数据 video_status = video.video_status === undefined ? (videoUrls.length > 0 ? 1 : 0) : video.video_status; // 每一项 video 有多个视频 先默认取第一个 videoList.push({ urls: videoUrls, video_id: video.video_id, video_status: video_status, // 0 生成中 1 生成完成 2 生成失败 type: 'video' }); } taskCurrent.videos.data = videoList; console.log('----------正在生成视频中', realTaskResultData.length); break; } if (task.task_status === 'COMPLETED') { console.log('----------视频生成完成'); // 视频生成完成 // 暂时没有音频生成 直接跳过 // 视频分析 const error_totle = taskCurrent.videos.data.filter((item: any) => item.video_status === 2).length; const total_count = taskCurrent.videos.data.length; let analyze_video_completed_count = all_task_data.filter((item: any) => item.task_name === 'generate_analyze_video' && item.task_status !== 'INIT' && item.task_status !== 'RUNNING').length; let analyze_video_total_count = all_task_data.filter((item: any) => item.task_name === 'generate_analyze_video').length; // 检查是否需要开始显示视频分析进度 // 只在第一次检测到视频分析任务时显示通知 if (analyze_video_total_count > 0 && !isAnalyzing && analyze_video_completed_count !== analyze_video_total_count) { setIsAnalyzing(true); // 显示准备剪辑计划的提示 // emitToastShow({ title: isMobile ? 'Preparing for editing...' : 'Preparing intelligent editing plan...', progress: 0 }); } if (analyze_video_total_count && analyze_video_completed_count === analyze_video_total_count) { // 视频分析完成 if(error_totle !== total_count) { setCanGoToCut(true); // 重置进度条,显示生成剪辑计划进度 setIsAnalyzing(false); // 不主动隐藏,交由后续阶段覆盖标题与进度 } else { setIsShowError(true); setIsAnalyzing(false); // emitToastHide(); } } } } // 合成视频 if (task.task_name === 'combiner_videos') { if (task.task_status === 'COMPLETED') { combinerVideoUrl = task.task_result.video_url; } if (task.task_status === 'FAILED' || task.task_status === 'ERROR') { taskCurrent.status = 'FAILED'; // 触发导出失败回调 if (onExportFailed) { onExportFailed(); } // 停止轮询 setNeedStreamData(false); } } // 水印视频 if (task.task_name === 'watermark_videos') { if (task.task_status === 'COMPLETED') { taskCurrent.currentStage = 'final_video'; taskCurrent.final.url = task.task_result.video_url; taskCurrent.final.note = 'watermark'; taskCurrent.status = 'COMPLETED'; // 停止轮询 setNeedStreamData(false); } if (task.task_status === 'FAILED' || task.task_status === 'ERROR') { // 使用合成视频地址 taskCurrent.currentStage = 'final_video'; taskCurrent.final.url = combinerVideoUrl; taskCurrent.final.note = 'combiner'; taskCurrent.status = 'COMPLETED'; // 停止轮询 setNeedStreamData(false); } } // 最终剪辑 if (task.task_name === 'generate_final_video') { if (task.task_result && task.task_result.video) { taskCurrent.currentStage = 'final_video'; taskCurrent.final.url = task.task_result.video; taskCurrent.final.note = 'final'; taskCurrent.status = 'COMPLETED'; // 停止轮询 setNeedStreamData(false); } } } console.log('-----look-tempTaskObject-----', loadingText.current); // 设置最终的状态更新 setCurrentLoadingText(loadingText.current); if (JSON.stringify(taskCurrent) !== stateUpdates) { console.log('-----look-tempTaskObject-changed-----', taskCurrent); // 强制更新,使用新的对象引用确保触发更新 setTaskObject(prev => { const newState = JSON.parse(JSON.stringify({...prev, ...taskCurrent})); return newState; }); } } catch (error) { console.error('获取数据失败:', error); } }, [episodeId, needStreamData, onExportFailed, errorConfig, isAnalyzing]); // 轮询获取流式数据 useUpdateEffect(() => { let interval: NodeJS.Timeout; if (needStreamData) { interval = setInterval(fetchStreamData, 10000); fetchStreamData(); // 立即执行一次 } return () => { if (interval) { clearInterval(interval); } }; }, [needStreamData, fetchStreamData], {mode: 'none'}); // 初始化数据 const initializeWorkflow = useCallback(async () => { if (!episodeId) { setDataLoadError('缺少必要的参数'); return; } try { setState({ mode: 'automatic' as 'automatic' | 'manual' | 'auto', aspectRatio: 'VIDEO_ASPECT_RATIO_LANDSCAPE' as AspectRatioValue, originalText: '', isLoading: true }); setCurrentLoadingText(LOADING_TEXT_MAP.getInfo); // 获取剧集详情 const response = await detailScriptEpisodeNew({ project_id: episodeId }); if (!response.successful) { throw new Error(response.message); } const { status, data, tags, mode, original_text, aspect_ratio, name, final_simple_video, final_video } = response.data; const { current: taskCurrent } = tempTaskObject; taskCurrent.title = name || 'generating...'; taskCurrent.tags = tags || []; taskCurrent.status = status as Status; taskCurrent.currentStage = 'script'; // 设置标题 if (!name) { setCurrentLoadingText(LOADING_TEXT_MAP.initializing); // 如果没有标题,轮询获取 const titleResponse = await getScriptTitle({ project_id: episodeId }); console.log('titleResponse', titleResponse); if (titleResponse.successful) { taskCurrent.title = titleResponse.data.name; taskCurrent.tags = titleResponse.data.description.tags || []; } } if (status === 'COMPLETED') { loadingText.current = LOADING_TEXT_MAP.complete; taskCurrent.currentStage = 'final_video'; setCanGoToCut(true); } // 如果有已完成的数据,同步到状态 if (data) { if (data.character && data.character.data && data.character.data.length > 0) { taskCurrent.currentStage = 'character'; const characterList = []; for (const character of data.character.data) { characterList.push({ name: character.character_name, url: character.image_path, status: character.image_path ? 1 : (data.character.task_status === 'COMPLETED' ? 2 : 0), type: 'role' }); } taskCurrent.roles.data = characterList; taskCurrent.roles.total_count = data.character.total_count; if (data.character.total_count > data.character.data.length) { // 角色生成中 } else { // 角色生成完成 } } 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), type: 'scene' }); } taskCurrent.scenes.data = sketchList; taskCurrent.scenes.total_count = data.sketch.total_count; // 设置为最后一个草图 if (data.sketch.total_count > realSketchResultData.length) { // 场景生成中 } else { // 场景生成完成 } } if (data.video.data) { const realDataVideoData = data.video.data.filter((item: any) => (item.urls || (item.video_status !== 0 && item.video_status !== undefined))); taskCurrent.currentStage = 'video'; taskCurrent.videos.total_count = data.video.total_count; const videoList = []; let videoUrls: string[] = []; console.log('----------data.video.data', data.video.data); for (const video of data.video.data) { videoUrls = video.urls ? video.urls.filter((url: null | string) => url !== null) : []; console.log('----------videoUrls', videoUrls); let video_status = video.video_status === undefined ? (videoUrls.length > 0 ? 1 : 0) : video.video_status; // 每一项 video 有多个视频 默认取存在的项 videoList.push({ urls: videoUrls, video_id: video.video_id, video_status: video_status, // 0 生成中 1 生成完成 2 生成失败 type: 'video' }); } taskCurrent.videos.data = videoList; // 如果在视频步骤,设置为最后一个视频 if (data.video.total_count > realDataVideoData.length) { // 视频生成中 } else { // 视频生成完成 // 暂时没有音频生成 直接跳过 } } // 粗剪 if (data.final_simple_video && data.final_simple_video.video) { taskCurrent.currentStage = 'final_video'; taskCurrent.final.url = data.final_simple_video.video; taskCurrent.final.note = 'simple'; taskCurrent.status = 'COMPLETED'; } if (data.final_video && data.final_video.video) { taskCurrent.currentStage = 'final_video'; taskCurrent.final.url = data.final_video.video; taskCurrent.final.note = 'final'; taskCurrent.status = 'COMPLETED'; } } // 粗剪 if (final_simple_video) { taskCurrent.currentStage = 'final_video'; taskCurrent.final.url = final_simple_video; taskCurrent.final.note = 'simple'; taskCurrent.status = 'COMPLETED'; } if (final_video) { taskCurrent.currentStage = 'final_video'; taskCurrent.final.url = final_video; taskCurrent.final.note = 'final'; taskCurrent.status = 'COMPLETED'; } console.log('---look-taskData', taskCurrent); if (taskCurrent.currentStage === 'script') { console.log('开始初始化剧本', original_text,episodeId); // TODO 为什么一开始没项目id original_text && initializeFromProject(episodeId, original_text).then(() => { }); } setState({ mode: mode as 'automatic' | 'manual' | 'auto', aspectRatio: aspect_ratio as AspectRatioValue, originalText: original_text, isLoading: false }); // 设置步骤 setTaskObject(prev => { const newState = JSON.parse(JSON.stringify({...prev, ...taskCurrent})); return newState; }); // 设置是否需要获取流式数据 // setNeedStreamData(taskCurrent.status !== 'COMPLETED'); setNeedStreamData(true); } catch (error) { console.error('初始化失败:', error); setDataLoadError('加载失败,请重试'); // 设置是否需要获取流式数据 setNeedStreamData(true); setState({ mode: 'automatic' as 'automatic' | 'manual' | 'auto', aspectRatio: 'VIDEO_ASPECT_RATIO_LANDSCAPE' as AspectRatioValue, originalText: '', isLoading: false }); } }, [episodeId]); // 重试生成视频 const handleRetryVideo = async (video_id: string) => { try { // 先停止轮询 await new Promise(resolve => { setNeedStreamData(false); resolve(true); }); // 重置视频状态为生成中 await new Promise(resolve => { setTaskObject(prev => { const newState = JSON.parse(JSON.stringify(prev)); const videoIndex = newState.videos.data.findIndex((v: any) => v.video_id === video_id); if (videoIndex !== -1) { newState.videos.data[videoIndex].video_status = 0; } return newState; }); resolve(true); }); // 调用重新生成接口 await regenerateVideoNew({ project_id: episodeId, video_id: video_id }); // 重新开启轮询 setNeedStreamData(true); } catch (error) { console.error('重试生成视频失败:', error); // 发生错误时也要恢复轮询状态 setNeedStreamData(true); } }; // 回退到 指定状态 重新获取数据 const fallbackToStep = (step: string) => { console.log('fallbackToStep', step); setNeedStreamData(true); // tempTaskObject.current.currentStage = step as Stage; // loadingText = LOADING_TEXT_MAP.initializing; } // 重试加载数据 const retryLoadData = () => { setDataLoadError(null); setCurrentSketchIndex(0); // 重新初始化 initializeWorkflow(); }; // 初始化 useEffect(() => { initializeWorkflow(); }, [initializeWorkflow]); return { taskObject, scriptData, isLoading: state.isLoading, currentSketchIndex, currentLoadingText, dataLoadError, setCurrentSketchIndex, retryLoadData, handleManualPlay, isPauseWorkFlow, mode: state.mode, setIsPauseWorkFlow, setAnyAttribute, applyScript, fallbackToStep, originalText: state.originalText, showGotoCutButton: (canGoToCut && (isGenerateEditPlan || taskObject.currentStage === 'final_video') || isShowError) ? true : false, generateEditPlan: openEditPlan, handleRetryVideo, isShowAutoEditing: canGoToCut && taskObject.currentStage !== 'final_video' && isGenerateEditPlan && !isShowError ? true : false, aspectRatio: state.aspectRatio }; }