feat(video-prompt): 台词带说话人照脚本原样 + 时长/比例只走参数不写正文

- _segment_script_text:有结构化对白(dialogue)就按「说话人:台词」原样放(说话人 id 用 script_entities
  解析成名字,如 室友/学生女主),回退扁平 narration;新增 with_dialogue/with_exposure 开关
- build_video_segment_prompt:视频脚本用带说话人台词、不带「商品露出」行(露出靠尾句统一要求);
  正文去掉 {时长}s · 9:16 竖屏(时长/比例本就 API 参数 duration/ratio 传,不再重复写进文字)
- 尾句改为:电商带货短视频,商品露出清晰,节奏有转化感。不要字幕,不要背景音乐,但是要有音效,逼真的音效
- 迁移 0014 同步刷新 DB 里 video_segment 模板(DB 覆盖代码默认),admin「视频」卡随之更新

真渲染 v3·场1 与产品对齐版一致;后端测试通过。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
seaislee1209
2026-06-23 18:05:27 +08:00
co-authored by Claude Opus 4.8
parent c2c2dfdc77
commit ee842da19c
2 changed files with 57 additions and 8 deletions
@@ -0,0 +1,29 @@
from django.db import migrations
# 视频提示词模板更新:时长/比例靠 API 参数传(不写正文)、台词带说话人(在 build 里)、
# 尾句改风格 + 音效/字幕要求。DB 模板覆盖代码默认,故这里同步刷新 video_segment 那行。
NEW = (
"{设定}{分镜}【脚本】{脚本}\n"
"电商带货短视频,商品露出清晰,节奏有转化感。不要字幕,不要背景音乐,但是要有音效,逼真的音效。"
)
OLD = "{设定}{分镜}【脚本】{脚本}\n{时长}s · 9:16 竖屏电商带货短视频,镜头稳定,商品露出清晰,节奏有转化感。"
def _set(apps, template):
PromptTemplate = apps.get_model("ai", "PromptTemplate")
PromptTemplate.objects.filter(key="video_segment").update(template=template)
def forwards(apps, schema_editor):
_set(apps, NEW)
def backwards(apps, schema_editor):
_set(apps, OLD)
class Migration(migrations.Migration):
dependencies = [("ai", "0013_seed_prompt_templates")]
operations = [migrations.RunPython(forwards, backwards)]
+28 -8
View File
@@ -1094,16 +1094,33 @@ def _scene_context(project) -> str:
return " · ".join(parts)
def _segment_script_text(segment) -> str:
"""本镜脚本文本(画面 + 口播/旁白 + 商品露出),拼进故事板/视频提示词的【分镜脚本】。"""
def _segment_script_text(segment, entities=None, *, with_dialogue: bool = False, with_exposure: bool = True) -> str:
"""本镜脚本文本(画面 + 台词/口播 + 商品露出),拼进故事板/视频提示词的【分镜脚本】。
with_dialogue:有结构化对白时按「说话人:台词」原样放(说话人 id 用 entities 解析成名字),回退扁平 narration。
with_exposure:是否带「商品露出:…」一行。"""
parts = []
visual = (segment.visual_prompt or "").strip()
if visual:
parts.append(f"画面:{visual}")
narration = (segment.narration or "").strip()
if narration:
parts.append(f"口播/旁白:{narration}")
if getattr(segment, "product_exposure", ""):
dialogue = getattr(segment, "dialogue", None) if with_dialogue else None
name_by_id = {e.get("id"): (e.get("name") or "").strip() for e in (entities or []) if isinstance(e, dict)}
lines = []
if isinstance(dialogue, list):
for d in dialogue:
if not isinstance(d, dict):
continue
line = (d.get("line") or "").strip()
if not line:
continue
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))
else: # 无对白 → 回退扁平口播/旁白
narration = (segment.narration or "").strip()
if narration:
parts.append(f"口播/旁白:{narration}")
if with_exposure and getattr(segment, "product_exposure", ""):
parts.append(f"商品露出:{segment.product_exposure.strip()}")
return "\n".join(parts)
@@ -1141,12 +1158,15 @@ def build_video_segment_prompt(project, video_segment, scene, refs, user_prompt:
storyboard_idx = n
else:
setup_parts.append(f"@图{n}{r.get('label') or ''}({_ENTITY_TYPE_CN.get(r.get('type'), '参考')})")
script_text = _segment_script_text(scene) if scene is not None else ""
# 视频脚本:用带说话人的台词(原样照脚本),不带「商品露出」那行(露出靠模板尾句统一要求)
entities = (project.metadata or {}).get("script_entities", [])
script_text = _segment_script_text(scene, entities, with_dialogue=True, with_exposure=False) if scene is not None else ""
extra = (user_prompt or "").strip()
if extra:
script_text = (script_text + "\n" + extra).strip() if script_text else extra
# 正文可在 admin「提示词·视频」页改。占位符:{设定}=@图N点名、{分镜}=分镜图引用、{脚本}=本段脚本、{时长}。
default = "{设定}{分镜}【脚本】{脚本}\n{时长}s · 9:16 竖屏电商带货短视频,镜头稳定,商品露出清晰,节奏有转化感。"
# 时长/比例靠 API 参数传(duration/ratio),不写进正文;尾句给风格 + 音效/字幕要求。
default = "{设定}{分镜}【脚本】{脚本}\n电商带货短视频,商品露出清晰,节奏有转化感。不要字幕,不要背景音乐,但是要有音效,逼真的音效。"
rendered = render_prompt(
"video_segment", default,
设定=("【设定】" + "".join(setup_parts) + "\n" if setup_parts else ""),