fix(#633): 修复一键生成转场时长不生效 - _VirtualClip缺transition_duration字段
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 50s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 1m2s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 43s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 2m32s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 28s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 47s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 1m5s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Failing after 44s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 49s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 5m32s
AI Code Review / AI Code Review (pull_request) Successful in 9m45s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 12m43s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 1m9s
CI/CD Pipeline / Integration Tests (pull_request) Failing after 2m43s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 1m16s
CI/CD Pipeline / Production Browser E2E (pull_request) Failing after 1339h16m30s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 1339h19m34s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 1339h19m36s
CI/CD Pipeline / Deploy Production (pull_request) Failing after 1339h19m38s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 1339h23m2s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 1339h23m7s
CI/CD Pipeline / Build Production Web Image (pull_request) Failing after 1339h23m9s
CI/CD Pipeline / Build Production API Image (pull_request) Failing after 1339h23m11s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 1339h34m44s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 1339h34m44s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 1339h34m46s
CI/CD Pipeline / Build Production Worker Image (pull_request) Failing after 1339h55m25s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 1339h51m52s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 50s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 1m2s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 43s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 2m32s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 28s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 47s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 1m5s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Failing after 44s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 49s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 5m32s
AI Code Review / AI Code Review (pull_request) Successful in 9m45s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 12m43s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 1m9s
CI/CD Pipeline / Integration Tests (pull_request) Failing after 2m43s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 1m16s
CI/CD Pipeline / Production Browser E2E (pull_request) Failing after 1339h16m30s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 1339h19m34s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 1339h19m36s
CI/CD Pipeline / Deploy Production (pull_request) Failing after 1339h19m38s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 1339h23m2s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 1339h23m7s
CI/CD Pipeline / Build Production Web Image (pull_request) Failing after 1339h23m9s
CI/CD Pipeline / Build Production API Image (pull_request) Failing after 1339h23m11s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 1339h34m44s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 1339h34m44s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 1339h34m46s
CI/CD Pipeline / Build Production Worker Image (pull_request) Failing after 1339h55m25s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 1339h51m52s
根因:_VirtualClip 数据类缺少 transition_duration 字段,导致模板配置的自定义转场时长 在一键生成路径中被静默丢弃,永远使用引擎默认值 0.5s。 修复内容: 1. _VirtualClip 添加 transition_duration 字段(默认0.0,表示用全局默认值) 2. _apply_template_clip_effects 增加 transition_duration 从模板 config 映射 3. UnifiedRenderService 多段裁剪分支 ResolvedClip 构建补上 transition_duration (顺便补上同分支遗漏的 playback_speed) 4. 补充 3 个单测用例:正常映射/cut转场忽略/无效值安全跳过
This commit is contained in:
@@ -1304,6 +1304,8 @@ class UnifiedRenderService:
|
||||
start_time=seg_start,
|
||||
duration=seg_duration,
|
||||
transition_effect=clip.transition_effect or "cut",
|
||||
transition_duration=getattr(clip, "transition_duration", 0.0) or 0.0,
|
||||
playback_speed=getattr(clip, "playback_speed", 1.0) or 1.0,
|
||||
config={**clip_config, "_segment_id": seg.segment_id},
|
||||
actual_duration=actual_duration,
|
||||
trim_config=seg.trim,
|
||||
|
||||
Regular → Executable
+12
-1
@@ -171,6 +171,7 @@ class _VirtualClip:
|
||||
start_time: float = 0.0
|
||||
duration: float = 0.0
|
||||
transition_effect: str = "cut"
|
||||
transition_duration: float = 0.0 # 0 表示使用全局默认值
|
||||
playback_speed: float = 1.0
|
||||
status: str = "ready"
|
||||
config: dict[str, Any] = field(default_factory=dict)
|
||||
@@ -279,7 +280,7 @@ def _apply_template_clip_effects(
|
||||
cfg_idx = min(i, len(main_configs) - 1)
|
||||
template_cfg = main_configs[cfg_idx]
|
||||
|
||||
# 1. 转场效果
|
||||
# 1. 转场效果 + 时长
|
||||
transition = (
|
||||
template_cfg.transition_effect.value
|
||||
if hasattr(template_cfg.transition_effect, "value")
|
||||
@@ -287,6 +288,16 @@ def _apply_template_clip_effects(
|
||||
)
|
||||
if transition and transition != "cut":
|
||||
clip.transition_effect = transition
|
||||
# 同步转场时长(模板 clip_config 里的 transition_duration)
|
||||
tpl_cfg = template_cfg.config or {}
|
||||
tpl_duration = tpl_cfg.get("transition_duration")
|
||||
if tpl_duration:
|
||||
try:
|
||||
dur_val = float(tpl_duration)
|
||||
if dur_val > 0:
|
||||
clip.transition_duration = dur_val
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
|
||||
# 2. clip 级效果配置(滤镜、调速等)
|
||||
template_clip_config = template_cfg.config or {}
|
||||
|
||||
@@ -309,6 +309,8 @@ class TestTemplateClipEffectMapping:
|
||||
asset_id: str = f"asset_{idx}"
|
||||
duration: float = 5.0
|
||||
transition_effect: str = "cut"
|
||||
transition_duration: float = 0.0
|
||||
playback_speed: float = 1.0
|
||||
config: dict = field(default_factory=dict)
|
||||
|
||||
return FakeClip(config=config or {})
|
||||
@@ -396,6 +398,54 @@ class TestTemplateClipEffectMapping:
|
||||
# 模板是 cut 时,保留原有值(避免无意义覆盖)
|
||||
assert clips[0].transition_effect == "fade"
|
||||
|
||||
def test_transition_duration_mapped(self):
|
||||
"""转场时长(transition_duration)从模板 config 正确映射到 clip."""
|
||||
from worker_app.tasks.generation import _apply_template_clip_effects
|
||||
|
||||
clips = [self._make_virtual_clip(i) for i in range(3)]
|
||||
clip_configs = [
|
||||
self._make_template_clip_config("main", transition="fade", config={"transition_duration": 0.8}),
|
||||
self._make_template_clip_config("main", transition="dissolve", config={"transition_duration": 1.2}),
|
||||
]
|
||||
|
||||
_apply_template_clip_effects(clips, clip_configs, "one_take")
|
||||
|
||||
# 前两个按顺序映射,第三个复用最后一个
|
||||
assert clips[0].transition_effect == "fade"
|
||||
assert clips[0].transition_duration == 0.8
|
||||
assert clips[1].transition_effect == "dissolve"
|
||||
assert clips[1].transition_duration == 1.2
|
||||
assert clips[2].transition_effect == "dissolve"
|
||||
assert clips[2].transition_duration == 1.2
|
||||
|
||||
def test_transition_duration_ignored_for_cut(self):
|
||||
"""模板转场为 cut 时,transition_duration 不生效(保持默认0)."""
|
||||
from worker_app.tasks.generation import _apply_template_clip_effects
|
||||
|
||||
clips = [self._make_virtual_clip(0)]
|
||||
clip_configs = [
|
||||
self._make_template_clip_config("main", transition="cut", config={"transition_duration": 0.5}),
|
||||
]
|
||||
|
||||
_apply_template_clip_effects(clips, clip_configs, "one_take")
|
||||
|
||||
# cut 转场不映射,transition_duration 也不应用
|
||||
assert clips[0].transition_duration == 0.0
|
||||
|
||||
def test_transition_duration_invalid_value_skipped(self):
|
||||
"""transition_duration 为无效值时安全跳过."""
|
||||
from worker_app.tasks.generation import _apply_template_clip_effects
|
||||
|
||||
clips = [self._make_virtual_clip(0)]
|
||||
clip_configs = [
|
||||
self._make_template_clip_config("main", transition="fade", config={"transition_duration": "abc"}),
|
||||
]
|
||||
|
||||
_apply_template_clip_effects(clips, clip_configs, "one_take")
|
||||
|
||||
assert clips[0].transition_effect == "fade"
|
||||
assert clips[0].transition_duration == 0.0 # 无效值保持默认
|
||||
|
||||
def test_intro_outro_extracted(self):
|
||||
"""intro/outro 类型 clip_config 正确提取为 plan 级 intro_outro 配置."""
|
||||
from worker_app.tasks.generation import _extract_intro_outro_from_clip_configs
|
||||
|
||||
Reference in New Issue
Block a user