fix: 移除 asset_durations 的 random_preview 条件限制
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m52s
AI Code Review / AI Code Review (pull_request) Successful in 1m57s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m59s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m54s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 32s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 1m39s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 2m3s
CI/CD Pipeline / Validate - Code Quality (pull_request) Failing after 2m58s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 59s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 1m46s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 3m54s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 3m5s
CI/CD Pipeline / CI Gate (pull_request) Failing after 10s
CI/CD Pipeline / Production Browser E2E (pull_request) Failing after 501h13m28s
CI/CD Pipeline / Deploy Production (pull_request) Failing after 501h13m29s
CI/CD Pipeline / Build Production Worker Image (pull_request) Failing after 501h13m30s
CI/CD Pipeline / Build Production Web Image (pull_request) Failing after 501h13m31s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 501h16m17s
CI/CD Pipeline / PR Build Web Image (pull_request) Failing after 501h16m37s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 501h17m26s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 501h18m6s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 501h18m10s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 501h16m15s
CI/CD Pipeline / Canary Release to Production (pull_request) Failing after 501h47m26s
CI/CD Pipeline / Build Production API Image (pull_request) Failing after 501h47m28s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 501h50m17s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 501h52m4s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 501h51m24s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 501h50m15s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m52s
AI Code Review / AI Code Review (pull_request) Successful in 1m57s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m59s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m54s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 32s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 1m39s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 2m3s
CI/CD Pipeline / Validate - Code Quality (pull_request) Failing after 2m58s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 59s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 1m46s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 3m54s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 3m5s
CI/CD Pipeline / CI Gate (pull_request) Failing after 10s
CI/CD Pipeline / Production Browser E2E (pull_request) Failing after 501h13m28s
CI/CD Pipeline / Deploy Production (pull_request) Failing after 501h13m29s
CI/CD Pipeline / Build Production Worker Image (pull_request) Failing after 501h13m30s
CI/CD Pipeline / Build Production Web Image (pull_request) Failing after 501h13m31s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 501h16m17s
CI/CD Pipeline / PR Build Web Image (pull_request) Failing after 501h16m37s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 501h17m26s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 501h18m6s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 501h18m10s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 501h16m15s
CI/CD Pipeline / Canary Release to Production (pull_request) Failing after 501h47m26s
CI/CD Pipeline / Build Production API Image (pull_request) Failing after 501h47m28s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 501h50m17s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 501h52m4s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 501h51m24s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 501h50m15s
问题:asset_durations 仅在 random_preview=True 时传入 distribute_assets(), 导致正常创建 plan 时 _calc_random_start_time() 永远走不到随机逻辑, 所有 clip 的 start_time 固定为 0。 修复:移除 random_preview 条件,只要 _asset_repo 存在就始终获取 asset_durations,使素材片段随机起始时间对所有 plan 生效。 新增 3 个测试验证: - random_preview=False 时也获取 asset_durations - random_preview=True 时行为不变 - _asset_repo 为 None 时 asset_durations 为 None
This commit is contained in:
@@ -118,9 +118,9 @@ class PlanGeneratorService:
|
||||
|
||||
# 4. 按 editing_mode 分配素材
|
||||
if asset_ids:
|
||||
# 如果是随机预览模式,获取素材时长信息
|
||||
# 获取素材时长信息,用于随机起始时间
|
||||
asset_durations = None
|
||||
if random_preview and self._asset_repo:
|
||||
if self._asset_repo:
|
||||
asset_durations = self._fetch_asset_durations(asset_ids)
|
||||
self._distribute_assets(
|
||||
clips,
|
||||
|
||||
@@ -886,3 +886,134 @@ class TestTemplateConfigPropagation:
|
||||
assert clip_cfg.get("speed_ratio") == 1.2
|
||||
assert clip_cfg.get("name") == "开场"
|
||||
assert clip_cfg.get("custom_field") == "value"
|
||||
|
||||
|
||||
class TestAssetDurationsAlwaysFetched:
|
||||
"""验证 asset_durations 不再受 random_preview 条件限制。
|
||||
|
||||
修复前:asset_durations 仅在 random_preview=True 时传入 distribute_assets
|
||||
修复后:只要 _asset_repo 存在,就始终获取 asset_durations
|
||||
"""
|
||||
|
||||
def _make_service_with_asset_repo(self):
|
||||
"""创建带 mock asset_repo 的 PlanGeneratorService"""
|
||||
from apps.api.app.services.plan_generator_service import PlanGeneratorService
|
||||
|
||||
plan_repo = StubEditPlanRepository()
|
||||
clip_repo = StubEditPlanClipRepository()
|
||||
|
||||
# mock asset_repo: 返回带 duration 的素材
|
||||
asset_repo = MagicMock()
|
||||
|
||||
def fake_get(asset_id):
|
||||
mock_asset = MagicMock()
|
||||
mock_asset.duration = 30.0 # 每个素材 30 秒
|
||||
return mock_asset
|
||||
|
||||
asset_repo.get = MagicMock(side_effect=fake_get)
|
||||
|
||||
with (
|
||||
patch(
|
||||
"apps.api.app.services.plan_generator_service.SQLAlchemyEditPlanRepository",
|
||||
return_value=plan_repo,
|
||||
),
|
||||
patch(
|
||||
"apps.api.app.services.plan_generator_service.SQLAlchemyEditPlanClipRepository",
|
||||
return_value=clip_repo,
|
||||
),
|
||||
):
|
||||
db = MagicMock()
|
||||
svc = PlanGeneratorService(db, asset_repo=asset_repo)
|
||||
svc._plan_repo = plan_repo
|
||||
svc._clip_repo = clip_repo
|
||||
|
||||
return svc, asset_repo
|
||||
|
||||
def test_asset_durations_fetched_without_random_preview(self):
|
||||
"""random_preview=False 时也应获取 asset_durations"""
|
||||
svc, asset_repo = self._make_service_with_asset_repo()
|
||||
|
||||
template = _make_template("one_take")
|
||||
clip_configs = _make_clip_configs(
|
||||
template_id=template.id,
|
||||
specs=[{"clip_type": ClipType.MAIN, "order": 0, "min_duration": 3.0, "max_duration": 5.0}],
|
||||
)
|
||||
|
||||
# patch distribute_assets 以捕获传入的参数
|
||||
with patch(
|
||||
"apps.api.app.services.plan_generator_service.distribute_assets"
|
||||
) as mock_distribute:
|
||||
svc.generate_from_template(
|
||||
template=template,
|
||||
clip_configs=clip_configs,
|
||||
asset_ids=["a1", "a2"],
|
||||
random_preview=False, # 关键:非随机预览模式
|
||||
)
|
||||
|
||||
# 验证 asset_durations 被传入(不是 None)
|
||||
mock_distribute.assert_called_once()
|
||||
call_kwargs = mock_distribute.call_args
|
||||
asset_durations = call_kwargs.kwargs.get(
|
||||
"asset_durations", call_kwargs[1].get("asset_durations")
|
||||
)
|
||||
assert asset_durations is not None, (
|
||||
"asset_durations should be fetched even when random_preview=False"
|
||||
)
|
||||
assert "a1" in asset_durations
|
||||
assert "a2" in asset_durations
|
||||
assert asset_durations["a1"] == 30.0
|
||||
|
||||
# 验证 asset_repo.get 被调用(说明 _fetch_asset_durations 执行了)
|
||||
assert asset_repo.get.call_count >= 2
|
||||
|
||||
def test_asset_durations_fetched_with_random_preview(self):
|
||||
"""random_preview=True 时仍正常获取 asset_durations(行为不变)"""
|
||||
svc, asset_repo = self._make_service_with_asset_repo()
|
||||
|
||||
template = _make_template("one_take")
|
||||
clip_configs = _make_clip_configs(
|
||||
template_id=template.id,
|
||||
specs=[{"clip_type": ClipType.MAIN, "order": 0, "min_duration": 3.0, "max_duration": 5.0}],
|
||||
)
|
||||
|
||||
with patch(
|
||||
"apps.api.app.services.plan_generator_service.distribute_assets"
|
||||
) as mock_distribute:
|
||||
svc.generate_from_template(
|
||||
template=template,
|
||||
clip_configs=clip_configs,
|
||||
asset_ids=["a1"],
|
||||
random_preview=True,
|
||||
)
|
||||
|
||||
call_kwargs = mock_distribute.call_args
|
||||
asset_durations = call_kwargs.kwargs.get(
|
||||
"asset_durations", call_kwargs[1].get("asset_durations")
|
||||
)
|
||||
assert asset_durations is not None
|
||||
assert "a1" in asset_durations
|
||||
|
||||
def test_no_asset_repo_means_no_durations(self):
|
||||
"""_asset_repo 为 None 时 asset_durations 应为 None"""
|
||||
svc, _, _ = _make_generator() # 默认不带 asset_repo
|
||||
|
||||
template = _make_template("one_take")
|
||||
clip_configs = _make_clip_configs(
|
||||
template_id=template.id,
|
||||
specs=[{"clip_type": ClipType.MAIN, "order": 0, "min_duration": 3.0, "max_duration": 5.0}],
|
||||
)
|
||||
|
||||
with patch(
|
||||
"apps.api.app.services.plan_generator_service.distribute_assets"
|
||||
) as mock_distribute:
|
||||
svc.generate_from_template(
|
||||
template=template,
|
||||
clip_configs=clip_configs,
|
||||
asset_ids=["a1"],
|
||||
)
|
||||
|
||||
call_kwargs = mock_distribute.call_args
|
||||
asset_durations = call_kwargs.kwargs.get(
|
||||
"asset_durations", call_kwargs[1].get("asset_durations")
|
||||
)
|
||||
assert asset_durations is None
|
||||
|
||||
Reference in New Issue
Block a user