style: auto-format with black + isort + ruff + prettier [skip ci-format-check]
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 2s
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 2s
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / PR Build Web Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been skipped
PR Automation / Auto Approve on CI Green (pull_request) Successful in 2m55s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 2m25s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 2m23s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 4m53s
AI Code Review / AI Code Review (pull_request) Successful in 6m49s
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 10m48s
CI/CD Pipeline / Validate - Style (pull_request) Successful in 32m25s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 33m43s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Successful in 37m2s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 39m23s
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled

This commit is contained in:
CI Bot
2026-09-20 14:36:48 +00:00
parent 1584a85bab
commit fdad465d3a
3 changed files with 67 additions and 48 deletions
+2 -1
View File
@@ -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(
+1 -1
View File
@@ -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。
+64 -46
View File
@@ -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] == "第一行"