feat: 统一预览与确认生成渲染路径 — 预览=正式品质+确认复用
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 30s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m29s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 1m30s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 1m30s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m43s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 2m23s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 2m22s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m36s
CI/CD Pipeline / Validate - Code Quality (pull_request) Failing after 4m36s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 5m29s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 1m35s
CI/CD Pipeline / CI Gate (pull_request) Failing after 6s
AI Code Review / AI Code Review (pull_request) Successful in 6m45s
CI/CD Pipeline / Production Browser E2E (pull_request) Failing after 880h48m4s
CI/CD Pipeline / Deploy Production (pull_request) Failing after 880h48m4s
CI/CD Pipeline / Build Production Worker Image (pull_request) Failing after 880h48m5s
CI/CD Pipeline / Build Production API Image (pull_request) Failing after 880h48m5s
CI/CD Pipeline / PR Build Web Image (pull_request) Failing after 880h53m34s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 880h53m34s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 880h53m34s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 880h54m0s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 880h54m0s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 880h54m4s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 880h54m6s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 880h54m6s
CI/CD Pipeline / Canary Release to Production (pull_request) Failing after 881h21m32s
CI/CD Pipeline / Build Production Web Image (pull_request) Failing after 881h21m33s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 881h27m28s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 881h27m34s

## 渲染管线统一
- UnifiedRenderService: 移除 is_preview 参数,统一 CRF 23 + medium preset
- RenderAdapter: 移除 is_preview 参数,统一执行校验和缩略图生成
- Worker generation.py: 移除 480p+1M 码率覆盖逻辑
- generation_preview.py: 删除 PREVIEW_RESOLUTION 常量和 _calc_preview_resolution()

## 确认生成复用预览产物
- Domain: 新增 GenerationTask.mark_confirmed() 方法
- confirm_generation: 预览已完成时直接复用产物(秒出),无需重新渲染
- Editor generate: 检查 plan 关联的预览任务,plan未修改时复用产物
- Schema: CreatePreviewGenerationTaskRequest 新增 source_edit_plan_id 字段

## 测试更新
- test_1280_preview_speedup: 重写为验证统一品质参数
- test_confirm_generation: 重写为验证复用逻辑

## 交付标准
- 预览渲染质量与确认生成一致(1080p, CRF 23, medium)
- 预览后确认生成直接复用产物(秒出)
- Worker 端无任何 is_preview 低质量渲染代码残留
This commit is contained in:
CI Bot
2026-08-10 22:32:35 +08:00
parent aff62b29fc
commit 29994ffcbc
10 changed files with 337 additions and 357 deletions
+26 -34
View File
@@ -485,7 +485,6 @@ class RenderAdapter:
rendered_clip_ids: list[str] | None = None,
failed_clip_ids: list[str] | None = None,
voiceover_audio_path: str | None = None,
is_preview: bool = False,
) -> RenderAdapterResult:
"""执行统一渲染核心流程(BGM + ASR + 渲染 + 缩略图 + 上传)。
@@ -508,7 +507,7 @@ class RenderAdapter:
# 预览模式下,如果 plan.config 中存在 voice_id,仍需初始化 ASR 以支持配音
plan_config = plan.config or {}
has_voice_id = bool(plan_config.get("voice_id"))
asr_service = None if (is_preview and not has_voice_id) else self._get_asr_service()
asr_service = self._get_asr_service()
# 3. 读取输出分辨率
export_config = plan_config.get("export", {}) or {}
@@ -532,27 +531,23 @@ class RenderAdapter:
bgm_path=bgm_path,
asr_service=asr_service,
voiceover_audio_path=voiceover_audio_path,
is_preview=is_preview,
)
result = render_svc.render()
# 4.5 渲染后校验输出完整性(预览模式跳过,节省耗时)
if is_preview:
logger.info("[render-adapter] 预览模式:跳过输出校验")
else:
validation = validate_video_output(result.output_path)
if not validation.valid:
logger.error(
"[render-adapter] 渲染输出校验失败: plan_id=%s job_id=%s error=%s",
plan_id,
job_id,
validation.error_message,
)
return RenderAdapterResult(
success=False,
error_message=f"渲染输出校验失败: {validation.error_message}",
error_detail=validation.error_message,
)
# 4.5 渲染后校验输出完整性
validation = validate_video_output(result.output_path)
if not validation.valid:
logger.error(
"[render-adapter] 渲染输出校验失败: plan_id=%s job_id=%s error=%s",
plan_id,
job_id,
validation.error_message,
)
return RenderAdapterResult(
success=False,
error_message=f"渲染输出校验失败: {validation.error_message}",
error_detail=validation.error_message,
)
self._report_progress(progress_cb, 80.0, "上传渲染结果")
# 5. 上传结果
@@ -561,20 +556,19 @@ class RenderAdapter:
self._report_progress(progress_cb, 90.0, "生成封面缩略图")
# 6. 生成缩略图(预览模式跳过,节省耗时)
# 6. 生成封面缩略图
thumbnail_url = ""
if not is_preview:
try:
from video_processing.thumbnail_generator import generate_and_upload_thumbnail
try:
from video_processing.thumbnail_generator import generate_and_upload_thumbnail
thumb_storage_key = f"rendered/{plan_id}/thumbnail.jpg"
thumbnail_url = generate_and_upload_thumbnail(str(result.output_path), thumb_storage_key)
except Exception as thumb_err:
logger.warning(
"[render-adapter] 缩略图生成失败(不影响主流程): plan_id=%s error=%s",
plan_id,
thumb_err,
)
thumb_storage_key = f"rendered/{plan_id}/thumbnail.jpg"
thumbnail_url = generate_and_upload_thumbnail(str(result.output_path), thumb_storage_key)
except Exception as thumb_err:
logger.warning(
"[render-adapter] 缩略图生成失败(不影响主流程): plan_id=%s error=%s",
plan_id,
thumb_err,
)
self._report_progress(progress_cb, 100.0, "渲染完成")
@@ -620,7 +614,6 @@ class RenderAdapter:
work_dir: Path | None = None,
progress_cb: ProgressCallback | None = None,
voiceover_audio_path: str | None = None,
is_preview: bool = False,
) -> RenderAdapterResult:
"""使用内存中的 plan/clips/asset_path_map 直接渲染。
@@ -679,7 +672,6 @@ class RenderAdapter:
job_id=job_id,
progress_cb=progress_cb,
voiceover_audio_path=voiceover_audio_path,
is_preview=is_preview,
)
except subprocess.CalledProcessError as exc: