style: auto-format with black + isort + prettier [skip ci-format-check]
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 / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
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
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 37s
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 / Validate - Migration (alembic) (pull_request) Successful in 48s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 1m11s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m44s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 1m58s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 1m58s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m27s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 3m42s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 4m37s
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Canary Release to Production (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
AI Code Review / AI Code Review (pull_request) Successful in 5m44s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 2m53s
CI/CD Pipeline / CI Gate (pull_request) Successful in 6s
Preview Deploy / Deploy Preview Environment (pull_request) Failing after 11m47s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 15s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 29s

This commit is contained in:
CI Bot
2026-08-07 11:33:42 +00:00
parent 33e977cc2a
commit 04b1e9522e
4 changed files with 29 additions and 33 deletions
@@ -123,7 +123,9 @@ class PlanGeneratorService:
if random_preview and self._asset_repo:
asset_durations = self._fetch_asset_durations(asset_ids)
self._distribute_assets(
clips, asset_ids, editing_mode,
clips,
asset_ids,
editing_mode,
random_selection=random_preview,
asset_durations=asset_durations,
)
@@ -219,7 +221,9 @@ class PlanGeneratorService:
委托给 plan_generator_utils.distribute_assets 纯函数。
"""
distribute_assets(
clips, asset_ids, editing_mode,
clips,
asset_ids,
editing_mode,
random_selection=random_selection,
asset_durations=asset_durations,
)
@@ -236,6 +240,6 @@ class PlanGeneratorService:
durations: dict[str, float] = {}
for asset_id in asset_ids:
asset = self._asset_repo.get(asset_id)
if asset and hasattr(asset, 'duration'):
if asset and hasattr(asset, "duration"):
durations[asset_id] = float(asset.duration or 0.0)
return durations
+1 -1
View File
@@ -129,7 +129,7 @@ class EditPlanClip:
def assign_asset(self, asset_id: str, *, start_time: float | None = None) -> None:
"""分配素材
Args:
asset_id: 素材 ID
start_time: 可选,素材播放起始时间(秒)。如果提供且在有效范围内,则设置;否则保持默认 0.0
+3 -3
View File
@@ -180,16 +180,16 @@ def _calc_random_start_time(
"""
if asset_durations is None:
return None
total_duration = asset_durations.get(asset_id)
if total_duration is None or total_duration <= 0:
return None
# 最大起始点 = 素材总时长 - 片段时长
max_start = max(0.0, total_duration - clip_duration)
if max_start <= 0:
return 0.0
return random.uniform(0.0, max_start)
+18 -26
View File
@@ -1,12 +1,13 @@
"""Tests for #1256: random asset selection and random start_time in preview generation."""
import pytest
from unittest.mock import MagicMock
import pytest
from packages.domain.edit_plan_clip import EditPlanClip
from packages.domain.plan_generator_utils import (
distribute_assets,
_calc_random_start_time,
distribute_assets,
)
@@ -67,6 +68,7 @@ class TestCalcRandomStartTime:
def test_returns_valid_random_start_time(self):
"""Returns start_time within valid range"""
import random
random.seed(42)
result = _calc_random_start_time("asset1", 5.0, {"asset1": 30.0})
# max_start = 30.0 - 5.0 = 25.0
@@ -83,42 +85,34 @@ class TestDistributeAssetsRandom:
def test_one_take_without_random(self):
"""ONE_TAKE without random: assets assigned in order"""
clips = [
EditPlanClip.create(plan_id="p1", clip_type="main", order=i, duration=5.0)
for i in range(3)
]
clips = [EditPlanClip.create(plan_id="p1", clip_type="main", order=i, duration=5.0) for i in range(3)]
assets = ["a1", "a2", "a3", "a4"]
distribute_assets(clips, assets, "one_take", random_selection=False)
assert clips[0].asset_id == "a1"
assert clips[1].asset_id == "a2"
assert clips[2].asset_id == "a3"
def test_one_take_with_random(self):
"""ONE_TAKE with random: assets assigned in random order"""
clips = [
EditPlanClip.create(plan_id="p1", clip_type="main", order=i, duration=5.0)
for i in range(3)
]
clips = [EditPlanClip.create(plan_id="p1", clip_type="main", order=i, duration=5.0) for i in range(3)]
assets = ["a1", "a2", "a3", "a4"]
# Run multiple times to verify randomness
results = set()
for _ in range(10):
distribute_assets(clips, assets, "one_take", random_selection=True)
results.add(tuple(c.asset_id for c in clips))
# Should have multiple different orderings
assert len(results) > 1, "Random selection should produce different orderings"
def test_with_asset_durations_sets_start_time(self):
"""distribute_assets with asset_durations sets random start_time"""
clips = [
EditPlanClip.create(plan_id="p1", clip_type="main", order=0, duration=5.0)
]
clips = [EditPlanClip.create(plan_id="p1", clip_type="main", order=0, duration=5.0)]
durations = {"a1": 30.0}
distribute_assets(clips, ["a1"], "one_take", asset_durations=durations)
assert clips[0].asset_id == "a1"
# start_time should be set (0 <= start_time <= 25.0)
assert 0.0 <= clips[0].start_time <= 25.0
@@ -132,7 +126,7 @@ class TestDistributeAssetsRandom:
]
assets = ["a1", "a2", "a3"]
distribute_assets(clips, assets, "pip", random_selection=True)
# All clips should have assets assigned
assert clips[0].asset_id
assert clips[1].asset_id
@@ -148,7 +142,7 @@ class TestDistributeAssetsRandom:
assets = ["a1", "a2", "a3"]
durations = {"a1": 30.0, "a2": 25.0, "a3": 20.0}
distribute_assets(clips, assets, "voice_pip", random_selection=True, asset_durations=durations)
# All clips should have assets and start_times
for clip in clips:
assert clip.asset_id
@@ -156,13 +150,11 @@ class TestDistributeAssetsRandom:
def test_does_not_modify_original_list(self):
"""random_selection should not modify the original asset_ids list"""
clips = [
EditPlanClip.create(plan_id="p1", clip_type="main", order=0, duration=5.0)
]
clips = [EditPlanClip.create(plan_id="p1", clip_type="main", order=0, duration=5.0)]
assets = ["a1", "a2", "a3"]
original = list(assets)
distribute_assets(clips, assets, "one_take", random_selection=True)
assert assets == original, "Original asset_ids list should not be modified"
@@ -176,7 +168,7 @@ class TestDistributeAssetsBackwardCompatible:
EditPlanClip.create(plan_id="p1", clip_type="main", order=1, duration=5.0),
]
distribute_assets(clips, ["a1", "a2"], "one_take")
assert clips[0].asset_id == "a1"
assert clips[1].asset_id == "a2"
assert clips[0].start_time == 0.0 # default
@@ -190,7 +182,7 @@ class TestDistributeAssetsBackwardCompatible:
]
distribute_assets(clips, ["a1"], mode)
assert clips[0].asset_id == "a1"
# pip: main + overlay
clips = [
EditPlanClip.create(plan_id="p1", clip_type="main", order=0, duration=5.0),
@@ -198,7 +190,7 @@ class TestDistributeAssetsBackwardCompatible:
]
distribute_assets(clips, ["a1"], "pip")
assert clips[0].asset_id == "a1" # first asset goes to main
# voice_pip: background + corner_voice + b_roll
clips = [
EditPlanClip.create(plan_id="p1", clip_type="background", order=0, duration=5.0),