diff --git a/apps/web/src/pages/ai-avatar/AiAvatarPage.tsx b/apps/web/src/pages/ai-avatar/AiAvatarPage.tsx index 68b56f5f0..202fede6d 100644 --- a/apps/web/src/pages/ai-avatar/AiAvatarPage.tsx +++ b/apps/web/src/pages/ai-avatar/AiAvatarPage.tsx @@ -80,6 +80,8 @@ const AiAvatarPage: React.FC = () => { const [finalizeLoading, setFinalizeLoading] = useState(false) /* ── 对口型轮询 ── */ + /** 对口型轮询总时长上限(10分钟):超过后停止轮询并提示去历史记录查看 */ + const LIPSYNC_POLL_MAX_MS = 10 * 60 * 1000 const lipsyncTimerRef = useRef | null>(null) /* ── 渲染进度轮询 ── */ const renderTimerRef = useRef | null>(null) @@ -270,7 +272,24 @@ const AiAvatarPage: React.FC = () => { // 如果是预合成模式,后端会同步把状态置为 submitted(甚至可能已返回 running), // 但仍需轮询等 completed if (lipsyncTimerRef.current) clearInterval(lipsyncTimerRef.current) + // 轮询间隔 5 秒;单请求超时 5 分钟(见 api/aiAvatar.ts);总轮询上限 10 分钟 + // 单次请求失败/超时不中断轮询,继续下一轮;超过总上限后停止并提示用户去历史记录查看 lipsyncTimerRef.current = setInterval(async () => { + // 总时长保护:超过 10 分钟停止轮询 + if (Date.now() - lipsyncStartAtRef.current > LIPSYNC_POLL_MAX_MS) { + if (lipsyncTimerRef.current) { + clearInterval(lipsyncTimerRef.current) + lipsyncTimerRef.current = null + } + if (lipsyncTickRef.current) { + clearInterval(lipsyncTickRef.current) + lipsyncTickRef.current = null + } + setLipsyncStatus("failed") + setLipsyncErrorMessage("渲染时间较长,请稍后在历史记录中查看") + message.warning("对口型渲染时间较长,已停止自动刷新,请稍后在历史记录中查看") + return + } try { const updated = await getLipsyncJob(job.id) state.setLipsyncJob(updated) @@ -296,9 +315,10 @@ const AiAvatarPage: React.FC = () => { setLipsyncErrorMessage(updated.error_message || "对口型生成失败") } } catch (err) { - console.error("[对口型] 轮询错误:", err) + // 单次轮询失败(含 timeout):不中断轮询,打印日志后等下一轮 + console.warn("[对口型] 轮询请求失败,将继续下一轮:", err) } - }, 3000) + }, 5000) } catch (err) { console.error("[对口型] 创建失败:", { status: (err as { response?: { status?: number } })?.response?.status, diff --git a/apps/web/src/pages/ai-avatar/api/aiAvatar.ts b/apps/web/src/pages/ai-avatar/api/aiAvatar.ts index d8dbc2037..0b65bbc04 100644 --- a/apps/web/src/pages/ai-avatar/api/aiAvatar.ts +++ b/apps/web/src/pages/ai-avatar/api/aiAvatar.ts @@ -72,7 +72,8 @@ export const previewTts = async (data: { } export const getLipsyncJob = async (id: string): Promise => { - const response = await apiClient.get(`/lipsync/jobs/${id}`, { timeout: 60000 }) + // MuseTalk 渲染 8s 视频约 54s + 排队时间,给足 5 分钟超时避免单次轮询 AxiosError 中断 + const response = await apiClient.get(`/lipsync/jobs/${id}`, { timeout: 300_000 }) return response.data } @@ -91,7 +92,10 @@ export const submitRender = async (data: { } export const getRenderJob = async (jobId: string): Promise => { - const response = await apiClient.get(`/ai-avatar/render/${jobId}`, { timeout: 60000 }) + // 渲染链路(对口型+B-roll+标题+合成+上传)耗时较长,给足 5 分钟超时 + const response = await apiClient.get(`/ai-avatar/render/${jobId}`, { + timeout: 300_000, + }) return response.data }