diff --git a/apps/api/app/services/ai_avatar_render_service.py b/apps/api/app/services/ai_avatar_render_service.py index 92952663c..1d2294dfc 100644 --- a/apps/api/app/services/ai_avatar_render_service.py +++ b/apps/api/app/services/ai_avatar_render_service.py @@ -335,7 +335,8 @@ class AiAvatarRenderService: output_video_url, job_id=job_id, max_frames=5, - title_config=job.title_config, + # 注意:不传 title_config —— 最终输出视频已经通过 drawtext 叠加了标题, + # 再传会导致封面标题双重叠加 ) except Exception: logger.warning("智能封面(MediaKit)失败,回退 FFmpeg 封面 job_id=%s", job_id, exc_info=True) diff --git a/apps/web/src/pages/ai-avatar/AiAvatarPage.tsx b/apps/web/src/pages/ai-avatar/AiAvatarPage.tsx index 8ca9a999d..96637178a 100644 --- a/apps/web/src/pages/ai-avatar/AiAvatarPage.tsx +++ b/apps/web/src/pages/ai-avatar/AiAvatarPage.tsx @@ -279,7 +279,7 @@ const AiAvatarPage: React.FC = () => { } setSmartCoverLoading(true) try { - const res = await generateSmartCover(videoUrl, 5) + const res = await generateSmartCover(videoUrl, state.titleConfig, 5) if (res.cover_url) { state.setCoverConfig((prev) => ({ ...prev, @@ -342,7 +342,6 @@ const AiAvatarPage: React.FC = () => { selectedVideo={state.selectedVideo} onSelectVideo={() => state.setShowAssetPicker(true)} onRemoveVideo={state.removeVideo} - titleConfig={state.titleConfig} /> diff --git a/apps/web/src/pages/ai-avatar/api/aiAvatar.ts b/apps/web/src/pages/ai-avatar/api/aiAvatar.ts index ee02cde82..276c87372 100644 --- a/apps/web/src/pages/ai-avatar/api/aiAvatar.ts +++ b/apps/web/src/pages/ai-avatar/api/aiAvatar.ts @@ -2,7 +2,7 @@ * AI数字人 — API 封装(#1822 契约对齐) */ import apiClient from "@/api/client" -import type { Script, LipsyncJob, RenderJob, BRollSegment } from "../types" +import type { Script, LipsyncJob, RenderJob, BRollSegment, AiAvatarTitleConfig } from "../types" /* ── 文案库 ── */ export const getScripts = async (): Promise => { @@ -58,14 +58,15 @@ export const getLipsyncJob = async (id: string): Promise => { return response.data } -/* ── 智能封面(MediaKit 抽帧 + 质量评分选最佳帧,独立于渲染任务) ── */ +/* ── 智能封面(MediaKit 抽帧 + 质量评分选最佳帧 + 可选 drawtext 标题叠加) ── */ export const generateSmartCover = async ( video_url: string, + title_config?: AiAvatarTitleConfig | null, max_frames = 5, ): Promise<{ cover_url: string; status: string; message: string }> => { const response = await apiClient.post<{ cover_url: string; status: string; message: string }>( "/ai-avatar/render/smart-cover", - { video_url, max_frames }, + { video_url, max_frames, title_config: title_config ?? null }, // smart-cover 链路:下载视频+抽帧+drawtext 加标题+上传 OSS,需要较长时间,120s 超时 { timeout: 120000 }, ) @@ -86,7 +87,7 @@ export const submitRender = async (data: { } export const getRenderJob = async (jobId: string): Promise => { - const response = await apiClient.get(`/ai-avatar/render/${jobId}`) + const response = await apiClient.get(`/ai-avatar/render/${jobId}`, { timeout: 60000 }) return response.data } diff --git a/apps/web/src/pages/ai-avatar/components/PanelVideoSelector.tsx b/apps/web/src/pages/ai-avatar/components/PanelVideoSelector.tsx index b09baf3f0..a2379a4dd 100644 --- a/apps/web/src/pages/ai-avatar/components/PanelVideoSelector.tsx +++ b/apps/web/src/pages/ai-avatar/components/PanelVideoSelector.tsx @@ -2,17 +2,16 @@ * AI数字人 — 出镜视频选择面板 * - 未选视频:虚线上传区,点击打开素材库弹窗 * - 已选视频:竖屏 9:16 预览播放器 + 视频信息卡片 + 移除按钮 + * + * 注意:本面板只展示原始素材视频,不叠加标题(标题在对口型预览和最终成片上展示) */ import type { AssetItem } from "@/api/assets" -import type { AiAvatarTitleConfig } from "../types" -import { getFontFamily } from "@/pages/generate/constants" export interface PanelVideoSelectorProps { selectedVideo: AssetItem | null /** 触发打开素材库弹窗 */ onSelectVideo: () => void onRemoveVideo: () => void - titleConfig?: AiAvatarTitleConfig } /** 格式化时长(秒 → mm:ss) */ @@ -27,7 +26,6 @@ export function PanelVideoSelector({ selectedVideo, onSelectVideo, onRemoveVideo, - titleConfig, }: PanelVideoSelectorProps) { /* 未选视频:虚线上传区,点击打开素材库弹窗 */ if (!selectedVideo) { @@ -57,42 +55,13 @@ export function PanelVideoSelector({ return (
- {/* 竖屏 9:16 视频预览播放器 + 标题实时预览 */} -
+ {/* 竖屏 9:16 视频预览播放器(纯素材预览,不叠加标题) */} +
{fileUrl ? (
{/* 视频信息卡片:文件名 / 时长 / 分辨率 */}