完善二期清单

This commit is contained in:
Azmat@qq.com
2026-08-18 14:11:05 +08:00
parent d1ecb52125
commit dfe94a923e
55 changed files with 1754 additions and 137 deletions
@@ -0,0 +1,100 @@
"""套路模板:从一版脚本抽出可复用的结构,以及把模板还原成给脚本 agent 的指令。
设计前提:模板只承载**怎么讲**(镜数、每镜作用、节奏、商品怎么露、口播还是对白、转化写法),
不承载**讲什么**(旧商品的口播词)。换商品重跑时才不会把上一个品牌带出去。
"""
from __future__ import annotations
ROLE_FALLBACK = "叙述"
DELIVERY_DIALOGUE = "角色对白"
DELIVERY_NARRATION = "口播/旁白"
# 脚本 agent 落库存中文标签(「短剧」),设定卡 / 模板表用 ASCII key(drama)。两边都认。
# 人物同理:一期向导偶尔把中文写进 wizard.persona。
_PERSONA_KEYS = {"urban", "bestie", "ceo", "reviewer", "mom", "genz"}
_PERSONA_KEY_BY_LABEL = {
"都市白领女性": "urban",
"闺蜜种草": "bestie",
"总裁亲选": "ceo",
"专业测评师": "reviewer",
"专业测评": "reviewer",
"实用宝妈": "mom",
"学生党": "genz",
}
def coerce_template_combo(presentation_format: str, video_structure: str) -> tuple[str, str]:
from apps.ai.script_agent import combo_keys
return combo_keys(presentation_format or None, video_structure or None)
def coerce_persona(value: str) -> str:
raw = (value or "").strip()
if not raw:
return ""
if raw in _PERSONA_KEYS:
return raw
return _PERSONA_KEY_BY_LABEL.get(raw, raw)
def build_template_fields(*, project, script) -> dict:
"""从 ScriptVersion 抽出模板字段。不落库,调用方决定怎么存。"""
meta = script.metadata or {}
wizard = (project.metadata or {}).get("wizard") or {}
segments = list(script.segments.all())
outline = []
cta = ""
for index, segment in enumerate(segments):
role = (segment.role or "").strip() or ROLE_FALLBACK
outline.append(
{
"index": index,
"role": role,
"duration": int(segment.duration_seconds or 0),
"product_exposure": (segment.product_exposure or "").strip(),
"delivery": DELIVERY_DIALOGUE if segment.dialogue else DELIVERY_NARRATION,
}
)
if role == "CTA":
cta = (segment.narration or "").strip()
if not cta and segments:
cta = (segments[-1].narration or "").strip()
total = meta.get("total_duration")
if not isinstance(total, int) or total <= 0:
total = sum(item["duration"] for item in outline)
raw_format = str(meta.get("presentation_format") or wizard.get("presentation_format") or "")
raw_structure = str(meta.get("video_structure") or wizard.get("video_structure") or "")
fmt, structure = coerce_template_combo(raw_format, raw_structure) if (raw_format or raw_structure) else ("", "")
return {
"presentation_format": fmt,
"video_structure": structure,
"persona": coerce_persona(str(wizard.get("persona") or "")),
"total_duration": total,
"outline": outline,
"cta": cta[:500],
"scenes": [s for s in ((project.metadata or {}).get("scenes") or []) if isinstance(s, str)][:6],
}
def render_outline_text(template_fields: dict) -> str:
"""把模板渲染成人能读、模型也能照着做的一段中文,供前端预览与生成时拼 prompt 复用。"""
outline = template_fields.get("outline") or []
lines = []
for item in outline:
parts = [f"{int(item.get('index', 0)) + 1}", f"{item.get('duration', 0)}s", str(item.get("role") or ROLE_FALLBACK)]
exposure = str(item.get("product_exposure") or "").strip()
if exposure:
parts.append(f"商品{exposure}")
parts.append(str(item.get("delivery") or DELIVERY_NARRATION))
lines.append(" · ".join(parts))
body = "\n".join(lines)
cta = str(template_fields.get("cta") or "").strip()
if cta:
body += f"\n结尾转化写法参考:{cta}"
return body