From 833a969ff717b1864b40b446ee0fb0e0203354aa Mon Sep 17 00:00:00 2001 From: CI Bot Date: Fri, 21 Aug 2026 11:01:58 +0800 Subject: [PATCH 1/2] fix(ingest): separate ffprobe and ffmpeg timeout handling in HEVC transcode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: ffprobe (timeout=10s) and ffmpeg (timeout=900s) shared a single try/except block. When ffprobe timed out on large 4K HEVC files, the TimeoutExpired was caught as 'FFmpeg transcode timeout' and the entire transcode was skipped, leaving HEVC files unconverted. Fix: - Split into 3 independent try/except blocks: 1. Disk space check (warning-only, continues even on failure) 2. ffprobe rotation detection (timeout 10s -> 60s, failure sets _needs_rotation=False and continues to ffmpeg) 3. ffmpeg transcode (isolated, only catches its own errors) - Distinct log messages for each failure mode: - 'ffprobe 旋转检测超时(60s)' vs 'FFmpeg 转码超时(900s)' - ffprobe failure no longer blocks the entire transcode pipeline --- apps/worker/worker_app/tasks/ingest.py | 86 +++++++++++++------------- 1 file changed, 44 insertions(+), 42 deletions(-) diff --git a/apps/worker/worker_app/tasks/ingest.py b/apps/worker/worker_app/tasks/ingest.py index 8ccd9f745..0a4d07823 100755 --- a/apps/worker/worker_app/tasks/ingest.py +++ b/apps/worker/worker_app/tasks/ingest.py @@ -248,26 +248,25 @@ def ingest_asset(job_id: str) -> dict: ) _tc_tmp = None _needs_rotation = False + + # ── Step 1: 磁盘空间检查(独立 try/except,失败仍尝试转码)── try: - # 检查磁盘空间(大文件转码需要至少 2GB 可用空间) _disk_usage = shutil.disk_usage("/tmp") _free_gb = _disk_usage.free / (1024**3) if _free_gb < 2: - raise RuntimeError(f"磁盘空间不足 ({_free_gb:.1f}GB < 2GB),无法处理大文件") + raise RuntimeError(f"磁盘空间不足 ({_free_gb:.1f}GB < 2GB)") + except Exception as _disk_err: + logger.warning("磁盘检查失败,仍尝试转码: job_id=%s err=%s", job_id, _disk_err) - # 检测视频是否有 rotation 元数据(竖屏视频) + # ── Step 2: ffprobe 旋转检测(独立 try/except,失败不阻塞转码)── + try: _probe_cmd = [ "ffprobe", - "-v", - "error", - "-select_streams", - "v:0", - "-show_entries", - "side_data=rotation", - "-show_entries", - "stream_tags=rotate", - "-of", - "default=noprint_wrappers=1:nokey=1", + "-v", "error", + "-select_streams", "v:0", + "-show_entries", "side_data=rotation", + "-show_entries", "stream_tags=rotate", + "-of", "default=noprint_wrappers=1:nokey=1", str(local_file), ] _probe_result = subprocess.run( @@ -275,7 +274,7 @@ def ingest_asset(job_id: str) -> dict: stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, text=True, - timeout=10, + timeout=60, # 大文件在容器 overlay 文件系统上解析可能较慢 ) _rotation_str = (_probe_result.stdout or "").strip().split("\n")[0] if _rotation_str in ("90", "270", "-90"): @@ -285,7 +284,22 @@ def ingest_asset(job_id: str) -> dict: _rotation_str, job_id, ) + except subprocess.TimeoutExpired: + logger.warning( + "ffprobe 旋转检测超时(60s),跳过旋转继续转码: job_id=%s", + job_id, + ) + _needs_rotation = False + except Exception as _probe_err: + logger.warning( + "ffprobe 旋转检测异常,跳过旋转继续转码: job_id=%s err=%s", + job_id, + _probe_err, + ) + _needs_rotation = False + # ── Step 3: ffmpeg 转码(独立 try/except)── + try: _tc_tmp_file = tempfile.NamedTemporaryFile(delete=False, suffix="_h264.mp4") _tc_tmp = Path(_tc_tmp_file.name) _tc_tmp_file.close() # 关闭文件描述符,ffmpeg 会自己打开 @@ -299,38 +313,25 @@ def ingest_asset(job_id: str) -> dict: _cmd = [ "ffmpeg", "-y", - "-i", - str(local_file), - "-c:v", - "libx264", - "-preset", - "fast", - "-crf", - "18", - "-vf", - _vf + ",format=yuv420p", - "-colorspace", - "bt709", - "-color_primaries", - "bt709", - "-color_trf", - "bt709", - "-pix_fmt", - "yuv420p", - "-level", - "4.0", + "-i", str(local_file), + "-c:v", "libx264", + "-preset", "fast", + "-crf", "18", + "-vf", _vf + ",format=yuv420p", + "-colorspace", "bt709", + "-color_primaries", "bt709", + "-color_trf", "bt709", + "-pix_fmt", "yuv420p", + "-level", "4.0", ] # 竖屏视频:清除旋转元数据 if _needs_rotation: _cmd.extend(["-metadata:s:v:0", "rotate=0"]) _cmd.extend( [ - "-c:a", - "aac", - "-b:a", - "128k", - "-movflags", - "+faststart", + "-c:a", "aac", + "-b:a", "128k", + "-movflags", "+faststart", str(_tc_tmp), ] ) @@ -370,13 +371,14 @@ def ingest_asset(job_id: str) -> dict: else: _tail = _proc.stderr[-300:] if _proc.stderr else "" logger.warning( - "FFmpeg 转码失败 rc=%s,降级原始文件: job_id=%s", + "FFmpeg 转码失败 rc=%s stderr=%s: job_id=%s", _proc.returncode, + _tail, job_id, ) except subprocess.TimeoutExpired: logger.warning( - "FFmpeg 转码超时 (900s),降级原始文件: job_id=%s", + "FFmpeg 转码超时(900s),降级原始文件: job_id=%s", job_id, ) except Exception as _e: -- 2.54.0 From 16dbf27cf04b5cb0f0c41a39032ed11245c0a573 Mon Sep 17 00:00:00 2001 From: CI Bot Date: Fri, 21 Aug 2026 03:06:08 +0000 Subject: [PATCH 2/2] style: auto-format with black + isort + prettier [skip ci-format-check] --- apps/worker/worker_app/tasks/ingest.py | 54 +++++++++++++++++--------- 1 file changed, 36 insertions(+), 18 deletions(-) diff --git a/apps/worker/worker_app/tasks/ingest.py b/apps/worker/worker_app/tasks/ingest.py index 0a4d07823..9f80918f7 100755 --- a/apps/worker/worker_app/tasks/ingest.py +++ b/apps/worker/worker_app/tasks/ingest.py @@ -262,11 +262,16 @@ def ingest_asset(job_id: str) -> dict: try: _probe_cmd = [ "ffprobe", - "-v", "error", - "-select_streams", "v:0", - "-show_entries", "side_data=rotation", - "-show_entries", "stream_tags=rotate", - "-of", "default=noprint_wrappers=1:nokey=1", + "-v", + "error", + "-select_streams", + "v:0", + "-show_entries", + "side_data=rotation", + "-show_entries", + "stream_tags=rotate", + "-of", + "default=noprint_wrappers=1:nokey=1", str(local_file), ] _probe_result = subprocess.run( @@ -313,25 +318,38 @@ def ingest_asset(job_id: str) -> dict: _cmd = [ "ffmpeg", "-y", - "-i", str(local_file), - "-c:v", "libx264", - "-preset", "fast", - "-crf", "18", - "-vf", _vf + ",format=yuv420p", - "-colorspace", "bt709", - "-color_primaries", "bt709", - "-color_trf", "bt709", - "-pix_fmt", "yuv420p", - "-level", "4.0", + "-i", + str(local_file), + "-c:v", + "libx264", + "-preset", + "fast", + "-crf", + "18", + "-vf", + _vf + ",format=yuv420p", + "-colorspace", + "bt709", + "-color_primaries", + "bt709", + "-color_trf", + "bt709", + "-pix_fmt", + "yuv420p", + "-level", + "4.0", ] # 竖屏视频:清除旋转元数据 if _needs_rotation: _cmd.extend(["-metadata:s:v:0", "rotate=0"]) _cmd.extend( [ - "-c:a", "aac", - "-b:a", "128k", - "-movflags", "+faststart", + "-c:a", + "aac", + "-b:a", + "128k", + "-movflags", + "+faststart", str(_tc_tmp), ] ) -- 2.54.0