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("预览视频就绪,重新生成封面...") // 重试封面生成