From 9cdbe6e97d202d0d4da58f6696e6240cfe7b53ea Mon Sep 17 00:00:00 2001 From: xiaoxia Date: Thu, 20 Aug 2026 11:49:24 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=20AI=20Code=20Review?= =?UTF-8?q?=20v5=EF=BC=88=E8=BD=AE=E8=AF=A2=E8=AF=B7=E6=B1=82=E9=87=8D?= =?UTF-8?q?=E5=8F=A0+=E9=87=8D=E6=92=AD=E9=BB=91=E5=B1=8F=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - useStep6Cover: setInterval 改为递归 setTimeout,确保上一次请求完成后再发起下一次,避免请求风暴 - useCanvasPlayer play(): 检测重播场景(currentTime 接近0但 decodedSegmentsRef 非空)时清空标记并递增 generation,让 decodeAroundPosition 重新解码 --- .../pages/generate/hooks/useCanvasPlayer.ts | 7 ++++ .../src/pages/generate/hooks/useStep6Cover.ts | 40 +++++++++---------- 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/apps/web/src/pages/generate/hooks/useCanvasPlayer.ts b/apps/web/src/pages/generate/hooks/useCanvasPlayer.ts index 9c260776a..b3f69f117 100644 --- a/apps/web/src/pages/generate/hooks/useCanvasPlayer.ts +++ b/apps/web/src/pages/generate/hooks/useCanvasPlayer.ts @@ -749,6 +749,13 @@ export function useCanvasPlayer( const play = useCallback(async () => { if (!state.hasSupport || isDestroyedRef.current) return + // 重播场景:currentTime 已回到起点但 decodedSegmentsRef 仍有旧标记 + // 此时 FrameQueue 中旧帧已被淘汰,需清空标记让 decodeAroundPosition 重新解码 + if (state.currentTime <= 0.1 && decodedSegmentsRef.current.size > 0) { + decodeGenerationRef.current++ + decodedSegmentsRef.current.clear() + } + setState((s) => ({ ...s, isPlaying: true })) playStartRef.current = performance.now() playStartOffsetRef.current = state.currentTime diff --git a/apps/web/src/pages/generate/hooks/useStep6Cover.ts b/apps/web/src/pages/generate/hooks/useStep6Cover.ts index 129f489b7..d050126c7 100644 --- a/apps/web/src/pages/generate/hooks/useStep6Cover.ts +++ b/apps/web/src/pages/generate/hooks/useStep6Cover.ts @@ -137,38 +137,34 @@ export function useStep6Cover({ asset_ids: assetIds, duration: duration || 30, }) - // 轮询等待预览渲染完成,双重超时保护 - const maxPolls = 60 - let pollCount = 0 + // 轮询等待预览渲染完成:递归 setTimeout 避免请求重叠 + 120s 超时兜底 await new Promise((resolve, reject) => { + let finished = false + const done = (fn: () => void) => { + if (finished) return + finished = true + clearTimeout(timeoutId) + fn() + } const timeoutId = setTimeout(() => { - clearInterval(poll) - reject(new Error("预览生成超时,请稍后重试")) + done(() => reject(new Error("预览生成超时,请稍后重试"))) }, 120_000) - const poll = setInterval(async () => { - pollCount++ + const poll = async () => { + if (finished) return try { const status = await getPreviewStatus(previewResp.task_id) if (status.status === "completed") { - clearTimeout(timeoutId) - clearInterval(poll) - resolve() + done(() => resolve()) } else if (status.status === "failed") { - clearTimeout(timeoutId) - clearInterval(poll) - reject(new Error(status.error_message || "预览渲染失败")) - } - if (pollCount >= maxPolls) { - clearTimeout(timeoutId) - clearInterval(poll) - reject(new Error("预览生成超时,请稍后重试")) + done(() => reject(new Error(status.error_message || "预览渲染失败"))) + } else { + setTimeout(poll, 2000) } } catch (e) { - clearTimeout(timeoutId) - clearInterval(poll) - reject(e) + done(() => reject(e)) } - }, 2000) + } + poll() }) message.success("预览视频就绪,重新生成封面...") // 重试封面生成