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
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:
@@ -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(
|
||||
|
||||
@@ -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。
|
||||
|
||||
@@ -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] == "第一行"
|
||||
|
||||
Reference in New Issue
Block a user