优化完善替换角色

This commit is contained in:
Azmat@qq.com
2026-09-01 18:29:36 +08:00
parent 0603a85d83
commit 1825543873
4 changed files with 64 additions and 121 deletions
+11 -61
View File
@@ -83,54 +83,25 @@ PRODUCT_PROMPT = (
"按下面的商品语义重构分镜生成视频。@目标商品只用于锁定外观、材质与包装。"
"严格遵守每镜的新商品动作、场景与口播;不要恢复参考片原商品、原动作、原卖点或原台词。"
)
# 角色复刻提示词:极简单句。之前那版把声音规则写成五大段反复强调,实测是「开头原声、
# 中途才切到目标音色」;长提示词会稀释关键指令,这版改为一句话直给
# ⚠️ 句中的 @参考角色 / @参考音频 必须与 references 的 label 完全一致,否则
# build_content_items 映射不成「图片N / 音频N」,火山那边就是一串没有指代的字面量
# 角色复刻提示词:极简单句。
# ⚠️ 只换画面,不碰声音 —— 成片保留参考视频原声
# 历史(别再走一遍):试过让 Seedance 换声的四条路,提示词声线规则、音频参考走 TOS 直链、
# 走 asset:// 素材库引用、以及平台预置样音,全部只做到「中途才切到目标音色」或完全无效
# 编辑模式(参考视频 + duration=-1 + adaptive)对音轨的处理不受这些输入控制。
# 因此产品上收敛为:只支持同性别角色替换,声音一律保留原声,前端明确告知用户。
CHARACTER_PROMPT = (
"将@参考视频中出现的角色替换为@参考角色,口播、旁白、画外音的音色替换为@参考音频,"
"其它部分例如构图、运镜、人物动作、场景、商品、剪辑、画面节奏等保持与原视频一致。"
)
# 没有音频参考(没写明性别)时,把声音那半句去掉,免得点名一个不存在的 @参考音频。
CHARACTER_PROMPT_NO_VOICE = (
"将@参考视频中出现的角色替换为@参考角色,"
"其它部分例如构图、运镜、人物动作、场景、商品、剪辑、画面节奏等保持与原视频一致。"
)
def build_character_submit_prompt(subject_brief: str = "", *, has_voice_ref: bool = False) -> str:
"""角色复刻提交提示词。就是那一句,不再追加角色设定 ——
subject_brief 只用于推导性别(决定用哪条音色样音),不进提示词,保持指令干净。"""
return CHARACTER_PROMPT if has_voice_ref else CHARACTER_PROMPT_NO_VOICE
"""角色复刻提交提示词。就是那一句
# 平台预置的目标音色样音(公读直链,不走预签名 —— 参考素材要长期可取)。
VOICE_REFERENCE_LABEL = "参考音频"
VOICE_REFERENCE_URLS = {
# ⚠️ 换音色样音时务必换文件名,不要覆盖旧文件:上传设的是 immutable + 一年强缓存,
# 同名覆盖后 CDN / 火山侧可能仍拿到旧音频,查起来很难受。
"male": "https://airshelf.tos-s3-cn-shanghai.volces.com/system/video-replace/voice-references/male-v3.mp3",
"female": "https://airshelf.tos-s3-cn-shanghai.volces.com/system/video-replace/voice-references/female-bv001.mp3",
}
def build_voice_reference_ref(gender: str) -> dict | None:
"""平台预置音色样音的 reference 项。读不出性别就没有音频参考。
⚠️ 不带 asset_id:带了会被 _ensure_replace_refs_reviewed 送去火山真人素材审核,
而那套是给图片/视频用的,平台音频过不去会把任务卡死。
参数保留是为了兼容既有调用点;声音已不再由提示词控制,两个参数都不影响输出。
"""
url = VOICE_REFERENCE_URLS.get(gender)
if not url:
return None
return {
"url": url,
"type": "audio",
"role": "reference_audio",
"label": VOICE_REFERENCE_LABEL,
"source": "platform",
}
return CHARACTER_PROMPT
# 用户在第 3 步填的补充信息(商品名/品类,或角色性别年龄音色)。选填,不填维持原行为。
MAX_SUBJECT_BRIEF = 500
@@ -140,20 +111,6 @@ def _clean_subject_brief(value) -> str:
return str(value or "").strip()[:MAX_SUBJECT_BRIEF]
def _detect_voice_gender(brief: str) -> str:
"""从用户填的角色信息里读性别。返回 male / female / ""(读不出)。
男女都提到(例如「把女的换成男的」)时不下断言 —— 分不清哪个是目标。
"""
has_male = "" in brief
has_female = "" in brief
if has_male and not has_female:
return "male"
if has_female and not has_male:
return "female"
return ""
# 拆解期间的占位提示词。真正的提示词在 worker 拆完后回写。
DIGEST_PENDING_PROMPT = "正在提炼参考视频的分镜稿…"
@@ -730,7 +687,6 @@ def serialize_video_replace_task(task, *, include_deleted_assets: bool = False)
"subject_name": payload.get("subject_name") or "",
"subject_source": payload.get("subject_source") or "",
"subject_brief": payload.get("subject_brief") or "",
"voice_gender": payload.get("voice_gender") or "",
"product_id": payload.get("product_id") or "",
"model_id": payload.get("model_id") or "",
"review_stage": "reviewing" if reviewing else "",
@@ -793,7 +749,6 @@ def submit_video_replace(*, team, user, params: dict):
# 第 3 步用户填的补充信息。商品:名称/品类/卖点;角色:性别/年龄/音色。选填。
subject_brief = _clean_subject_brief(params.get("subject_brief"))
# 角色描述识别到性别时,成片后调用火山音色转换;识别不到即保留原声。
voice_gender = _detect_voice_gender(subject_brief) if replace_mode == "character" else ""
if has_product:
subject_name, image_refs, subject_source = _product_library_refs(team, product_id)
@@ -841,8 +796,6 @@ def submit_video_replace(*, team, user, params: dict):
"model_id": str(model_id) if model_id else "",
"product_facts": product_facts,
"subject_brief": subject_brief,
"voice_gender": voice_gender,
"voice_strategy": "native_audio_reference" if voice_gender else "preserve_original",
# adaptive / -1 只用于火山请求;预估积分仍按参考视频实际时长和上传时检测出的比例计算。
"billing_duration": max(4, int(round(video_seconds or 4))),
"billing_aspect_ratio": billing_ratio,
@@ -868,12 +821,9 @@ def submit_video_replace(*, team, user, params: dict):
if resolve_digest_model_config() is None:
raise ValueError("视频提炼模型未配置,请联系管理员")
if replace_mode == "character":
# 平台预置的目标音色样音,和参考视频、角色图一起交给 Seedance。
voice_ref = build_voice_reference_ref(voice_gender)
image_refs = [
_owned_ref(video, kind="video", role="reference_video", label="参考视频"),
*image_refs,
*([voice_ref] if voice_ref else []),
]
# 商品图/角色图仍要过审才能当生成参考。提前送审 → 审核和提炼并行跑,明确不过审的直接 400。
review_state = _ensure_replace_refs_reviewed(team, image_refs)
@@ -895,7 +845,7 @@ def submit_video_replace(*, team, user, params: dict):
**base_params,
"prompt": (
DIGEST_PENDING_PROMPT if replace_mode == "product"
else build_character_submit_prompt(subject_brief, has_voice_ref=bool(voice_gender))
else build_character_submit_prompt()
),
"references": image_refs,
"extra_payload": extra,