fix(ass): 字段名归一化(font_size/font_color→size/color) + 移除字号上限36 #1501
@@ -247,6 +247,12 @@ def build_ass_content(
|
||||
title_config = title_config or {}
|
||||
subtitle_config = subtitle_config or {}
|
||||
|
||||
# ── 字段名归一化:前端传 font_size/font_color,内部用 size/color ──
|
||||
if "font_size" in title_config and "size" not in title_config:
|
||||
title_config["size"] = title_config["font_size"]
|
||||
if "font_color" in title_config and "color" not in title_config:
|
||||
title_config["color"] = title_config["font_color"]
|
||||
|
||||
# ── 兼容前端简化格式:stroke/shadow 为 boolean 时,转换为标准 dict ──
|
||||
# 前端 TitleSettings 发送 stroke=true/false, shadow=true/false
|
||||
# 后端 build_ass_style 期望 stroke={enabled, color, width}, shadow={enabled, blur, offset_x, offset_y}
|
||||
@@ -296,7 +302,7 @@ def build_ass_content(
|
||||
build_ass_style(
|
||||
"TitleStyle",
|
||||
font_name=title_config.get("font", "思源黑体"),
|
||||
font_size=min(int(title_config.get("size", 36)), 36),
|
||||
font_size=int(title_config.get("size", 36)),
|
||||
primary_color=title_color,
|
||||
outline_color=stroke_color,
|
||||
outline_width=stroke_width,
|
||||
@@ -313,7 +319,7 @@ def build_ass_content(
|
||||
|
||||
# 根据视频宽度和字号自动换行标题,防止超出画面
|
||||
# 先 escape 特殊字符,再插入换行符 \N,避免顺序颠倒导致 \N 被转义
|
||||
title_font_size = min(int(title_config.get("size", 36)), 36)
|
||||
title_font_size = int(title_config.get("size", 36))
|
||||
safe_title_text_raw = escape_ass_text(title_text)
|
||||
safe_title_text = _wrap_title_text(safe_title_text_raw, video_width, title_font_size)
|
||||
|
||||
|
||||
@@ -408,11 +408,53 @@ class TestBuildAssContent:
|
||||
title_text="T",
|
||||
title_config={"size": 72},
|
||||
)
|
||||
# 在TitleStyle行里查找字体大小
|
||||
# 在TitleStyle行里查找字体大小(字号上限已移除,72应原样保留)
|
||||
for line in result.split("\n"):
|
||||
if line.startswith("Style: TitleStyle"):
|
||||
parts = line.split(",")
|
||||
assert parts[2] == "36"
|
||||
assert parts[2] == "72"
|
||||
break
|
||||
|
||||
def test_title_font_size_frontend_field_alias(self):
|
||||
"""前端传 font_size 应归一化为内部 size 字段。"""
|
||||
result = build_ass_content(
|
||||
video_width=1920,
|
||||
video_height=1080,
|
||||
video_duration=5.0,
|
||||
title_text="T",
|
||||
title_config={"font_size": 48},
|
||||
)
|
||||
for line in result.split("\n"):
|
||||
if line.startswith("Style: TitleStyle"):
|
||||
parts = line.split(",")
|
||||
assert parts[2] == "48"
|
||||
break
|
||||
|
||||
def test_title_font_color_frontend_field_alias(self):
|
||||
"""前端传 font_color 应归一化为内部 color 字段。"""
|
||||
result = build_ass_content(
|
||||
video_width=1920,
|
||||
video_height=1080,
|
||||
video_duration=5.0,
|
||||
title_text="T",
|
||||
title_config={"font_color": "#FF0000"},
|
||||
)
|
||||
# 红色 → &H0000FF
|
||||
assert "&H0000FF" in result
|
||||
|
||||
def test_title_size_takes_precedence_over_font_size(self):
|
||||
"""同时传 size 和 font_size 时,size 优先。"""
|
||||
result = build_ass_content(
|
||||
video_width=1920,
|
||||
video_height=1080,
|
||||
video_duration=5.0,
|
||||
title_text="T",
|
||||
title_config={"size": 56, "font_size": 28},
|
||||
)
|
||||
for line in result.split("\n"):
|
||||
if line.startswith("Style: TitleStyle"):
|
||||
parts = line.split(",")
|
||||
assert parts[2] == "56"
|
||||
break
|
||||
|
||||
def test_title_bold(self):
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""标题渲染前后端一致性测试。
|
||||
|
||||
验证 build_ass_content 生成的 ASS 样式参数与前端 drawTitleOnCanvas.ts 一致:
|
||||
- 字号上限 36px
|
||||
- 字号不再设置上限,由前端/调用方控制
|
||||
- 描边宽度 2px
|
||||
- 阴影 blur=4, offset=2
|
||||
- boolean stroke/shadow 自动转换
|
||||
@@ -16,8 +16,8 @@ sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "packages"))
|
||||
from domain.ass_subtitle_builder import build_ass_content, build_ass_style
|
||||
|
||||
|
||||
class TestFontSizeCap:
|
||||
"""字号上限应与前端 Math.min(settings.size, 36) 一致。"""
|
||||
class TestFontSize:
|
||||
"""字号处理:默认值与保留逻辑,不再做上限截断。"""
|
||||
|
||||
def test_default_font_size_is_36(self):
|
||||
"""无 size 字段时,默认字号应为 36。"""
|
||||
@@ -43,8 +43,8 @@ class TestFontSizeCap:
|
||||
)
|
||||
assert ",32," in content
|
||||
|
||||
def test_size_60_capped_at_36(self):
|
||||
"""size=60 应被 cap 到 36。"""
|
||||
def test_size_60_preserved(self):
|
||||
"""size=60 应原样保留(字号上限已移除)。"""
|
||||
config = {"size": 60}
|
||||
content = build_ass_content(
|
||||
video_width=1080,
|
||||
@@ -53,14 +53,40 @@ class TestFontSizeCap:
|
||||
title_text="测试标题",
|
||||
title_config=config,
|
||||
)
|
||||
# 解析 Style 行的 Fontsize 字段(第3个字段,索引2)
|
||||
style_line = [line for line in content.splitlines() if line.startswith("Style: TitleStyle")][0]
|
||||
fields = [f.strip() for f in style_line.split(",")]
|
||||
font_size = int(fields[2])
|
||||
assert font_size == 36, f"字号60应被cap到36, 实际={font_size}"
|
||||
assert font_size == 60, f"字号60应原样保留, 实际={font_size}"
|
||||
|
||||
def test_font_size_alias_normalized(self):
|
||||
"""前端传 font_size 应归一化为 size。"""
|
||||
config = {"font_size": 52}
|
||||
content = build_ass_content(
|
||||
video_width=1080,
|
||||
video_height=1920,
|
||||
video_duration=10.0,
|
||||
title_text="测试标题",
|
||||
title_config=config,
|
||||
)
|
||||
style_line = [line for line in content.splitlines() if line.startswith("Style: TitleStyle")][0]
|
||||
fields = [f.strip() for f in style_line.split(",")]
|
||||
assert fields[2] == "52", f"font_size=52 应归一化, 实际={fields[2]}"
|
||||
|
||||
def test_font_color_alias_normalized(self):
|
||||
"""前端传 font_color 应归一化为 color。"""
|
||||
config = {"font_color": "#00FF00"}
|
||||
content = build_ass_content(
|
||||
video_width=1080,
|
||||
video_height=1920,
|
||||
video_duration=10.0,
|
||||
title_text="测试标题",
|
||||
title_config=config,
|
||||
)
|
||||
# 绿色 → &H00FF00
|
||||
assert "&H00FF00" in content
|
||||
|
||||
def test_size_24_preserved(self):
|
||||
"""size=24 应原样使用(小于36,不cap)。"""
|
||||
"""size=24 应原样使用。"""
|
||||
config = {"size": 24}
|
||||
content = build_ass_content(
|
||||
video_width=1080,
|
||||
@@ -203,7 +229,7 @@ class TestFullStyleConsistency:
|
||||
|
||||
# Fontname
|
||||
assert fields[1] == "思源黑体"
|
||||
# Fontsize = 28 (小于36,不cap)
|
||||
# Fontsize = 28
|
||||
assert fields[2] == "28"
|
||||
# Bold = -1 (True)
|
||||
assert fields[7] == "-1"
|
||||
|
||||
Reference in New Issue
Block a user