fix(ai-avatar): 素材预览不叠加标题 + 封面接口传title_config + 渲染后封面避免双标题
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 4s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 33s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 33s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 1m14s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m32s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 2m56s
AI Code Review / AI Code Review (pull_request) Successful in 6m37s
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Failing after 10m16s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 1m18s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Successful in 1m20s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 1m22s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Successful in 1m42s
CI/CD Pipeline / Validate - Style (pull_request) Successful in 1m49s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 10m36s
CI/CD Pipeline / Validate - Security (pull_request) Successful in 6m39s
CI/CD Pipeline / CI Gate (pull_request) Successful in 1s
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 55s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 1m11s
CI/CD Pipeline / Build Production API Image (pull_request) Failing after 116h37m5s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 116h53m41s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 116h53m52s
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Failing after 116h53m31s
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Failing after 116h53m31s
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Failing after 116h53m31s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 116h53m36s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 116h53m37s
CI/CD Pipeline / Check push changed paths (pull_request) Failing after 116h53m42s
CI/CD Pipeline / Build Production Web Image (pull_request) Failing after 116h36m49s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 116h53m22s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 116h53m22s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 116h43m28s
CI/CD Pipeline / Canary Release to Production (pull_request) Failing after 116h36m48s
CI/CD Pipeline / Deploy Production (pull_request) Failing after 116h36m48s
CI/CD Pipeline / Build Production Worker Image (pull_request) Failing after 116h36m49s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 116h53m22s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 4s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 33s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 33s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 1m14s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m32s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 2m56s
AI Code Review / AI Code Review (pull_request) Successful in 6m37s
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Failing after 10m16s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 1m18s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Successful in 1m20s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 1m22s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Successful in 1m42s
CI/CD Pipeline / Validate - Style (pull_request) Successful in 1m49s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 10m36s
CI/CD Pipeline / Validate - Security (pull_request) Successful in 6m39s
CI/CD Pipeline / CI Gate (pull_request) Successful in 1s
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 55s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 1m11s
CI/CD Pipeline / Build Production API Image (pull_request) Failing after 116h37m5s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 116h53m41s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 116h53m52s
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Failing after 116h53m31s
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Failing after 116h53m31s
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Failing after 116h53m31s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 116h53m36s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 116h53m37s
CI/CD Pipeline / Check push changed paths (pull_request) Failing after 116h53m42s
CI/CD Pipeline / Build Production Web Image (pull_request) Failing after 116h36m49s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 116h53m22s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 116h53m22s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 116h43m28s
CI/CD Pipeline / Canary Release to Production (pull_request) Failing after 116h36m48s
CI/CD Pipeline / Deploy Production (pull_request) Failing after 116h36m48s
CI/CD Pipeline / Build Production Worker Image (pull_request) Failing after 116h36m49s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 116h53m22s
1. PanelVideoSelector: 移除素材视频上的标题叠加预览,标题只在对口型预览和最终成片上展示 2. generateSmartCover API: 传入title_config参数,让后端在对口型视频帧上用drawtext叠加标题 3. AiAvatarPage: 调用generateSmartCover时传入state.titleConfig;移除传给PanelVideoSelector的titleConfig prop 4. ai_avatar_render_service: 渲染完成后自动抽封面时不传title_config,避免最终视频已有标题再叠加导致双重标题
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -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<Script[]> => {
|
||||
@@ -58,14 +58,15 @@ export const getLipsyncJob = async (id: string): Promise<LipsyncJob> => {
|
||||
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<RenderJob> => {
|
||||
const response = await apiClient.get<RenderJob>(`/ai-avatar/render/${jobId}`)
|
||||
const response = await apiClient.get<RenderJob>(`/ai-avatar/render/${jobId}`, { timeout: 60000 })
|
||||
return response.data
|
||||
}
|
||||
|
||||
|
||||
@@ -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 (
|
||||
<div>
|
||||
{/* 竖屏 9:16 视频预览播放器 + 标题实时预览 */}
|
||||
<div className="aa-video-preview" style={{ position: "relative" }}>
|
||||
{/* 竖屏 9:16 视频预览播放器(纯素材预览,不叠加标题) */}
|
||||
<div className="aa-video-preview">
|
||||
{fileUrl ? (
|
||||
<video src={fileUrl} poster={selectedVideo.thumbnail_url} controls playsInline />
|
||||
) : (
|
||||
<div className="aa-video-preview__placeholder">视频暂不可预览</div>
|
||||
)}
|
||||
{titleConfig?.title && (
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
left: "50%",
|
||||
transform: "translateX(-50%)",
|
||||
...(titleConfig.position === "top"
|
||||
? { top: "10%" }
|
||||
: titleConfig.position === "bottom"
|
||||
? { bottom: "10%" }
|
||||
: { top: "50%", transform: "translate(-50%, -50%)" }),
|
||||
fontSize: Math.max(titleConfig.size, 32),
|
||||
fontFamily: getFontFamily(titleConfig.font),
|
||||
color: titleConfig.color,
|
||||
fontWeight: titleConfig.bold ? 700 : 400,
|
||||
fontStyle: titleConfig.italic ? "italic" : "normal",
|
||||
textShadow: "0 2px 4px rgba(0,0,0,0.5)",
|
||||
WebkitTextStroke: "2px #000",
|
||||
pointerEvents: "none",
|
||||
zIndex: 10,
|
||||
maxWidth: "90%",
|
||||
textAlign: "center",
|
||||
whiteSpace: "pre-wrap",
|
||||
lineHeight: 1.3,
|
||||
}}
|
||||
>
|
||||
{titleConfig.title}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 视频信息卡片:文件名 / 时长 / 分辨率 */}
|
||||
|
||||
Reference in New Issue
Block a user