fix(security): P1 audio_merger裸subprocess下沉 + ffmpeg_utils架构下沉到packages/shared
CI/CD Pipeline / Unit Tests (pull_request) Failing after 36s
CI/CD Pipeline / Validate Code Quality And Tests (pull_request) Failing after 41s
CI/CD Pipeline / Integration Tests (pull_request) Failing after 23s
CI/CD Pipeline / Production Browser E2E (pull_request) Failing after 1531h41m11s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 1531h41m12s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 1531h41m11s
CI/CD Pipeline / Build Production Web Image (pull_request) Failing after 1531h41m13s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 1531h41m11s
CI/CD Pipeline / Build Production API Image (pull_request) Failing after 1531h41m13s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 1531h41m13s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 1531h41m14s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 1531h41m14s
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Failing after 1532h12m51s
CI/CD Pipeline / Build Production Worker Image (pull_request) Failing after 1532h12m52s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 36s
CI/CD Pipeline / Validate Code Quality And Tests (pull_request) Failing after 41s
CI/CD Pipeline / Integration Tests (pull_request) Failing after 23s
CI/CD Pipeline / Production Browser E2E (pull_request) Failing after 1531h41m11s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 1531h41m12s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 1531h41m11s
CI/CD Pipeline / Build Production Web Image (pull_request) Failing after 1531h41m13s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 1531h41m11s
CI/CD Pipeline / Build Production API Image (pull_request) Failing after 1531h41m13s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 1531h41m13s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 1531h41m14s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 1531h41m14s
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Failing after 1532h12m51s
CI/CD Pipeline / Build Production Worker Image (pull_request) Failing after 1532h12m52s
- 新增 packages/shared/ffmpeg_utils.py: run_ffmpeg/FFMPEG_BIN/FFPROBE_BIN下沉到共享层 - apps/worker/video_processing/ffmpeg_utils.py: 从shared re-export,保持向后兼容 - packages/application/tts_job/audio_merger.py: 改用 shared.ffmpeg_utils.run_ffmpeg - 新增 test_audio_merger_security.py: 6个单测验证下沉正确性 解决架构分层问题:application层不再需要跨层调用worker层的ffmpeg工具
This commit is contained in:
@@ -1,23 +1,28 @@
|
||||
"""FFmpeg 工具函数 — 共享原语.
|
||||
"""FFmpeg 工具函数 — Worker 层.
|
||||
|
||||
提供 FFmpeg / FFprobe 调用、视频信息探测、视频标准化、xfade 转场滤镜构建
|
||||
等底层能力,供 UnifiedRenderService、VideoComposeService 等复用。
|
||||
业务相关的滤镜构建、视频探测、视频标准化等能力放在这里;
|
||||
底层原语(run_ffmpeg / 二进制路径 / 默认超时)已下沉到 packages/shared/ffmpeg_utils.py,
|
||||
本模块 re-export 保持向后兼容。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import shutil
|
||||
import subprocess # nosec B404
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
# 底层原语从 shared 层导入,application 层和 worker 层共用同一份实现
|
||||
from shared.ffmpeg_utils import ( # noqa: F401
|
||||
DEFAULT_FFMPEG_TIMEOUT,
|
||||
FFMPEG_BIN,
|
||||
FFPROBE_BIN,
|
||||
run_ffmpeg,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# ── 常量 ──────────────────────────────────────────────────────────────────────
|
||||
|
||||
FFMPEG_BIN: str = shutil.which("ffmpeg") or "ffmpeg"
|
||||
FFPROBE_BIN: str = shutil.which("ffprobe") or "ffprobe"
|
||||
# ── 常量(Worker 层业务相关) ────────────────────────────────────────────────
|
||||
|
||||
DEFAULT_OUTPUT_WIDTH = 1280
|
||||
DEFAULT_OUTPUT_HEIGHT = 720
|
||||
@@ -61,62 +66,8 @@ XFADE_TRANSITION_MAP: dict[str, str] = {
|
||||
|
||||
DEFAULT_TRANSITION_DURATION = 0.5
|
||||
|
||||
# FFmpeg 执行默认超时(秒),防止 FFmpeg hang 住导致 worker 永久阻塞
|
||||
# 默认 30 分钟,足够处理大部分短视频渲染;超长视频可单独传参覆盖
|
||||
DEFAULT_FFMPEG_TIMEOUT = 1800
|
||||
|
||||
|
||||
# ── FFmpeg 执行 ───────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
def run_ffmpeg(
|
||||
command: list[str],
|
||||
*,
|
||||
capture_output: bool = True,
|
||||
timeout: int | None = DEFAULT_FFMPEG_TIMEOUT,
|
||||
) -> tuple[str, str]:
|
||||
"""执行 FFmpeg 命令。
|
||||
|
||||
Args:
|
||||
command: 完整的 ffmpeg 命令列表(含 "ffmpeg" 本身)
|
||||
capture_output: 是否捕获 stdout/stderr
|
||||
timeout: 超时时间(秒),默认 1800s(30分钟);None 表示不设超时(不推荐)
|
||||
|
||||
Returns:
|
||||
(stdout, stderr) 元组
|
||||
|
||||
Raises:
|
||||
subprocess.CalledProcessError: 命令执行失败时抛出,
|
||||
异常信息包含完整 stderr 以便排查。
|
||||
subprocess.TimeoutExpired: 超时未完成时抛出,FFmpeg 进程会被 kill。
|
||||
"""
|
||||
try:
|
||||
result = subprocess.run( # nosec B603
|
||||
command,
|
||||
check=True,
|
||||
stdout=subprocess.PIPE if capture_output else None,
|
||||
stderr=subprocess.PIPE if capture_output else None,
|
||||
text=True,
|
||||
timeout=timeout,
|
||||
)
|
||||
return (result.stdout or "", result.stderr or "")
|
||||
except subprocess.TimeoutExpired:
|
||||
logger.error(
|
||||
"FFmpeg 命令超时 (%ds): command=%s",
|
||||
timeout or -1,
|
||||
" ".join(str(c) for c in command[:20]),
|
||||
)
|
||||
raise
|
||||
except subprocess.CalledProcessError as e:
|
||||
# 把完整 stderr 打到日志,方便排查 exit code 183 等问题
|
||||
stderr_text = (e.stderr or "").strip()
|
||||
logger.error(
|
||||
"FFmpeg 命令失败: exit_code=%d command=%s\nstderr:\n%s",
|
||||
e.returncode,
|
||||
" ".join(str(c) for c in command[:20]), # 截断过长的命令
|
||||
stderr_text[:5000], # 截断过长的 stderr
|
||||
)
|
||||
raise
|
||||
# ── FFprobe 探测 ──────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
def probe_has_audio(local_path: str | Path) -> bool:
|
||||
|
||||
Reference in New Issue
Block a user