e83a0b86f8
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (push) Successful in 2s
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / PR Build Worker Image (push) Has been skipped
CI/CD Pipeline / PR Build Web Image (push) Has been skipped
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 / Check if frontend-only change (pull_request) Successful in 2s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 3s
CI/CD Pipeline / PR Build API Image (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / Validate - Style (pull_request) Has been skipped
CI/CD Pipeline / Validate - Security (pull_request) Has been skipped
CI/CD Pipeline / Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been skipped
CI/CD Pipeline / Integration Tests (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 / Check push changed paths (push) Successful in 9s
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
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 / PR Build API Image (pull_request) Successful in 35s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 38s
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 / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / Canary Release to Production (pull_request) Has been skipped
CI/CD Pipeline / Build Staging API Image (push) Successful in 58s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 55s
CI/CD Pipeline / Validate - Python (mypy + alembic) (push) Successful in 2m5s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 2m18s
CI/CD Pipeline / Validate - Style (push) Successful in 2m31s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 2m30s
CI/CD Pipeline / Integration Tests (push) Successful in 2m35s
CI/CD Pipeline / CI Gate (pull_request) Successful in 1s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 2m7s
CI/CD Pipeline / Retag skipped Staging API Image (push) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (push) Has been skipped
CI/CD Pipeline / Retag skipped Staging Web Image (push) Has been skipped
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m15s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 1m30s
CI/CD Pipeline / Validate - Security (push) Successful in 4m34s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 1m37s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 5m54s
AI Code Review / AI Code Review (pull_request) Successful in 6m19s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 3m57s
CI/CD Pipeline / Unit Tests (push) Successful in 8m33s
CI/CD Pipeline / Build Production API Image (push) Has been skipped
CI/CD Pipeline / Build Production Web Image (push) Has been skipped
CI/CD Pipeline / Build Production Worker Image (push) Has been skipped
CI/CD Pipeline / CI Gate (push) Has been skipped
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
CI/CD Pipeline / Staging E2E Tests (push) Failing after 4m48s
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com> Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
99 lines
3.4 KiB
Python
99 lines
3.4 KiB
Python
"""Tests for duration compensation when source video is shorter than configured duration."""
|
||
|
||
from pathlib import Path
|
||
|
||
import pytest
|
||
|
||
from apps.worker.video_processing.unified_render_service import ResolvedClip, UnifiedRenderService
|
||
|
||
|
||
class TestClipEffectiveDurationWithSpeedCompensation:
|
||
"""Test _clip_effective_duration handles speed < 1 correctly."""
|
||
|
||
def test_normal_speed_returns_target_duration(self):
|
||
"""#1749:speed=1.0 时 effective_duration 始终为目标段长 duration。
|
||
|
||
素材短于段长不再慢放补偿,差值由末帧冻结 tpad / 音频 apad 铺满。
|
||
"""
|
||
clip = ResolvedClip(
|
||
clip_id="c1",
|
||
asset_id="a1",
|
||
local_path=Path("/tmp/fake.mp4"),
|
||
clip_type="main",
|
||
order=0,
|
||
start_time=0.0,
|
||
duration=4.0,
|
||
actual_duration=3.0, # shorter than configured
|
||
playback_speed=1.0,
|
||
)
|
||
assert UnifiedRenderService._clip_effective_duration(clip) == 4.0
|
||
|
||
def test_explicit_speed_does_not_change_effective_duration(self):
|
||
"""#1749:effective_duration 只认目标段长,与 playback_speed 无关(慢放补偿已删除)。"""
|
||
clip = ResolvedClip(
|
||
clip_id="c1",
|
||
asset_id="a1",
|
||
local_path=Path("/tmp/fake.mp4"),
|
||
clip_type="main",
|
||
order=0,
|
||
start_time=0.0,
|
||
duration=4.0,
|
||
actual_duration=3.0, # shorter than configured
|
||
playback_speed=0.75,
|
||
)
|
||
assert UnifiedRenderService._clip_effective_duration(clip) == 4.0
|
||
|
||
def test_zero_actual_duration_returns_configured(self):
|
||
"""When actual_duration=0, effective_duration = configured duration."""
|
||
clip = ResolvedClip(
|
||
clip_id="c1",
|
||
asset_id="a1",
|
||
local_path=Path("/tmp/fake.mp4"),
|
||
clip_type="main",
|
||
order=0,
|
||
start_time=0.0,
|
||
duration=4.0,
|
||
actual_duration=0.0,
|
||
playback_speed=1.0,
|
||
)
|
||
assert UnifiedRenderService._clip_effective_duration(clip) == 4.0
|
||
|
||
def test_compensated_speed_with_actual_zero(self):
|
||
"""When speed < 1 and actual=0, still returns configured duration."""
|
||
clip = ResolvedClip(
|
||
clip_id="c1",
|
||
asset_id="a1",
|
||
local_path=Path("/tmp/fake.mp4"),
|
||
clip_type="main",
|
||
order=0,
|
||
start_time=0.0,
|
||
duration=4.0,
|
||
actual_duration=0.0,
|
||
playback_speed=0.5,
|
||
)
|
||
assert UnifiedRenderService._clip_effective_duration(clip) == 4.0
|
||
|
||
|
||
class TestClipAdjustedDurationWithSpeedCompensation:
|
||
"""Test _clip_adjusted_duration accounts for compensated speed."""
|
||
|
||
def test_adjusted_duration_with_explicit_speed(self):
|
||
"""#1749:adjusted duration = 目标段长 / speed(用户显式调速仍生效;短素材不再自动慢放)。
|
||
|
||
4.0 / 0.75 ≈ 5.333。
|
||
"""
|
||
clip = ResolvedClip(
|
||
clip_id="c1",
|
||
asset_id="a1",
|
||
local_path=Path("/tmp/fake.mp4"),
|
||
clip_type="main",
|
||
order=0,
|
||
start_time=0.0,
|
||
duration=4.0,
|
||
actual_duration=3.0,
|
||
playback_speed=0.75,
|
||
)
|
||
adjusted = UnifiedRenderService._clip_adjusted_duration(clip)
|
||
# 4.0 / 0.75 = 5.333...
|
||
assert abs(adjusted - (4.0 / 0.75)) < 0.01
|