优化生成视频出现字幕问题

This commit is contained in:
Azmat@qq.com
2026-09-02 11:15:27 +08:00
parent 70d6353d28
commit ad6f4281c0
8 changed files with 152 additions and 53 deletions
+44 -23
View File
@@ -874,32 +874,46 @@ class VideoSubmissionStateUnknown(ProviderOutcomeUnknownError):
# 本系统出的任何视频都不带字幕。这条是最高优先级的成片硬性禁令,写得穷尽一点 ——
# 实测只写「不要字幕」时,模型仍会自作主张加花字 / 价格贴片 / 购物浮层。
# ★ 这条的写法本身就是功能的一部分,改之前先读这段:
# 视频扩散模型对「否定」很弱、对「名词」很强。旧版禁令把「字幕」念 7 次、「花字」念 3 次,
# 等于反复把这个视觉概念喂给模型 —— 实测连续两条成片都因此带上了字幕。
# 所以新版反过来:**以正面陈述为主**(画面应该长什么样),负面名词全片只出现一次,
# 并把「台词只出声、不上屏」单独讲明白(这是最大的诱因,见 _segment_script_text 的人声区)。
# 维护提醒:往这条里加词 = 提高负面名词词频 = 更容易出字幕。要加先想清楚。
NO_EMBEDDED_CAPTIONS_REQUIREMENT = (
"【字幕硬性禁令 · 最高优先级】从第0秒的开场首帧起直到片尾,本片必须是**无字幕成片**。"
"尤其前3秒不得出现任何标题、开场文案、花字或贴片;画面中绝对不能出现任何后期文字或图形叠加,包括但不限于:"
"字幕、台词字幕、双语字幕、注音、标题、片头、片尾、花字、艺术字、水印、角标、台标、"
"logo 贴片、价格贴片、优惠券、促销标签、倒计时、弹幕、对话气泡、贴纸、表情包文字、"
"参数表、成分表、对比图表、评分条、UI 界面、按钮、购物车 / 购买浮层 / 商品卡、进度条、时间码。"
"口播、台词、旁白、音效只以**声音**存在,绝不转写成画面上的文字。"
"画面里唯一允许出现的文字,是参考图中商品包装 / 标签上原本就有的真实物理文字与品牌标识,"
"且必须与参考图完全一致,不得放大、重排或新增。"
"即使本提示词别处提到了「字幕」「花字」「标注」等字样,也一律以本条为准:不生成任何文字叠加。"
"【画面洁净 · 最高优先级】成片是一条纯实拍素材:画面上只有真实拍到的人、商品和环境,"
"没有任何后期叠加层。全片从开场首帧到结尾都保持这个状态,开头也不加标题页。"
"画面里能看到的文字只有一种 —— 参考图中商品包装、标签上本来就印着的那些,"
"保持与参考图一致,不放大、不重排、不新增。"
"人物说的话通过口型和声音表达,不写到画面上;不要字幕,也不要任何贴片、浮层或界面元素。"
)
# 结尾再补一句极短的正面复述:末位权重高,但只用「叠加」这类中性词,不再重复负面名词。
NO_EMBEDDED_CAPTIONS_TAIL = "画面全程保持纯实拍,无任何叠加层。"
# 提示词正文里残留的「字幕:…」「花字:…」会和上面的禁令打架 —— 模型看到具体字幕内容就会画出来。
# 来源:脚本【声音】栏、视频提炼稿逐字照抄的画面花字、用户自己写的提示词。这里在送出前统一抹掉。
_CAPTION_FIELD_LINE_RE = re.compile(r"^\s*[-•*]?\s*(?:字幕|花字|艺术字|贴片|水印|角标|标题栏)\s*[::].*$")
_CAPTION_FIELD_INLINE_RE = re.compile(r"[,,;;、]?\s*(?:字幕|花字|艺术字|贴片|水印|角标)\s*[::][^;;。\n]*")
_CAPTION_WORDS = r"(?:字幕|花字|艺术字|贴片|水印|角标|标题栏|弹幕)"
_CAPTION_FIELD_LINE_RE = re.compile(rf"^\s*[-•*]?\s*{_CAPTION_WORDS}\s*[::].*$")
_CAPTION_FIELD_INLINE_RE = re.compile(rf"[,,;;、]?\s*{_CAPTION_WORDS}\s*[::][^;;。\n]*")
# 无冒号的写法同样要清:「右下角加字幕」「配上花字」「画面无字幕」等。
# 历史项目早就把这些落进了 ScriptSegment.visual_prompt,光改 skill 管不到存量数据。
# 按**子句**整段删(以 ;;,,。 断句),否则会留下「右下角」「画面无」这种断头残渣让模型犯迷糊。
_CAPTION_CLAUSE_RE = re.compile(rf"[,,;;、]?\s*[^,,;;。\n]*{_CAPTION_WORDS}[^,,;;。\n]*[。]?")
def strip_caption_directives(prompt: str) -> str:
"""抹掉提示词里「字幕:xxx / 花字:xxx」这类会诱使模型画文字的栏位(整行或行内片段)。
只删这类字段,不动其余正文;删空的行直接丢掉。"""
"""把正文里所有会让模型联想到「画面文字」的字样抹掉 —— 无论它是要求加字幕还是声明没有字幕。
模型不区分肯定否定,看到「字幕」这个词本身就更容易画出来,所以一律清掉,
真正的规则只由 NO_EMBEDDED_CAPTIONS_REQUIREMENT 一处统一表述。
来源:脚本【声音】栏、视频提炼稿逐字照抄的原片花字、用户自己写的提示词、存量项目的历史脚本。
只清这些字样,不动其余正文;整行被清空则丢掉该行。"""
out: list[str] = []
for line in (prompt or "").split("\n"):
if _CAPTION_FIELD_LINE_RE.match(line):
continue
cleaned = _CAPTION_FIELD_INLINE_RE.sub("", line)
cleaned = _CAPTION_CLAUSE_RE.sub("", _CAPTION_FIELD_INLINE_RE.sub("", line))
if cleaned.strip() or not line.strip():
out.append(cleaned)
return "\n".join(out)
@@ -909,10 +923,13 @@ def enforce_no_embedded_captions(prompt: str) -> str:
"""所有视频入口(专业创作 / 一键成片 / 自由创作 / 视频复刻 / 提炼改写)最终汇入这里:
先洗掉正文里的字幕字段,再把最高优先级的无字幕禁令置于提示词第一行。
首镜最容易被模型自动加标题,所以不能再把禁令放在长提示词末尾。"""
base = strip_caption_directives(prompt or "").strip()
# 统一把规则前置;即便调用方已经加过,也避免第二次拼接或把禁令淹没在正文中。
base = base.replace(NO_EMBEDDED_CAPTIONS_REQUIREMENT, "").strip()
return f"{NO_EMBEDDED_CAPTIONS_REQUIREMENT}\n{base}".strip()
# 顺序要紧:先摘掉可能已存在的规则本身,再洗正文,最后统一拼回去。
# 反过来的话,清洗器会把规则里「不要字幕…」那半句也当成字幕字样吃掉 → 摘不干净 → 规则拼两遍
# (重复 = 负面词频翻倍 = 更容易出字幕,正是本次要根治的毛病)。
base = (prompt or "").replace(NO_EMBEDDED_CAPTIONS_REQUIREMENT, "").replace(NO_EMBEDDED_CAPTIONS_TAIL, "")
base = strip_caption_directives(base).strip()
# 首帧最容易被模型自动加标题页 → 规则放开头;末位权重高 → 结尾补一句中性的正面复述。
return f"{NO_EMBEDDED_CAPTIONS_REQUIREMENT}\n{base}\n{NO_EMBEDDED_CAPTIONS_TAIL}".strip()
def execute_routed_video_submit(
@@ -2649,9 +2666,12 @@ def _scene_context(project) -> str:
def _segment_script_text(segment, entities=None, *, with_dialogue: bool = False, with_exposure: bool = True) -> str:
"""本镜脚本文本(画面 + 台词/口播 + 商品露出),拼进故事板/视频提示词的【分镜脚本】。
"""本镜脚本文本(画面 + 人声 + 商品露出),拼进视频提示词的【脚本】。
with_dialogue:有结构化对白时按「说话人:台词」原样放(说话人 id 用 entities 解析成名字),回退扁平 narration。
with_exposure:是否带「商品露出:…」一行。"""
with_exposure:是否带「商品露出:…」一行。
★ 人声一律带「仅音频…不出现在画面上」的抬头:裸摆一段台词时,出片模型会把这段可见文本
当成需要渲染的画面文字 —— 这是成片带字幕最常见的来源,比缺少禁令更致命。"""
parts = []
visual = (segment.visual_prompt or "").strip()
if visual:
@@ -2670,11 +2690,12 @@ def _segment_script_text(segment, entities=None, *, with_dialogue: bool = False,
spk = name_by_id.get(d.get("speaker")) or ""
lines.append(f"{spk}:{line}" if spk else line)
if lines: # 有对白 → 用带说话人的台词
parts.append("台词:\n" + "\n".join(lines))
# 「仅音频」这个抬头是防字幕的关键:裸摆一段台词,模型会当成要显示的文本渲染上屏
parts.append("人声(仅音频,由人物口型与配音表达,不出现在画面上):\n" + "\n".join(lines))
else: # 无对白 → 回退扁平口播/旁白
narration = (segment.narration or "").strip()
if narration:
parts.append(f"口播/旁白:{narration}")
parts.append(f"人声(仅音频,由人物口型与配音表达,不出现在画面上):{narration}")
if with_exposure and getattr(segment, "product_exposure", ""):
parts.append(f"商品露出:{segment.product_exposure.strip()}")
return "\n".join(parts)
@@ -2710,7 +2731,7 @@ def build_video_segment_prompt(project, video_segment, scene, refs, user_prompt:
"商品用法必须是真人会做的(茶/咖啡用热水、有蒸汽与茶汤渐染;护肤品挤出并涂抹;食品打开并入口),"
"禁止诡异姿势、错误容器或违背常识的操作。"
"\n【画质】真人实拍质感,自然光影,肤色与材质真实,焦点始终落在脚本指定的主体上,画面稳定不糊。"
"\n电商带货短视频,商品露出清晰,节奏有转化感。不要字幕,不要背景音乐,但是要有音效,逼真的音效。"
"\n电商带货短视频,商品露出清晰,节奏有转化感。不要背景音乐,但是要有音效,逼真的音效。"
)
rendered = render_prompt(
"video_segment", default,