diff --git a/apps/api/app/schemas/ai_avatar_render.py b/apps/api/app/schemas/ai_avatar_render.py index ed362efe8..921269cb4 100644 --- a/apps/api/app/schemas/ai_avatar_render.py +++ b/apps/api/app/schemas/ai_avatar_render.py @@ -53,7 +53,8 @@ class CreateAiAvatarRenderRequest(BaseModel): script_id: str = Field("", description="文案 ID(选自文案库时传;手动输入文案直生场景可留空)") b_roll_segments: list[BRollSegment] = Field(default_factory=list, description="B-roll 片段列表") title_config: dict[str, Any] = Field( - default_factory=dict, description="标题配置(可含 title_image_dataurl:前端 Canvas 渲染的标题 PNG dataURL;含 line_overrides 逐行样式)" + default_factory=dict, + description="标题配置(可含 title_image_dataurl:前端 Canvas 渲染的标题 PNG dataURL;含 line_overrides 逐行样式)", ) cover_config: dict[str, Any] = Field(default_factory=dict, description="封面配置") cover_title_config: dict[str, Any] = Field( diff --git a/packages/domain/subtitle_style.py b/packages/domain/subtitle_style.py index e3cf7236d..59f2fcb6b 100755 --- a/packages/domain/subtitle_style.py +++ b/packages/domain/subtitle_style.py @@ -264,7 +264,7 @@ class SubtitleStyle: return f"&H{alpha_hex}{color_bgr}" def build_line_override_tag(self, line_index: int, total_lines: int) -> str: - """按 line_overrides 配置为指定行构造 ASS 内联 override 标签 {\c&HBBGGRR&...}. + r"""按 line_overrides 配置为指定行构造 ASS 内联 override 标签 {\c&HBBGGRR&...}. 仅返回大括号包裹的 override 标签串;调用方拼到该行文本前即可。 未配置该覆盖项时返回空串。缺省字段继承主样式,不生成对应 tag。 diff --git a/tests/unit/test_subtitle_style.py b/tests/unit/test_subtitle_style.py index eb018ffa8..a8135887a 100755 --- a/tests/unit/test_subtitle_style.py +++ b/tests/unit/test_subtitle_style.py @@ -418,9 +418,11 @@ class TestBuildLineOverrideTag: assert style.build_line_override_tag(0, 2) == "" def test_negative_index_resolves_from_end(self): - style = SubtitleStyle.from_dict({ - "line_overrides": [{"line_index": -1, "color": "#FF0000", "font_size": 48}], - }) + style = SubtitleStyle.from_dict( + { + "line_overrides": [{"line_index": -1, "color": "#FF0000", "font_size": 48}], + } + ) tag = style.build_line_override_tag(-1, 3) # last line, 3 lines total assert tag assert "\\c&H000000FF" in tag # red @@ -428,23 +430,29 @@ class TestBuildLineOverrideTag: assert tag.startswith("{") and tag.endswith("}") def test_color_override(self): - style = SubtitleStyle.from_dict({ - "line_overrides": [{"line_index": 0, "color": "#00FF00"}], - }) + style = SubtitleStyle.from_dict( + { + "line_overrides": [{"line_index": 0, "color": "#00FF00"}], + } + ) tag = style.build_line_override_tag(0, 1) assert "\\c&H0000FF00" in tag # green = 00FF00 → bgr 00FF00 def test_font_size_override(self): - style = SubtitleStyle.from_dict({ - "line_overrides": [{"line_index": 0, "font_size": 60}], - }) + style = SubtitleStyle.from_dict( + { + "line_overrides": [{"line_index": 0, "font_size": 60}], + } + ) tag = style.build_line_override_tag(0, 1) assert "\\fs60" in tag def test_font_override(self): - style = SubtitleStyle.from_dict({ - "line_overrides": [{"line_index": 0, "font": "优设标题黑"}], - }) + style = SubtitleStyle.from_dict( + { + "line_overrides": [{"line_index": 0, "font": "优设标题黑"}], + } + ) tag = style.build_line_override_tag(0, 1) assert "\\fn优设标题黑" in tag @@ -461,42 +469,48 @@ class TestBuildLineOverrideTag: assert "\\i0" in style_noitalic.build_line_override_tag(0, 1) def test_stroke_override(self): - style = SubtitleStyle.from_dict({ - "line_overrides": [ - {"line_index": 0, "stroke_color": "#0000FF", "stroke_width": 3.0} - ], - }) + style = SubtitleStyle.from_dict( + { + "line_overrides": [{"line_index": 0, "stroke_color": "#0000FF", "stroke_width": 3.0}], + } + ) tag = style.build_line_override_tag(0, 1) assert "\\3c&H00FF0000" in tag # blue bgr assert "\\bord3" in tag def test_shadow_override(self): - style = SubtitleStyle.from_dict({ - "line_overrides": [ - {"line_index": 0, "shadow_color": "#000000", "shadow_offset_x": 2, "shadow_offset_y": 3} - ], - }) + style = SubtitleStyle.from_dict( + { + "line_overrides": [ + {"line_index": 0, "shadow_color": "#000000", "shadow_offset_x": 2, "shadow_offset_y": 3} + ], + } + ) tag = style.build_line_override_tag(0, 1) assert "\\4c&H00000000" in tag # black assert "\\xshad2" in tag assert "\\yshad3" in tag def test_full_combo(self): - style = SubtitleStyle.from_dict({ - "line_overrides": [{ - "line_index": 0, - "color": "#FF0000", - "font_size": 72, - "font": "抖音美好体", - "bold": True, - "italic": False, - "stroke_color": "#FFFFFF", - "stroke_width": 2, - "shadow_color": "#000000", - "shadow_offset_x": 0, - "shadow_offset_y": 4, - }], - }) + style = SubtitleStyle.from_dict( + { + "line_overrides": [ + { + "line_index": 0, + "color": "#FF0000", + "font_size": 72, + "font": "抖音美好体", + "bold": True, + "italic": False, + "stroke_color": "#FFFFFF", + "stroke_width": 2, + "shadow_color": "#000000", + "shadow_offset_x": 0, + "shadow_offset_y": 4, + } + ], + } + ) tag = style.build_line_override_tag(0, 1) assert "\\c&H000000FF" in tag assert "\\fs72" in tag @@ -555,12 +569,14 @@ class TestApplyLineOverrides: assert lines[2] == "第三行" def test_multi_line_middle_and_last(self): - style = SubtitleStyle.from_dict({ - "line_overrides": [ - {"line_index": 0, "font_size": 72, "bold": True}, - {"line_index": -1, "color": "#00FF00", "font_size": 36}, - ], - }) + style = SubtitleStyle.from_dict( + { + "line_overrides": [ + {"line_index": 0, "font_size": 72, "bold": True}, + {"line_index": -1, "color": "#00FF00", "font_size": 36}, + ], + } + ) result = style.apply_line_overrides("主标题\\N副标题\\N脚注") lines = result.split("\\N") assert lines[0].startswith("{\\fs72\\b1}") @@ -569,9 +585,11 @@ class TestApplyLineOverrides: assert "\\fs36" in lines[2] def test_line_without_override_kept_verbatim(self): - style = SubtitleStyle.from_dict({ - "line_overrides": [{"line_index": 1, "bold": True}], - }) + style = SubtitleStyle.from_dict( + { + "line_overrides": [{"line_index": 1, "bold": True}], + } + ) result = style.apply_line_overrides("第一行\\N第二行") lines = result.split("\\N") assert lines[0] == "第一行"