diff --git a/apps/api/app/api/routes/generation_tasks.py b/apps/api/app/api/routes/generation_tasks.py index d6afc7219..df4d73e51 100755 --- a/apps/api/app/api/routes/generation_tasks.py +++ b/apps/api/app/api/routes/generation_tasks.py @@ -169,8 +169,22 @@ def _writeback_edit_plan_config( current_config = plan_model.config if isinstance(plan_model.config, dict) else {} merged = dict(current_config) merged["generation_task_id"] = task_id + + # 检查标题是否发生变化,如果变化则清除 cover 字段强制重新生成封面 if title_config: + old_title_config = merged.get("title_config", {}) or {} + old_title_text = (old_title_config.get("text") or "").strip() + new_title_text = (title_config.get("text") or "").strip() + if old_title_text != new_title_text: + # 标题变化,清除旧封面 + if "cover" in merged: + del merged["cover"] + logger.info( + "[生成任务] 标题变化,清除旧封面: plan_id=%s old_title=%s new_title=%s", + plan_id, old_title_text, new_title_text, + ) merged["title_config"] = title_config + plan_model.config = merged db.commit() logger.info( diff --git a/apps/worker/video_processing/render_adapter.py b/apps/worker/video_processing/render_adapter.py index 295d32464..4f5cb5444 100755 --- a/apps/worker/video_processing/render_adapter.py +++ b/apps/worker/video_processing/render_adapter.py @@ -598,7 +598,7 @@ class RenderAdapter: # 抽帧天然带标题,因此这里传空字符串,避免 Pillow 二次叠加导致重影。 # Pillow 叠加仅用于 API 从源素材抽帧(源素材本身无标题)的兜底场景。 cover_candidates = extract_and_upload_cover_frames( - str(result.output_path), plan_id, num_frames=3, title_text="" + str(result.output_path), plan_id, task_id=job_id, num_frames=3, title_text="" ) if cover_candidates: logger.info( diff --git a/apps/worker/video_processing/thumbnail_generator.py b/apps/worker/video_processing/thumbnail_generator.py index a53163d4d..4282c0027 100755 --- a/apps/worker/video_processing/thumbnail_generator.py +++ b/apps/worker/video_processing/thumbnail_generator.py @@ -278,6 +278,7 @@ def extract_and_upload_cover_frames( video_path: str, plan_id: str, *, + task_id: str = "", num_frames: int = 3, title_text: str = "", title_color: str = "#ffffff", @@ -291,6 +292,7 @@ def extract_and_upload_cover_frames( Args: video_path: 视频文件路径 plan_id: 编辑计划 ID(用于生成 storage key) + task_id: 任务 ID(用于生成独立的 storage key,避免标题变更时封面冲突) num_frames: 抽取帧数(默认 3) title_text: 标题文字;非空时用 Pillow 叠加到每帧。 从已渲染视频抽帧时通常传空(标题已烧录);从源素材抽帧时传标题。 @@ -338,7 +340,7 @@ def extract_and_upload_cover_frames( font_size=title_font_size, ) - storage_key = f"covers/{plan_id}/mediakit_frame_{i}.jpg" + storage_key = f"covers/{plan_id}/{task_id}/mediakit_frame_{i}.jpg" url = upload_to_oss(tmp.name, storage_key) if url: seek_time = frame.get("timestamp", 0.0) @@ -377,7 +379,7 @@ def extract_and_upload_cover_frames( position=title_position, font_size=title_font_size, ) - storage_key = f"covers/{plan_id}/frame_{i}.jpg" + storage_key = f"covers/{plan_id}/{task_id}/frame_{i}.jpg" url = upload_to_oss(frame_path, storage_key) if url: seek_time = max(0.5, duration * ratio) if duration > 0 else 0.0