From 104af2623abf3eb794baaa05642fed7fa0d0d5b2 Mon Sep 17 00:00:00 2001 From: ops-bot Date: Mon, 13 Jul 2026 22:52:47 +0800 Subject: [PATCH] =?UTF-8?q?fix(format):=20black=E6=A0=BC=E5=BC=8F=E5=8C=96?= =?UTF-8?q?=20unified=5Frender=5Fservice.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../unified_render_service.py | 76 ++++++++++++++----- 1 file changed, 58 insertions(+), 18 deletions(-) diff --git a/apps/worker/video_processing/unified_render_service.py b/apps/worker/video_processing/unified_render_service.py index c482c88ad..f0ef3beea 100755 --- a/apps/worker/video_processing/unified_render_service.py +++ b/apps/worker/video_processing/unified_render_service.py @@ -238,10 +238,15 @@ class UnifiedRenderService: else: # 回退到带滤镜的直通渲染 pass_through_has_audio = self._render_pass_through( - layers, output_path, ass_path=ass_path, video_duration=video_duration + layers, + output_path, + ass_path=ass_path, + video_duration=video_duration, ) else: - filter_complex, input_args = self._build_filter_complex(layers, ass_path=ass_path) + filter_complex, input_args = self._build_filter_complex( + layers, ass_path=ass_path + ) self._execute_ffmpeg(filter_complex, input_args, video_only_path) t_video_end = time.time() @@ -326,7 +331,9 @@ class UnifiedRenderService: if not main_layer or not main_layer.clips: return 0.0 - total = sum(UnifiedRenderService._clip_effective_duration(c) for c in main_layer.clips) + total = sum( + UnifiedRenderService._clip_effective_duration(c) for c in main_layer.clips + ) # 减去转场重叠时间(粗略估算) n_clips = len(main_layer.clips) @@ -434,7 +441,10 @@ class UnifiedRenderService: return False, f"像素格式不是yuv420p: {info.get('pix_fmt', 'unknown')}" # 分辨率必须一致 - if info.get("width", 0) != self.output_width or info.get("height", 0) != self.output_height: + if ( + info.get("width", 0) != self.output_width + or info.get("height", 0) != self.output_height + ): return False, ( f"分辨率不匹配: " f"{info.get('width', 0)}x{info.get('height', 0)} " @@ -484,7 +494,9 @@ class UnifiedRenderService: role = layers[0].role # 判断是否满足 copy 条件 - can_copy, reason = self._can_use_stream_copy(clip, ass_path=ass_path, video_duration=video_duration) + can_copy, reason = self._can_use_stream_copy( + clip, ass_path=ass_path, video_duration=video_duration + ) if not can_copy: logger.info( "[unified-render] stream_copy 跳过: plan_id=%s reason=%s", @@ -512,7 +524,9 @@ class UnifiedRenderService: # 计算最终时长 final_duration = effective_duration - if video_duration > 0 and (final_duration <= 0 or final_duration > video_duration): + if video_duration > 0 and ( + final_duration <= 0 or final_duration > video_duration + ): final_duration = video_duration if final_duration > 0: command.extend(["-t", f"{final_duration:.3f}"]) @@ -549,7 +563,9 @@ class UnifiedRenderService: ) return True else: - logger.warning("[unified-render] stream_copy 输出为空: plan_id=%s", self.plan.id) + logger.warning( + "[unified-render] stream_copy 输出为空: plan_id=%s", self.plan.id + ) return False except (subprocess.CalledProcessError, subprocess.TimeoutExpired) as e: logger.warning( @@ -612,7 +628,10 @@ class UnifiedRenderService: filters.append(f"scale={pip_w}:{pip_h}") else: # main / broll / background: 铺满裁剪 - filters.append(f"scale={self.output_width}:{self.output_height}" ":force_original_aspect_ratio=increase") + filters.append( + f"scale={self.output_width}:{self.output_height}" + ":force_original_aspect_ratio=increase" + ) filters.append(f"crop={self.output_width}:{self.output_height}") filters.append("setpts=PTS-STARTPTS") @@ -628,7 +647,9 @@ class UnifiedRenderService: # 最终输出时长:取 clip 有效时长和 video_duration 的较小值 final_duration = effective_duration - if video_duration > 0 and (final_duration <= 0 or final_duration > video_duration): + if video_duration > 0 and ( + final_duration <= 0 or final_duration > video_duration + ): final_duration = video_duration command = [ @@ -727,7 +748,9 @@ class UnifiedRenderService: resolved.sort(key=lambda c: c.order) return resolved - def _group_clips_into_layers(self, resolved_clips: list[ResolvedClip]) -> list[RenderLayer]: + def _group_clips_into_layers( + self, resolved_clips: list[ResolvedClip] + ) -> list[RenderLayer]: """将 ResolvedClips 分组为 RenderLayers。 分组规则见 _resolve_layer_role 函数文档。 @@ -817,14 +840,16 @@ class UnifiedRenderService: filters.append(f"scale={pip_w}:{pip_h}") elif role == "background": filters.append( - f"scale={self.output_width}:{self.output_height}" ":force_original_aspect_ratio=increase" + f"scale={self.output_width}:{self.output_height}" + ":force_original_aspect_ratio=increase" ) filters.append(f"crop={self.output_width}:{self.output_height}") else: # main / broll: 铺满裁剪(scale to cover + center crop) # 对齐链路A编辑器合成行为,与主流短视频平台一致 filters.append( - f"scale={self.output_width}:{self.output_height}" ":force_original_aspect_ratio=increase" + f"scale={self.output_width}:{self.output_height}" + ":force_original_aspect_ratio=increase" ) filters.append(f"crop={self.output_width}:{self.output_height}") @@ -841,8 +866,13 @@ class UnifiedRenderService: layer_clip_indices = [all_clips.index(c) for c in layer.clips] layer_labels = [preprocessed_labels[i] for i in layer_clip_indices] # 使用 trim 后的有效时长,与 Step 1 的 trim=duration 保持一致 - layer_durations = [UnifiedRenderService._clip_effective_duration(all_clips[i]) for i in layer_clip_indices] - layer_transitions = [all_clips[i].transition_effect for i in layer_clip_indices] + layer_durations = [ + UnifiedRenderService._clip_effective_duration(all_clips[i]) + for i in layer_clip_indices + ] + layer_transitions = [ + all_clips[i].transition_effect for i in layer_clip_indices + ] if len(layer_labels) == 1: # 单 clip 层,直接使用预处理标签 @@ -873,7 +903,8 @@ class UnifiedRenderService: base_label = layer_output_labels[role] combined_label = f"combined_{role}" filter_parts.append( - f"[{final_video_label}][{base_label}]" f"overlay=(W-w)/2:(H-h)/2[{combined_label}]" + f"[{final_video_label}][{base_label}]" + f"overlay=(W-w)/2:(H-h)/2[{combined_label}]" ) final_video_label = combined_label else: @@ -898,13 +929,18 @@ class UnifiedRenderService: 20, ) combined_label = f"combined_{layer.role}" - filter_parts.append(f"[{final_video_label}][{overlay_label}]" f"overlay={x}:{y}[{combined_label}]") + filter_parts.append( + f"[{final_video_label}][{overlay_label}]" + f"overlay={x}:{y}[{combined_label}]" + ) final_video_label = combined_label # 叠加字幕(如有)+ 最终像素格式 if ass_path is not None: ass_filter_path = str(ass_path).replace("\\", "/").replace(":", "\\:") - filter_parts.append(f"[{final_video_label}]subtitles='{ass_filter_path}',format=yuv420p[final_video]") + filter_parts.append( + f"[{final_video_label}]subtitles='{ass_filter_path}',format=yuv420p[final_video]" + ) else: filter_parts.append(f"[{final_video_label}]format=yuv420p[final_video]") @@ -979,5 +1015,9 @@ class UnifiedRenderService: def _clip_effective_duration(clip: ResolvedClip) -> float: """计算 clip 的有效时长.""" if clip.duration > 0: - return min(clip.duration, clip.actual_duration) if clip.actual_duration > 0 else clip.duration + return ( + min(clip.duration, clip.actual_duration) + if clip.actual_duration > 0 + else clip.duration + ) return clip.actual_duration if clip.actual_duration > 0 else 0.0