c873bb635f
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / PR Build API Image (push) Has been skipped
CI/CD Pipeline / PR Build Web Image (push) Has been skipped
CI/CD Pipeline / PR Build Worker Image (push) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 55s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 1m13s
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 / Validate - Migration (alembic) (push) Successful in 2m43s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been skipped
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 2m46s
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 2m51s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 3m6s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 3m59s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 4m14s
CI/CD Pipeline / PR Build Web Image (pull_request) Has been skipped
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 30s
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
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 2m53s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 3m6s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 7m6s
CI/CD Pipeline / Build Staging API Image (push) Successful in 7m12s
AI Code Review / AI Code Review (pull_request) Failing after 6m19s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 39s
CI/CD Pipeline / Validate - Code Quality (push) Successful in 8m57s
CI/CD Pipeline / Staging E2E Tests (push) Successful in 1m2s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 8m55s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 1m9s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 3m16s
CI/CD Pipeline / Integration Tests (push) Successful in 3m12s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 3m13s
CI/CD Pipeline / Unit Tests (push) Successful in 14m24s
CI/CD Pipeline / CI Gate (push) Has been skipped
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 / Deploy Production (push) Has been skipped
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Unit Tests (pull_request) Successful in 12m30s
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
CI/CD Pipeline / CI Gate (pull_request) Successful in 8s
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com> Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
165 lines
5.5 KiB
Python
165 lines
5.5 KiB
Python
"""统一渲染管线 — 预览与确认生成使用相同品质参数。
|
|
|
|
验证点:
|
|
1. UnifiedRenderService 不再有 is_preview 参数
|
|
2. 所有渲染统一使用 medium preset + CRF 23
|
|
3. RenderAdapter 统一执行校验和缩略图生成
|
|
4. generation.py 并行下载逻辑(保留)
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import tempfile
|
|
from pathlib import Path
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
import pytest
|
|
|
|
# ── 1. UnifiedRenderService 无 is_preview 参数 ──
|
|
|
|
|
|
class TestUnifiedRenderServiceNoPreviewParam:
|
|
"""UnifiedRenderService 构造函数不再接受 is_preview 参数。"""
|
|
|
|
def test_constructor_has_no_is_preview(self):
|
|
import inspect
|
|
|
|
from video_processing.unified_render_service import UnifiedRenderService
|
|
|
|
sig = inspect.signature(UnifiedRenderService.__init__)
|
|
param_names = list(sig.parameters.keys())
|
|
assert (
|
|
"is_preview" not in param_names
|
|
), f"is_preview should be removed from UnifiedRenderService.__init__, found params: {param_names}"
|
|
|
|
def test_no_is_preview_attribute(self):
|
|
from video_processing.unified_render_service import UnifiedRenderService
|
|
|
|
svc = UnifiedRenderService(
|
|
plan=MagicMock(id="test"),
|
|
clips=[],
|
|
asset_path_map={},
|
|
work_dir=Path(tempfile.mkdtemp()),
|
|
)
|
|
assert not hasattr(
|
|
svc, "is_preview"
|
|
), "UnifiedRenderService should not have is_preview attribute after unification"
|
|
|
|
|
|
# ── 2. FFmpeg 参数统一为 medium + CRF 23 ──
|
|
|
|
|
|
class TestUnifiedFFmpegPreset:
|
|
"""所有渲染统一使用 medium preset + CRF 23。"""
|
|
|
|
def _make_clip(self):
|
|
from video_processing.unified_render_service import ResolvedClip
|
|
|
|
return ResolvedClip(
|
|
clip_id="c1",
|
|
asset_id="a1",
|
|
local_path=Path("/tmp/fake.mp4"),
|
|
clip_type="main",
|
|
order=0,
|
|
start_time=0,
|
|
duration=10.0,
|
|
playback_speed=1.0,
|
|
transition_effect="cut",
|
|
transition_duration=0.0,
|
|
config={},
|
|
)
|
|
|
|
@patch("video_processing.unified_render_service.run_ffmpeg")
|
|
def test_execute_ffmpeg_uses_medium_crf23(self, mock_run):
|
|
from video_processing.unified_render_service import (
|
|
RenderLayer,
|
|
UnifiedRenderService,
|
|
)
|
|
|
|
plan = MagicMock()
|
|
plan.id = "test_plan"
|
|
plan.config = {}
|
|
|
|
clip = self._make_clip()
|
|
|
|
svc = UnifiedRenderService(
|
|
plan=plan,
|
|
clips=[clip],
|
|
asset_path_map={"a1": Path("/tmp/fake.mp4")},
|
|
work_dir=Path(tempfile.mkdtemp()),
|
|
output_width=1920,
|
|
output_height=1080,
|
|
)
|
|
|
|
layers = [RenderLayer(role="main", clips=[clip])]
|
|
filter_complex, input_args = svc._build_filter_complex(layers)
|
|
output_path = Path(tempfile.mkdtemp()) / "out.mp4"
|
|
svc._execute_ffmpeg(filter_complex, input_args, output_path)
|
|
|
|
mock_run.assert_called_once()
|
|
cmd = mock_run.call_args[0][0]
|
|
|
|
# Check preset is medium (no conditional)
|
|
preset_idx = cmd.index("-preset")
|
|
assert cmd[preset_idx + 1] == "medium", f"Expected medium, got {cmd[preset_idx + 1]}"
|
|
|
|
# Check crf is 23 (no conditional)
|
|
crf_idx = cmd.index("-crf")
|
|
assert cmd[crf_idx + 1] == "23", f"Expected crf 23, got {cmd[crf_idx + 1]}"
|
|
|
|
|
|
# ── 3. RenderAdapter 统一执行校验和缩略图 ──
|
|
|
|
|
|
class TestRenderAdapterUnifiedPostProcess:
|
|
"""RenderAdapter 不再跳过校验和缩略图。"""
|
|
|
|
def test_render_adapter_no_is_preview_param(self):
|
|
import inspect
|
|
|
|
from video_processing.render_adapter import RenderAdapter
|
|
|
|
# Check render_from_memory signature
|
|
sig = inspect.signature(RenderAdapter.render_from_memory)
|
|
param_names = list(sig.parameters.keys())
|
|
assert (
|
|
"is_preview" not in param_names
|
|
), f"is_preview should be removed from render_from_memory, found params: {param_names}"
|
|
|
|
def test_no_preview_skip_validation_in_source(self):
|
|
"""渲染适配器源码中不再包含预览跳过校验的逻辑。"""
|
|
with open("apps/worker/video_processing/render_adapter.py") as f:
|
|
source = f.read()
|
|
|
|
assert "预览模式:跳过输出校验" not in source, "Should not skip validation in any mode"
|
|
assert "if not is_preview:" not in source, "Thumbnail should always be generated"
|
|
|
|
|
|
# ── 4. generation.py 不再有 is_preview 覆盖逻辑 ──
|
|
|
|
|
|
class TestWorkerGenerationNoPreviewOverride:
|
|
"""Worker generation.py 不再覆盖预览分辨率为 480p。"""
|
|
|
|
def test_no_480p_override(self):
|
|
with open("apps/worker/worker_app/tasks/generation.py") as f:
|
|
source = f.read()
|
|
|
|
assert 'resolution = "854x480"' not in source, "Should not override resolution to 480p in preview mode"
|
|
assert 'bitrate = "1M"' not in source, "Should not override bitrate to 1M in preview mode"
|
|
|
|
|
|
# ── 5. generation_preview.py 不再有 PREVIEW_RESOLUTION ──
|
|
|
|
|
|
class TestPreviewNoLowQualityConstants:
|
|
"""预览 API 不再定义低质量常量。"""
|
|
|
|
def test_no_preview_resolution_constant(self):
|
|
with open("apps/api/app/api/routes/generation_preview.py") as f:
|
|
source = f.read()
|
|
|
|
assert "PREVIEW_RESOLUTION" not in source, "PREVIEW_RESOLUTION constant should be removed"
|
|
assert "_calc_preview_resolution" not in source, "_calc_preview_resolution function should be removed"
|