From c3c00c63612772f6241b361b54bb7b50b8fd9ec6 Mon Sep 17 00:00:00 2001 From: CI Bot Date: Fri, 17 Jul 2026 12:37:52 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=B8=80=E9=94=AE=E7=94=9F=E6=88=90P0?= =?UTF-8?q?=E6=95=88=E6=9E=9C=E5=B1=82=E8=A1=A5=E9=BD=90=20-=20=E6=A8=A1?= =?UTF-8?q?=E6=9D=BF=E9=85=8D=E7=BD=AE=E6=B3=A8=E5=85=A5(BGM/=E5=AD=97?= =?UTF-8?q?=E5=B9=95)=20+=20=E7=9B=B4=E9=80=9A=E6=A8=A1=E5=BC=8F=E8=B0=83?= =?UTF-8?q?=E9=80=9F=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 一键生成渲染前从模板加载plan级配置,注入virtual_plan.config - BGM、字幕(含ASR)等效果层自动生效 - 直通模式(_render_pass_through)补充speed调速处理 - 有水印时禁用直通路径,避免静默失效 - 模板不存在/加载失败时降级,不阻塞主流程 --- .../unified_render_service.py | 18 +++++++ apps/worker/worker_app/tasks/generation.py | 54 +++++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/apps/worker/video_processing/unified_render_service.py b/apps/worker/video_processing/unified_render_service.py index b63392f0e..4c8fe5ade 100755 --- a/apps/worker/video_processing/unified_render_service.py +++ b/apps/worker/video_processing/unified_render_service.py @@ -759,6 +759,19 @@ class UnifiedRenderService: plan_config = getattr(self.plan, "config", None) or {} if isinstance(plan_config, dict) and plan_config.get("stickers"): return False + + # 有水印时禁用直通(图片水印需要额外输入,统一走 filter_complex) + try: + from video_processing.watermark_engine import WatermarkConfig + + wm_config = WatermarkConfig.from_dict(plan_config.get("watermark")) + if wm_config is not None and wm_config.validate()[0]: + return False + except Exception: + pass + + # 有调速时仍然可以走直通(视频调速通过 setpts 实现,单输入即可) + return True def _can_use_stream_copy( @@ -971,6 +984,11 @@ class UnifiedRenderService: filters.append(f"trim=duration={effective_duration}") filters.append("setpts=PTS-STARTPTS") + # 调速 — 与 filter_complex 路径一致 + speed = UnifiedRenderService._clip_speed(clip) + if abs(speed - 1.0) >= 1e-6: + filters.append(f"setpts=PTS/{speed:.4f}") + # 倒放滤镜 reverse_config = ReverseConfig.from_dict(clip.config.get("reverse")) if reverse_config.enabled and reverse_config.reverse_video: diff --git a/apps/worker/worker_app/tasks/generation.py b/apps/worker/worker_app/tasks/generation.py index e04941b17..eabfdea79 100755 --- a/apps/worker/worker_app/tasks/generation.py +++ b/apps/worker/worker_app/tasks/generation.py @@ -774,6 +774,47 @@ def _validate_template_exists(template_id: str) -> None: session.close() +def _load_template_plan_config(template_id: str) -> dict: + """从模板加载 plan 级配置(BGM、字幕、滤镜等效果层)。 + + 模板不存在时返回空 dict,不阻塞主流程。 + """ + if not template_id: + return {} + try: + from packages.adapters.sqlalchemy_impl.models import TemplateModel + + session = SessionLocal() + try: + template = ( + session.query(TemplateModel) + .filter( + TemplateModel.id == template_id, + TemplateModel.is_active.is_(True), + ) + .first() + ) + if template is None: + logger.warning("模板不存在,跳过配置加载: template_id=%s", template_id) + return {} + config = template.config or {} + if isinstance(config, str): + import json + + config = json.loads(config) + logger.info( + "模板配置加载成功: template_id=%s keys=%s", + template_id, + list(config.keys()), + ) + return config + finally: + session.close() + except Exception as e: + logger.warning("加载模板配置失败,跳过: template_id=%s err=%s", template_id, e) + return {} + + # ── 渲染引擎选择 ───────────────────────────────────────────────────────────── @@ -1032,6 +1073,19 @@ def _render_video( mode=editing_mode.value, ) + # 注入模板配置(BGM、字幕等效果层) + if template_id: + template_config = _load_template_plan_config(template_id) + if template_config: + # 合并:现有 config 优先级更高(目前为空,模板配置直接生效) + base_config = virtual_plan.config or {} + virtual_plan.config = {**template_config, **base_config} + logger.info( + "[task_id=%s] [渲染] 模板配置已注入: keys=%s", + task_id, + list(template_config.keys()), + ) + total_duration = sum(c.duration for c in virtual_clips) logger.info( "[task_id=%s] [剪辑计划] 片段数=%d, 总时长=%.1fs", -- 2.54.0