refactor: 统一封面生成管道 — 从渲染后视频抽帧作为封面 (#1371)
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 1m32s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 1m45s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 2m20s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 2m22s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 5m2s
CI/CD Pipeline / Validate - Code Quality (push) Successful in 6m19s
CI/CD Pipeline / Integration Tests (push) Successful in 2m0s
CI/CD Pipeline / Unit Tests (push) Successful in 9m9s
CI/CD Pipeline / Build Staging API Image (push) Successful in 20m32s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 1m2s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 41s
CI/CD Pipeline / Staging E2E Tests (push) Successful in 1m53s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 2m50s
CI/CD Pipeline / Canary Release to Production (push) Failing after 784h46m17s
CI/CD Pipeline / Deploy Production (push) Failing after 785h1m33s
CI/CD Pipeline / CI Gate (push) Failing after 785h1m33s
CI/CD Pipeline / Build Production Web Image (push) Failing after 785h1m34s
CI/CD Pipeline / Build Production API Image (push) Failing after 785h1m34s
CI/CD Pipeline / PR Build Web Image (push) Failing after 785h10m41s
CI/CD Pipeline / PR Build API Image (push) Failing after 785h10m42s
CI/CD Pipeline / Check if frontend-only change (push) Failing after 785h10m44s
CI/CD Pipeline / Production Browser E2E (push) Failing after 785h35m3s
CI/CD Pipeline / Build Production Worker Image (push) Failing after 785h35m5s
CI/CD Pipeline / PR Build Worker Image (push) Failing after 785h44m11s
CI/CD Pipeline / Frontend Lint (push) Failing after 785h44m14s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 1m32s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 1m45s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 2m20s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 2m22s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 5m2s
CI/CD Pipeline / Validate - Code Quality (push) Successful in 6m19s
CI/CD Pipeline / Integration Tests (push) Successful in 2m0s
CI/CD Pipeline / Unit Tests (push) Successful in 9m9s
CI/CD Pipeline / Build Staging API Image (push) Successful in 20m32s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 1m2s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 41s
CI/CD Pipeline / Staging E2E Tests (push) Successful in 1m53s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 2m50s
CI/CD Pipeline / Canary Release to Production (push) Failing after 784h46m17s
CI/CD Pipeline / Deploy Production (push) Failing after 785h1m33s
CI/CD Pipeline / CI Gate (push) Failing after 785h1m33s
CI/CD Pipeline / Build Production Web Image (push) Failing after 785h1m34s
CI/CD Pipeline / Build Production API Image (push) Failing after 785h1m34s
CI/CD Pipeline / PR Build Web Image (push) Failing after 785h10m41s
CI/CD Pipeline / PR Build API Image (push) Failing after 785h10m42s
CI/CD Pipeline / Check if frontend-only change (push) Failing after 785h10m44s
CI/CD Pipeline / Production Browser E2E (push) Failing after 785h35m3s
CI/CD Pipeline / Build Production Worker Image (push) Failing after 785h35m5s
CI/CD Pipeline / PR Build Worker Image (push) Failing after 785h44m11s
CI/CD Pipeline / Frontend Lint (push) Failing after 785h44m14s
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com> Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
This commit was merged in pull request #1371.
This commit is contained in:
@@ -81,9 +81,7 @@ class RenderAdapterResult:
|
||||
failed_clip_ids: list[str] = None # 失败的 clip id 列表
|
||||
error_message: str = ""
|
||||
error_detail: str = "" # 详细错误信息(如 ffmpeg stderr),用于排查
|
||||
cover_candidates: list[dict] | None = (
|
||||
None # 封面候选帧 [{"image_url": "...", "frame_time": 5.0, "storage_key": "..."}]
|
||||
)
|
||||
cover_url: str = "" # 封面图片 URL(从渲染后视频抽帧,天然带标题)
|
||||
|
||||
def __post_init__(self):
|
||||
if self.rendered_clip_ids is None:
|
||||
@@ -562,43 +560,33 @@ class RenderAdapter:
|
||||
storage_key = f"rendered/{plan_id}/{job_id or plan_id}.mp4"
|
||||
output_url = upload_to_oss(result.output_path, storage_key)
|
||||
|
||||
self._report_progress(progress_cb, 90.0, "生成封面缩略图")
|
||||
self._report_progress(progress_cb, 90.0, "抽取封面帧")
|
||||
|
||||
# 6. 生成封面缩略图
|
||||
thumbnail_url = ""
|
||||
# 6. 从已渲染视频抽取封面帧(标题已通过 ASS 字幕烧录,封面天然带标题)
|
||||
cover_url = ""
|
||||
cover_frame_path = None
|
||||
try:
|
||||
from video_processing.thumbnail_generator import generate_and_upload_thumbnail
|
||||
from video_processing.thumbnail_generator import extract_first_frame
|
||||
|
||||
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,
|
||||
)
|
||||
|
||||
# 7. 抽取封面候选帧并上传 OSS(失败不阻断主流程)
|
||||
cover_candidates = None
|
||||
try:
|
||||
from video_processing.thumbnail_generator import extract_and_upload_cover_frames
|
||||
|
||||
# 从 plan config 提取标题文字,叠加到封面候选帧上
|
||||
_title_cfg = (plan_config or {}).get("title", {}) or {}
|
||||
_title_text = (_title_cfg.get("text", "") or "").strip() if _title_cfg.get("enabled", True) else ""
|
||||
|
||||
cover_candidates = extract_and_upload_cover_frames(
|
||||
str(result.output_path), plan_id, num_frames=3, title_text=_title_text
|
||||
)
|
||||
if cover_candidates:
|
||||
cover_frame_path = extract_first_frame(str(result.output_path), width=640)
|
||||
cover_storage_key = f"rendered/{plan_id}/cover.jpg"
|
||||
try:
|
||||
cover_url = upload_to_oss(cover_frame_path, cover_storage_key) or ""
|
||||
finally:
|
||||
if cover_frame_path:
|
||||
try:
|
||||
Path(cover_frame_path).unlink(missing_ok=True)
|
||||
except Exception:
|
||||
pass
|
||||
if cover_url:
|
||||
logger.info(
|
||||
"[render-adapter] 封面候选帧生成成功: plan_id=%s count=%d",
|
||||
"[render-adapter] 封面帧提取成功: plan_id=%s url=%s",
|
||||
plan_id,
|
||||
len(cover_candidates),
|
||||
cover_url[:80],
|
||||
)
|
||||
except Exception as cover_err:
|
||||
logger.warning(
|
||||
"[render-adapter] 封面候选帧生成失败(不影响主流程): plan_id=%s error=%s",
|
||||
"[render-adapter] 封面帧提取失败(不影响主流程): plan_id=%s error=%s",
|
||||
plan_id,
|
||||
cover_err,
|
||||
)
|
||||
@@ -626,7 +614,7 @@ class RenderAdapter:
|
||||
success=True,
|
||||
output_url=output_url or "",
|
||||
output_path=result.output_path,
|
||||
thumbnail_url=thumbnail_url,
|
||||
thumbnail_url=cover_url,
|
||||
duration=result.duration,
|
||||
file_size=result.file_size,
|
||||
width=result.width,
|
||||
@@ -634,7 +622,7 @@ class RenderAdapter:
|
||||
clip_count=len(clips),
|
||||
rendered_clip_ids=final_rendered_ids,
|
||||
failed_clip_ids=final_failed_ids,
|
||||
cover_candidates=cover_candidates,
|
||||
cover_url=cover_url,
|
||||
)
|
||||
|
||||
def render_from_memory(
|
||||
|
||||
Reference in New Issue
Block a user