From 87f171e683cfb7b08c2dda57f5b2fc691d542d2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8C=97=E6=9E=B3?= <7854742+wang_rumeng@user.noreply.gitee.com> Date: Fri, 10 Oct 2025 20:18:47 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B0=B4=E5=8D=B0=E8=A7=86=E9=A2=91=E5=A4=B1?= =?UTF-8?q?=E8=B4=A5=E5=86=8D=E4=BD=BF=E7=94=A8=E5=90=88=E6=88=90=E7=9A=84?= =?UTF-8?q?=E8=A7=86=E9=A2=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/pages/work-flow.tsx | 355 +----------------- components/pages/work-flow/H5MediaViewer.tsx | 6 - components/pages/work-flow/H5ProgressBar.tsx | 28 +- components/pages/work-flow/task-info.tsx | 39 +- components/pages/work-flow/thumbnail-grid.tsx | 1 - .../pages/work-flow/use-workflow-data.tsx | 191 +--------- 6 files changed, 44 insertions(+), 576 deletions(-) diff --git a/components/pages/work-flow.tsx b/components/pages/work-flow.tsx index 972b034..8bf7ab5 100644 --- a/components/pages/work-flow.tsx +++ b/components/pages/work-flow.tsx @@ -17,30 +17,12 @@ import { SaveEditUseCase } from "@/app/service/usecase/SaveEditUseCase"; import { useSearchParams } from "next/navigation"; import SmartChatBox from "@/components/SmartChatBox/SmartChatBox"; import { Drawer, Tooltip, notification } from 'antd'; -import { exportVideoWithRetry } from '@/utils/export-service'; import { EditPoint as EditPointType } from './work-flow/video-edit/types'; import { useDeviceType } from '@/hooks/useDeviceType'; import { H5ProgressToastProvider, useH5ProgressToast } from '@/components/ui/h5-progress-toast'; const WorkFlow = React.memo(function WorkFlow() { const { isMobile, isTablet, isDesktop } = useDeviceType(); - // 通过全局事件桥接 H5ProgressToast(Provider 在本组件 JSX 中,逻辑层无法直接使用 hook) - const emitToastShow = useCallback((params: { title?: string; progress?: number }) => { - window.dispatchEvent(new CustomEvent('h5Toast:show', { detail: params })); - }, []); - const emitToastUpdate = useCallback((params: { title?: string; progress?: number }) => { - window.dispatchEvent(new CustomEvent('h5Toast:update', { detail: params })); - }, []); - const emitToastHide = useCallback(() => { - window.dispatchEvent(new CustomEvent('h5Toast:hide')); - }, []); - useEffect(() => { - console.log("init-WorkFlow"); - return () => { - console.log("unmount-WorkFlow"); - // 不在卸载时强制隐藏,避免严格模式下二次卸载导致刚显示就被关闭 - }; - }, [emitToastHide]); const containerRef = useRef(null); const [isEditModalOpen, setIsEditModalOpen] = React.useState(false); const [activeEditTab, setActiveEditTab] = React.useState('1'); @@ -53,22 +35,6 @@ const WorkFlow = React.memo(function WorkFlow() { const [selectedView, setSelectedView] = React.useState<'final' | 'video' | null>(null); const [aiEditingResult, setAiEditingResult] = React.useState(null); - // const aiEditingButtonRef = useRef<{ handleAIEditing: () => Promise }>(null); - const [editingStatus, setEditingStatus] = React.useState<'initial' | 'idle' | 'success' | 'error'>('initial'); - // const [iframeAiEditingKey, setIframeAiEditingKey] = React.useState(`iframe-ai-editing-${Date.now()}`); - const [isEditingInProgress, setIsEditingInProgress] = React.useState(false); - const isEditingInProgressRef = useRef(false); - // 导出进度状态 - const [exportProgress, setExportProgress] = React.useState<{ - status: 'processing' | 'completed' | 'failed'; - percentage: number; - message: string; - stage?: string; - taskId?: string; - } | null>(null); - const editingTimeoutRef = useRef(null); - const editingProgressIntervalRef = useRef(null); - const editingProgressStartRef = useRef(0); const searchParams = useSearchParams(); const episodeId = searchParams.get('episodeId') || ''; @@ -76,134 +42,7 @@ const WorkFlow = React.memo(function WorkFlow() { const userId = JSON.parse(localStorage.getItem("currentUser") || '{}').id || NaN; SaveEditUseCase.setProjectId(episodeId); - const [isHandleEdit, setIsHandleEdit] = React.useState(false); - // 使用 ref 存储 handleTestExport 避免循环依赖 - const handleTestExportRef = useRef<(() => Promise) | null>(null); - // 导出进度回调处理 - const handleExportProgress = useCallback((progressData: { - status: 'processing' | 'completed' | 'failed'; - percentage: number; - message: string; - stage?: string; - taskId?: string; - }) => { - console.log('📊 导出进度更新:', progressData); - setExportProgress(progressData); - - // 根据状态显示不同的通知 - 已注释 - /* - if (progressData.status === 'processing') { - notification.info({ - message: '导出进度', - description: `${progressData.message} (${progressData.percentage}%)`, - placement: 'topRight', - duration: 2, - key: 'export-progress' - }); - } else if (progressData.status === 'completed') { - notification.success({ - message: '导出成功', - description: progressData.message, - placement: 'topRight', - duration: 5, - key: 'export-progress' - }); - } else if (progressData.status === 'failed') { - notification.error({ - message: '导出失败', - description: progressData.message, - placement: 'topRight', - duration: 8, - key: 'export-progress' - }); - } - */ - }, []); - // 处理编辑计划生成完成的回调 - const handleEditPlanGenerated = useCallback(() => { - console.log('✨ 编辑计划生成完成,开始AI剪辑'); - setIsHandleEdit(true); - setEditingStatus('idle'); - // setIsEditingInProgress(true); // 已移除该状态变量 - isEditingInProgressRef.current = true; - - // 改为调用测试剪辑计划导出按钮方法 - // aiEditingButtonRef.current?.handleAIEditing(); - // 使用 ref 调用避免循环依赖 - setTimeout(() => { - handleTestExportRef.current?.(); - }, 0); - - const title = isMobile ? 'editing...' : 'Performing intelligent editing...'; - - // 显示进度提示并启动超时定时器 - // emitToastShow({ title: title, progress: 0 }); - // 启动自动推进到 90% 的进度(8分钟) - if (editingProgressIntervalRef.current) clearInterval(editingProgressIntervalRef.current); - editingProgressStartRef.current = Date.now(); - const totalMs = 8 * 60 * 1000; - // editingProgressIntervalRef.current = setInterval(() => { - // const elapsed = Date.now() - editingProgressStartRef.current; - // const pct = Math.min(90, Math.max(0, Math.floor((elapsed / totalMs) * 90))); - // emitToastUpdate({ progress: pct }); - // }, 250); - if (editingTimeoutRef.current) clearTimeout(editingTimeoutRef.current); - editingTimeoutRef.current = setTimeout(() => { - console.log('❌ Editing timeout - retrying...'); - localStorage.removeItem(`isLoaded_plan_${episodeId}`); - if (editingProgressIntervalRef.current) { - clearInterval(editingProgressIntervalRef.current); - editingProgressIntervalRef.current = null; - } - // emitToastHide(); - setTimeout(() => { - // emitToastShow({ title: 'Retry intelligent editing...', progress: 0 }); - // 重试阶段自动推进(5分钟到 90%) - if (editingProgressIntervalRef.current) clearInterval(editingProgressIntervalRef.current); - editingProgressStartRef.current = Date.now(); - const retryTotalMs = 5 * 60 * 1000; - editingProgressIntervalRef.current = setInterval(() => { - const elapsed = Date.now() - editingProgressStartRef.current; - const pct = Math.min(90, Math.max(0, Math.floor((elapsed / retryTotalMs) * 90))); - // emitToastUpdate({ progress: pct }); - }, 250); - if (editingTimeoutRef.current) clearTimeout(editingTimeoutRef.current); - editingTimeoutRef.current = setTimeout(() => { - console.log('Editing retry failed'); - localStorage.removeItem(`isLoaded_plan_${episodeId}`); - setTimeout(() => { - setEditingStatus('error'); - setIsEditingInProgress(false); - isEditingInProgressRef.current = false; - if (editingProgressIntervalRef.current) { - clearInterval(editingProgressIntervalRef.current); - editingProgressIntervalRef.current = null; - } - // emitToastHide(); - }, 5000); - }, 5 * 60 * 1000); - }, 200); - }, 8 * 60 * 1000); - }, [episodeId, emitToastHide, emitToastShow, emitToastUpdate]); // 移除 isEditingInProgress 依赖 - - /** 处理导出失败 */ - const handleExportFailed = useCallback(() => { - console.log('Export failed, setting error status'); - setEditingStatus('error'); - // setIsEditingInProgress(false); // 已移除该状态变量 - isEditingInProgressRef.current = false; - if (editingTimeoutRef.current) { - clearTimeout(editingTimeoutRef.current); - editingTimeoutRef.current = null; - } - if (editingProgressIntervalRef.current) { - clearInterval(editingProgressIntervalRef.current); - editingProgressIntervalRef.current = null; - } - // emitToastHide(); - }, [emitToastHide]); - // 使用自定义 hooks 管理状态 const { taskObject, @@ -222,12 +61,8 @@ const WorkFlow = React.memo(function WorkFlow() { showGotoCutButton, generateEditPlan, handleRetryVideo, - isShowAutoEditing, aspectRatio } = useWorkflowData({ - onEditPlanGenerated: handleEditPlanGenerated, - editingStatus: editingStatus, - onExportFailed: handleExportFailed }); const { @@ -251,63 +86,11 @@ const WorkFlow = React.memo(function WorkFlow() { } }, [taskObject?.final?.url]); - // 监听粗剪是否完成 - useEffect(() => { - console.log('🎬 final video useEffect triggered:', { - finalUrl: taskObject.final.url, - isHandleEdit - }); - - if (taskObject.final.url && isHandleEdit) { - console.log('🎉 显示编辑完成通知'); - // 完成:推进到 100 并清理超时计时器 - if (editingTimeoutRef.current) { - clearTimeout(editingTimeoutRef.current); - editingTimeoutRef.current = null; - } - if (editingProgressIntervalRef.current) { - clearInterval(editingProgressIntervalRef.current); - editingProgressIntervalRef.current = null; - } - // emitToastUpdate({ title: 'Editing successful', progress: 100 }); - console.log('Editing successful'); - localStorage.setItem(`isLoaded_plan_${episodeId}`, 'true'); - setEditingStatus('success'); - setIsEditingInProgress(false); - isEditingInProgressRef.current = false; - // setTimeout(() => { - // emitToastHide(); - // }, 3000); - } - }, [taskObject.final, isHandleEdit, episodeId, emitToastHide, emitToastUpdate]); - const handleEditModalOpen = useCallback((tab: string) => { setActiveEditTab(tab); setIsEditModalOpen(true); }, []); - // AI剪辑回调函数 - const handleAIEditingComplete = useCallback((finalVideoUrl: string) => { - console.log('🎉 AI剪辑完成,最终视频URL:', finalVideoUrl); - - // 更新任务对象的最终视频状态 - setAnyAttribute('final', { - url: finalVideoUrl, - note: 'ai_edited' - }); - - // 切换到最终视频阶段 - setAnyAttribute('currentStage', 'final_video'); - - // setAiEditingInProgress(false); // 已移除该状态变量 - }, [setAnyAttribute]); - - const handleAIEditingError = useCallback((error: string) => { - console.error('❌ AI剪辑失败:', error); - // 这里可以显示错误提示 - // setAiEditingInProgress(false); // 已移除该状态变量 - }, []); - // 视频编辑描述提交处理函数 const handleVideoEditDescriptionSubmit = useCallback((editPoint: EditPointType, description: string) => { console.log('🎬 视频编辑描述提交:', { editPoint, description }); @@ -339,77 +122,6 @@ Please process this video editing request.`; }); }, [currentSketchIndex, isSmartChatBoxOpen]); - // 测试导出接口的处理函数(使用封装的导出服务) - const handleTestExport = useCallback(async () => { - console.log('🧪 开始测试导出接口...'); - console.log('📊 当前taskObject状态:', { - currentStage: taskObject.currentStage, - videosCount: taskObject.videos?.data?.length || 0, - completedVideos: taskObject.videos?.data?.filter(v => v.video_status === 1).length || 0 - }); - - try { - // 使用封装的导出服务,传递进度回调 - const result = await exportVideoWithRetry(episodeId, taskObject, handleExportProgress); - console.log('🎉 导出服务完成,结果:', result); - return result; - } catch (error) { - console.error('❌ 导出服务失败:', error); - throw error; - } - }, [episodeId, taskObject, handleExportProgress]); - - // 将 handleTestExport 赋值给 ref - React.useEffect(() => { - handleTestExportRef.current = handleTestExport; - }, [handleTestExport]); - - - - - - - // iframe智能剪辑回调函数 - 已注释 - /* - const handleIframeAIEditingComplete = useCallback((result: any) => { - console.log('🎉 iframe AI剪辑完成,结果:', result); - - // 保存剪辑结果 - setAiEditingResult(result); - - // 更新任务对象的最终视频状态 - setAnyAttribute('final', { - url: result.videoUrl, - note: 'ai_edited_iframe' - }); - - // 切换到最终视频阶段 - setAnyAttribute('currentStage', 'final_video'); - - // setAiEditingInProgress(false); // 已移除该状态变量 - }, [setAnyAttribute]); - */ - - /* - const handleIframeAIEditingError = useCallback((error: string) => { - console.error('❌ iframe AI剪辑失败:', error); - // setAiEditingInProgress(false); // 已移除该状态变量 - }, []); - */ - - /* - const handleIframeAIEditingProgress = useCallback((progress: number, message: string) => { - console.log(`📊 AI剪辑进度: ${progress}% - ${message}`); - setAiEditingInProgress(true); - // 收到显式进度时停止自动推进,防止倒退 - if (editingProgressIntervalRef.current) { - clearInterval(editingProgressIntervalRef.current); - editingProgressIntervalRef.current = null; - } - emitToastUpdate({ title: message, progress }); - }, [emitToastUpdate]); - */ - return ( @@ -441,7 +153,6 @@ Please process this video editing request.`; currentLoadingText={currentLoadingText} roles={taskObject.roles.data} isPauseWorkFlow={isPauseWorkFlow} - showGotoCutButton={showGotoCutButton || editingStatus !== 'idle'} onGotoCut={generateEditPlan} setIsPauseWorkFlow={setIsPauseWorkFlow} /> @@ -478,7 +189,7 @@ Please process this video editing request.`; setPreviewVideoUrl(url); setPreviewVideoId(id); }} - showGotoCutButton={showGotoCutButton || editingStatus !== 'idle'} + showGotoCutButton={showGotoCutButton} onGotoCut={generateEditPlan} isSmartChatBoxOpen={isSmartChatBoxOpen} onRetryVideo={(video_id) => handleRetryVideo(video_id)} @@ -535,8 +246,6 @@ Please process this video editing request.`; setPreviewVideoUrl(url); setPreviewVideoId(id); }} - showGotoCutButton={showGotoCutButton || editingStatus !== 'idle'} - onGotoCut={generateEditPlan} isSmartChatBoxOpen={isSmartChatBoxOpen} onRetryVideo={(video_id) => handleRetryVideo(video_id)} onSelectView={(view) => setSelectedView(view)} @@ -585,68 +294,6 @@ Please process this video editing request.`; - {/* AI剪辑按钮 - 已注释,不加载iframe */} - {/* - { - isShowAutoEditing && ( -
- - - -
- ) - } - */} - - {/* 导出进度显示 - 已注释 */} - {/* - {exportProgress && exportProgress.status === 'processing' && ( -
-
-
- 导出进度: {exportProgress.percentage}% -
-
-
-
-
- {exportProgress.message} - {exportProgress.stage && ` (${exportProgress.stage})`} -
-
-
- )} - */} - - {/* 测试导出接口按钮 - 隐藏显示(仍可通过逻辑调用) */} -
- - - -
- {/* 智能对话按钮 */}
void; /** 设置聊天预览视频 */ setVideoPreview?: (url: string, id: string) => void; - /** 显示跳转至剪辑平台按钮 */ - showGotoCutButton?: boolean; - /** 跳转至剪辑平台 */ - onGotoCut?: () => void; /** 智能对话是否打开(H5可忽略布局调整,仅占位) */ isSmartChatBoxOpen?: boolean; /** 失败重试生成视频 */ @@ -210,8 +206,6 @@ export function H5MediaViewer({ setCurrentSketchIndex, onOpenChat, setVideoPreview, - showGotoCutButton, - onGotoCut, isSmartChatBoxOpen, onRetryVideo, onSelectView, diff --git a/components/pages/work-flow/H5ProgressBar.tsx b/components/pages/work-flow/H5ProgressBar.tsx index dffc001..7afd790 100644 --- a/components/pages/work-flow/H5ProgressBar.tsx +++ b/components/pages/work-flow/H5ProgressBar.tsx @@ -27,8 +27,16 @@ const H5ProgressBar: React.FC = ({ currentLoadingText, className }) => { + /** Check if task has failed */ + const isFailed = taskObject.status === 'FAILED' + /** Calculate current stage based on taskObject state */ const currentStage = useMemo(() => { + /** If task failed, show at final stage */ + if (isFailed) { + return 3 /** Final stage to show failure */ + } + /** Check if roles & scenes are completed */ const rolesCompleted = taskObject.roles?.total_count > 0 && @@ -71,6 +79,7 @@ const H5ProgressBar: React.FC = ({ return 0 /** Default to script stage */ }, [ + isFailed, taskObject.currentStage, taskObject.roles?.data, taskObject.roles?.total_count, @@ -134,6 +143,10 @@ const H5ProgressBar: React.FC = ({ return Math.min(Math.round((videosCount / videosTotal) * 100), 95) case 3: /** Final video stage */ + /** If task failed, show 60% progress */ + if (isFailed) { + return 60 + } /** If final.url exists, show 100% */ if (taskObject.final?.url) { return 100 @@ -166,6 +179,7 @@ const H5ProgressBar: React.FC = ({ }) }, [ currentStage, + isFailed, scriptData, taskObject.roles?.data, taskObject.roles?.total_count, @@ -184,6 +198,8 @@ const H5ProgressBar: React.FC = ({
{segments.map(({ stage, config, isCompleted, isCurrent, segmentProgress }) => { const Icon = config.icon + /** Check if this is the last stage and task has failed */ + const isFailedStage = isFailed && stage === 3 return (
= ({ data-alt="progress-fill" className="absolute inset-0 rounded-full z-0 backdrop-blur-md" style={{ - background: `${config.color}80` + background: isFailedStage ? '#ef444480' : `${config.color}80` }} initial={{ width: '0%' }} animate={{ width: `${segmentProgress}%` }} @@ -218,13 +234,13 @@ const H5ProgressBar: React.FC = ({ x: '-50%', opacity: 1, scale: 1, - rotate: [0, 360], + rotate: isFailedStage ? 0 : [0, 360], transition: { left: { duration: 0.6, ease: 'easeInOut' }, x: { duration: 0 }, opacity: { duration: 0.3 }, scale: { duration: 0.3 }, - rotate: { duration: 2, repeat: Infinity, ease: 'linear' } + rotate: isFailedStage ? { duration: 0 } : { duration: 2, repeat: Infinity, ease: 'linear' } } }} exit={{ @@ -237,8 +253,8 @@ const H5ProgressBar: React.FC = ({ data-alt="icon-wrapper" className="w-3 h-3 rounded-full bg-white/90 backdrop-blur-sm flex items-center justify-center shadow-lg" style={{ - boxShadow: `0 0 8px ${config.color}80`, - background: `${config.color}` + boxShadow: isFailedStage ? '0 0 8px #ef444480' : `0 0 8px ${config.color}80`, + background: isFailedStage ? '#ef4444' : `${config.color}` }} > {/* */} @@ -249,7 +265,7 @@ const H5ProgressBar: React.FC = ({ {/* Glow effect for current stage */} - {isCurrent && segmentProgress < 100 && ( + {isCurrent && segmentProgress < 100 && !isFailedStage && ( void; setIsPauseWorkFlow: (isPauseWorkFlow: boolean) => void; } @@ -136,7 +135,6 @@ export function TaskInfo({ currentLoadingText, roles, isPauseWorkFlow, - showGotoCutButton, onGotoCut, setIsPauseWorkFlow }: TaskInfoProps) { @@ -151,6 +149,7 @@ export function TaskInfo({ // 监听 currentLoadingText useEffect(() => { + console.log('currentLoadingText', currentLoadingText); // 清理之前的定时器 if (timerRef.current) { clearTimeout(timerRef.current); @@ -358,42 +357,6 @@ export function TaskInfo({ /> - - {/* - */} - - {/* // 跳转剪辑按钮 - {showGotoCutButton && ( - - - - )} */} ) ) diff --git a/components/pages/work-flow/thumbnail-grid.tsx b/components/pages/work-flow/thumbnail-grid.tsx index a008102..4972520 100644 --- a/components/pages/work-flow/thumbnail-grid.tsx +++ b/components/pages/work-flow/thumbnail-grid.tsx @@ -118,7 +118,6 @@ export function ThumbnailGrid({ } } - console.log('changedIndex_thumbnail-grid', changedIndex); if (changedIndex !== -1) { diff --git a/components/pages/work-flow/use-workflow-data.tsx b/components/pages/work-flow/use-workflow-data.tsx index 326efa4..f97f910 100644 --- a/components/pages/work-flow/use-workflow-data.tsx +++ b/components/pages/work-flow/use-workflow-data.tsx @@ -7,49 +7,24 @@ 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 = {}) { +export function useWorkflowData({}: 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(); }; }, []); // 查看缓存中 是否已经 加载过 这个项目的 剪辑计划 @@ -90,7 +65,6 @@ export function useWorkflowData({ onEditPlanGenerated, editingStatus, onExportFa 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({ @@ -156,102 +130,13 @@ export function useWorkflowData({ onEditPlanGenerated, editingStatus, onExportFa } }, [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; @@ -288,10 +173,18 @@ export function useWorkflowData({ onEditPlanGenerated, editingStatus, onExportFa 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'}); + if (taskObject.status === 'FAILED') { + if (isShowError) { + loadingText.current = LOADING_TEXT_MAP.toManyFailed; + window.msg.error('Too many failed storyboards, unable to execute automatic editing.', 8000); + } else { + loadingText.current = LOADING_TEXT_MAP.editingError; + window.msg.error('Editing failed, Please click the scissors button to go to the intelligent editing platform.', 8000); + } + } - // 将 sketchCount 和 videoCount 放到 redux 中 每一次变化也要更新 + setCurrentLoadingText(loadingText.current); + }, [isShowError, scriptBlocksMemo, taskObject.currentStage, taskObject.scenes.data, taskObject.roles.data, taskObject.videos.data, taskObject.status], {mode: 'none'}); // 添加手动播放控制 const handleManualPlay = useCallback(async () => { @@ -314,9 +207,6 @@ export function useWorkflowData({ onEditPlanGenerated, editingStatus, onExportFa 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); @@ -354,7 +244,6 @@ export function useWorkflowData({ onEditPlanGenerated, editingStatus, onExportFa 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'; @@ -403,39 +292,18 @@ export function useWorkflowData({ onEditPlanGenerated, editingStatus, onExportFa }); } 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(error_totle === total_count) { + setIsShowError(true); + taskCurrent.status = 'FAILED'; + setNeedStreamData(false); } } } @@ -447,11 +315,6 @@ export function useWorkflowData({ onEditPlanGenerated, editingStatus, onExportFa } if (task.task_status === 'FAILED' || task.task_status === 'ERROR') { taskCurrent.status = 'FAILED'; - - // 触发导出失败回调 - if (onExportFailed) { - onExportFailed(); - } // 停止轮询 setNeedStreamData(false); } @@ -492,13 +355,7 @@ export function useWorkflowData({ onEditPlanGenerated, editingStatus, onExportFa } } - 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})); @@ -509,7 +366,7 @@ export function useWorkflowData({ onEditPlanGenerated, editingStatus, onExportFa } catch (error) { console.error('获取数据失败:', error); } - }, [episodeId, needStreamData, onExportFailed, errorConfig, isAnalyzing]); + }, [episodeId, needStreamData, errorConfig, isAnalyzing]); // 轮询获取流式数据 useUpdateEffect(() => { @@ -563,7 +420,6 @@ export function useWorkflowData({ onEditPlanGenerated, editingStatus, onExportFa 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 || []; @@ -625,10 +481,8 @@ export function useWorkflowData({ onEditPlanGenerated, editingStatus, onExportFa 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({ @@ -679,10 +533,7 @@ export function useWorkflowData({ onEditPlanGenerated, editingStatus, onExportFa 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(() => { }); @@ -701,8 +552,7 @@ export function useWorkflowData({ onEditPlanGenerated, editingStatus, onExportFa }); // 设置是否需要获取流式数据 - // setNeedStreamData(taskCurrent.status !== 'COMPLETED'); - setNeedStreamData(true); + setNeedStreamData(taskCurrent.status !== 'COMPLETED'); } catch (error) { console.error('初始化失败:', error); @@ -789,10 +639,9 @@ export function useWorkflowData({ onEditPlanGenerated, editingStatus, onExportFa applyScript, fallbackToStep, originalText: state.originalText, - showGotoCutButton: (canGoToCut && (isGenerateEditPlan || taskObject.currentStage === 'final_video') || isShowError) ? true : false, + showGotoCutButton: (taskObject.status === 'FAILED' || taskObject.status === 'COMPLETED') ? true : false, generateEditPlan: openEditPlan, handleRetryVideo, - isShowAutoEditing: canGoToCut && taskObject.currentStage !== 'final_video' && isGenerateEditPlan && !isShowError ? true : false, aspectRatio: state.aspectRatio }; }