Compare commits

...

6 Commits

Author SHA1 Message Date
CI Bot 1e1bc7f904 style: auto-format with black + isort + prettier [skip ci-format-check]
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 1m5s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m59s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Successful in 59s
AI Code Review / AI Code Review (pull_request) Successful in 2m3s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 2m32s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 2m37s
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 2m40s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 1m7s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 35s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 2m14s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 4m11s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 2m26s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 6m14s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 9m14s
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Canary Release to Production (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / Integration Tests (pull_request) Successful in 4m16s
CI/CD Pipeline / CI Gate (pull_request) Failing after 15s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 58s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 1m16s
2026-08-20 03:54:42 +00:00
CI Bot efe34793f3 test: 添加 HEVC 自动转码逻辑单元测试
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 47s
AI Code Review / AI Code Review (pull_request) Successful in 2m2s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 2m4s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 2m56s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 2m58s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 3m2s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 4m14s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 1m20s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Successful in 1m28s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 45s
CI/CD Pipeline / Validate - Code Quality (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
覆盖场景:
- HEVC 编码检测(hevc/h265/hvh1,不区分大小写)
- H.264 视频不触发转码
- 转码后文件命名规则(_h264 后缀)
- 元数据提取失败时的脏数据防护(AI Code Review BUG 修复)
- FFmpeg 超时/错误降级策略
- Scale filter 逻辑(4K/1080p/720p)
- 安全修复(NamedTemporaryFile 替代 mktemp)
- 稳定性修复(stdout=DEVNULL, stderr=PIPE)
- FFmpeg 命令参数验证

16 个测试用例,全部通过
2026-08-20 11:49:03 +08:00
CI Bot dc3deda8d2 fix: 修复 AI Code Review 的安全和稳定性问题
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 57s
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m18s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 3m36s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 3m56s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m33s
AI Code Review / AI Code Review (pull_request) Successful in 3m47s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 1m14s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Successful in 1m25s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 54s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 3m23s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 2m59s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 3m35s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 6m56s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 10m56s
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Canary Release to Production (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / Integration Tests (pull_request) Successful in 4m15s
CI/CD Pipeline / CI Gate (pull_request) Failing after 11s
1. tempfile.mktemp -> NamedTemporaryFile(delete=False)
   - 避免 TOCTOU 竞态条件安全风险

2. subprocess.run capture_output=True -> stdout=DEVNULL, stderr=PIPE
   - 避免 FFmpeg 大量日志输出导致内存溢出 OOM
2026-08-20 11:28:35 +08:00
CI Bot ffd1b0f947 fix: HEVC 转码后先提取元数据再更新 storage_key,避免脏数据
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 1m2s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 30s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m46s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Successful in 47s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m56s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 1m24s
AI Code Review / AI Code Review (pull_request) Failing after 2m28s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 1m8s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 3m40s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 2m4s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 4m13s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 4m18s
CI/CD Pipeline / Validate - Code Quality (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
修复 AI Code Review 发现的逻辑 BUG:
- 转码成功后先提取元数据到临时变量
- 确认元数据提取成功后再更新 job.storage_key
- 避免 "新文件 + 旧元数据" 的数据不一致问题
2026-08-20 11:22:04 +08:00
CI Bot a5bc9a3ea6 style: auto-format with black + isort + prettier [skip ci-format-check]
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m43s
AI Code Review / AI Code Review (pull_request) Failing after 2m10s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 2m11s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m51s
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 34s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 52s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 30s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Successful in 59s
CI/CD Pipeline / Validate - Code Quality (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
2026-08-20 02:54:23 +00:00
CI Bot 773dafe5a8 feat: 上传时自动检测 HEVC 编码并转码为 1080p H.264
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 55s
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / PR Build Web Image (pull_request) Has been skipped
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 28s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 29s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m39s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m52s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 2m30s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 2m51s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m44s
CI/CD Pipeline / Validate - Code Quality (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
解决 4K HEVC 素材在浏览器 WebCodecs 硬件解码时输出黑帧的问题。

改动范围:仅 ingest.py(+88 行)
- metadata 提取后检测 codec 是否为 hevc/h265/hvh1
- HEVC 素材自动转码:H.264 CRF18 + 1080p + yuv420p + faststart
- 转码产物上传 OSS,storage_key 指向 _h264.mp4
- 转码后重新提取 metadata(新 codec/size/分辨率)
- 非 HEVC 素材(H.264 等)不做任何处理
- 转码失败降级使用原始文件,不阻塞上传流程
- 超时 300s,临时文件 finally 清理
2026-08-20 10:50:06 +08:00
3 changed files with 355 additions and 1 deletions
@@ -539,7 +539,9 @@ export function useCanvasPlayer(
try {
await Promise.race([
decoder.flush(),
new Promise((_, reject) => setTimeout(() => reject(new Error("flush timeout 10s")), 10_000)),
new Promise((_, reject) =>
setTimeout(() => reject(new Error("flush timeout 10s")), 10_000),
),
])
console.log(`[useCanvasPlayer] Segment ${meta.assetId}: flush complete`)
} catch (e) {
+97
View File
@@ -233,6 +233,103 @@ def ingest_asset(job_id: str) -> dict:
job_id,
thumb_err,
)
# ── HEVC 自动转码为 1080p H.264 ──────────────────────────────
# 浏览器 WebCodecs 硬件解码 HEVC 输出黑帧,上传时自动转码
# 失败时降级使用原始文件,不阻塞上传流程
if media_type == "video" and extract_success and local_file and local_file.exists():
codec = (metadata.get("codec") or "").lower()
if codec in ("hevc", "h265", "hvh1"):
logger.info(
"检测到 HEVC 编码 (codec=%s),启动转码: job_id=%s",
codec,
job_id,
)
_tc_tmp = None
try:
_tc_tmp_file = tempfile.NamedTemporaryFile(delete=False, suffix="_h264.mp4")
_tc_tmp = Path(_tc_tmp_file.name)
_tc_tmp_file.close() # 关闭文件描述符,ffmpeg 会自己打开
_cmd = [
"ffmpeg",
"-y",
"-i",
str(local_file),
"-c:v",
"libx264",
"-preset",
"fast",
"-crf",
"18",
"-vf",
"scale='if(gt(ih,1080),-2,iw)':'if(gt(ih,1080),1080,ih)'",
"-pix_fmt",
"yuv420p",
"-c:a",
"aac",
"-b:a",
"128k",
"-movflags",
"+faststart",
str(_tc_tmp),
]
_proc = subprocess.run(
_cmd,
stdout=subprocess.DEVNULL,
stderr=subprocess.PIPE,
text=True,
timeout=300,
)
if _proc.returncode == 0 and _tc_tmp.exists() and _tc_tmp.stat().st_size > 0:
from video_processing.oss_helpers import upload_to_oss
_p = Path(job.storage_key)
_new_key = str(_p.parent / (_p.stem + "_h264" + _p.suffix))
_url = upload_to_oss(_tc_tmp, _new_key)
if _url:
# 先提取元数据,确认成功后再更新 storage_key(避免脏数据)
_new_metadata, _new_extract_success = extract_media_metadata(
str(_tc_tmp),
media_type,
)
if _new_extract_success:
job.storage_key = _new_key
metadata = _new_metadata
extract_success = _new_extract_success
logger.info(
"HEVC→H.264 转码完成: job_id=%s key=%s",
job_id,
_new_key[:80],
)
else:
logger.warning(
"转码文件上传 OSS 失败,使用原始文件: job_id=%s",
job_id,
)
else:
_tail = _proc.stderr[-300:] if _proc.stderr else ""
logger.warning(
"FFmpeg 转码失败 rc=%s,降级原始文件: job_id=%s",
_proc.returncode,
job_id,
)
except subprocess.TimeoutExpired:
logger.warning(
"FFmpeg 转码超时 (300s),降级原始文件: job_id=%s",
job_id,
)
except Exception as _e:
logger.warning(
"HEVC 转码异常(降级原始文件): job_id=%s err=%s",
job_id,
_e,
)
finally:
if _tc_tmp and _tc_tmp.exists():
try:
_tc_tmp.unlink()
except OSError:
pass
finally:
if local_file and local_file.exists():
try:
+255
View File
@@ -0,0 +1,255 @@
"""HEVC 自动转码逻辑单元测试 (ingest.py)
测试覆盖:
- HEVC 编码检测逻辑
- 转码后文件命名规则
- 元数据提取失败时的脏数据防护
- FFmpeg 超时/错误降级策略
- 安全修复(tempfile、subprocess
- Scale filter 逻辑
"""
from __future__ import annotations
import subprocess
from pathlib import Path
from unittest.mock import MagicMock, patch
import pytest
class TestHEVCAutoTranscode:
"""测试 ingest_asset 中的 HEVC 自动转码逻辑"""
def test_hevc_detection_keywords(self):
"""验证 HEVC 编码的所有关键词"""
hevc_keywords = ("hevc", "h265", "hvh1")
assert "hevc" in hevc_keywords
assert "h265" in hevc_keywords
assert "hvh1" in hevc_keywords
assert "h264" not in hevc_keywords
assert "avc1" not in hevc_keywords
def test_h264_not_detected_as_hevc(self):
"""H.264 视频不应触发转码"""
codec = "h264"
hevc_keywords = ("hevc", "h265", "hvh1")
assert codec not in hevc_keywords, "H.264 不应触发转码"
def test_transcode_storage_key_naming(self):
"""验证转码后文件命名规则"""
original_key = "uploads/video_123/test.mp4"
p = Path(original_key)
new_key = str(p.parent / (p.stem + "_h264" + p.suffix))
assert new_key == "uploads/video_123/test_h264.mp4"
def test_transcode_storage_key_naming_complex_path(self):
"""验证复杂路径的命名规则"""
original_key = "uploads/2026/08/20/abc123/video_4k.mov"
p = Path(original_key)
new_key = str(p.parent / (p.stem + "_h264" + p.suffix))
assert new_key == "uploads/2026/08/20/abc123/video_4k_h264.mov"
def test_metadata_failure_no_dirty_data(self):
"""验证元数据提取失败时不更新 storage_key(避免脏数据)
这是 AI Code Review 发现的 BUG 修复:
- 旧逻辑:先更新 storage_key,再提取元数据 → 可能产生脏数据
- 新逻辑:先提取元数据,确认成功后再更新 storage_key
"""
original_storage_key = "uploads/test/video.mp4"
new_storage_key = "uploads/test/video_h264.mp4"
# 初始状态
job_storage_key = original_storage_key
metadata = {"codec": "hevc", "width": 3840, "height": 2160}
# 模拟转码成功
transcode_success = True
# 模拟元数据提取失败
new_metadata = {}
new_extract_success = False
# 修复后的逻辑:先提取元数据,确认成功后再更新
if transcode_success:
if new_extract_success:
job_storage_key = new_storage_key
metadata = new_metadata
# 如果元数据提取失败,不更新 job_storage_key
# 验证:storage_key 保持原值,没有脏数据
assert job_storage_key == original_storage_key
assert metadata["codec"] == "hevc" # 保持原始元数据
def test_metadata_success_updates_storage_key(self):
"""验证元数据提取成功时正确更新 storage_key"""
original_storage_key = "uploads/test/video.mp4"
new_storage_key = "uploads/test/video_h264.mp4"
job_storage_key = original_storage_key
metadata = {"codec": "hevc", "width": 3840, "height": 2160}
# 模拟转码成功
transcode_success = True
# 模拟元数据提取成功
new_metadata = {"codec": "h264", "width": 1920, "height": 1080}
new_extract_success = True
# 修复后的逻辑
if transcode_success:
if new_extract_success:
job_storage_key = new_storage_key
metadata = new_metadata
# 验证:storage_key 和 metadata 都更新为新值
assert job_storage_key == new_storage_key
assert metadata["codec"] == "h264"
assert metadata["width"] == 1920
@patch("subprocess.run")
def test_ffmpeg_timeout_degradation(self, mock_subprocess):
"""验证 FFmpeg 超时降级使用原始文件"""
mock_subprocess.side_effect = subprocess.TimeoutExpired(cmd="ffmpeg", timeout=300)
# 模拟降级逻辑
transcode_success = False
try:
raise subprocess.TimeoutExpired(cmd="ffmpeg", timeout=300)
except subprocess.TimeoutExpired:
transcode_success = False
assert not transcode_success, "超时应该导致转码失败"
@patch("subprocess.run")
def test_ffmpeg_error_degradation(self, mock_subprocess):
"""验证 FFmpeg 执行失败降级使用原始文件"""
mock_subprocess.return_value = MagicMock(
returncode=1,
stderr="Error: Invalid data found when processing input",
)
result = mock_subprocess.return_value
transcode_success = result.returncode == 0
assert not transcode_success, "FFmpeg 返回非零退出码应该导致转码失败"
def test_scale_filter_logic_4k_video(self):
"""验证 4K 视频会被缩放到 1080p"""
ih = 2160
should_scale = ih > 1080
assert should_scale, "4K 视频应该被缩放"
def test_scale_filter_logic_1080p_video(self):
"""验证 1080p 视频不会被缩放"""
ih = 1080
should_scale = ih > 1080
assert not should_scale, "1080p 视频不应该被缩放"
def test_scale_filter_logic_720p_video(self):
"""验证 720p 视频不会被缩放"""
ih = 720
should_scale = ih > 1080
assert not should_scale, "720p 视频不应该被缩放"
def test_tempfile_security_fix(self):
"""验证使用 NamedTemporaryFile 替代 mktemp(安全修复)
AI Code Review 发现的安全漏洞:
- tempfile.mktemp 存在 TOCTOU 竞态条件
- 应该使用 NamedTemporaryFile(delete=False)
"""
import tempfile
with patch("tempfile.NamedTemporaryFile") as mock_ntf:
mock_file = MagicMock()
mock_file.name = "/tmp/test_h264.mp4"
mock_ntf.return_value = mock_file
# 新代码的调用方式
_tc_tmp_file = tempfile.NamedTemporaryFile(delete=False, suffix="_h264.mp4")
_tc_tmp = Path(_tc_tmp_file.name)
_tc_tmp_file.close()
# 验证使用了 NamedTemporaryFile
mock_ntf.assert_called_once_with(delete=False, suffix="_h264.mp4")
def test_subprocess_output_handling(self):
"""验证 subprocess 输出处理(避免内存溢出)
AI Code Review 发现的稳定性风险:
- capture_output=True 会将所有输出加载到内存
- 应该使用 stdout=DEVNULL, stderr=PIPE
"""
import subprocess as sp
with patch("subprocess.run") as mock_run:
mock_run.return_value = MagicMock(returncode=0)
# 新代码的调用方式
sp.run(
["ffmpeg", "-i", "input.mp4", "output.mp4"],
stdout=sp.DEVNULL,
stderr=sp.PIPE,
text=True,
timeout=300,
)
# 验证使用了 stdout=DEVNULL, stderr=PIPE
call_kwargs = mock_run.call_args[1]
assert call_kwargs.get("stdout") == sp.DEVNULL
assert call_kwargs.get("stderr") == sp.PIPE
assert call_kwargs.get("timeout") == 300
def test_ffmpeg_command_parameters(self):
"""验证 FFmpeg 命令参数正确性"""
expected_params = [
"-c:v",
"libx264",
"-preset",
"fast",
"-crf",
"18",
"-pix_fmt",
"yuv420p",
"-c:a",
"aac",
"-b:a",
"128k",
"-movflags",
"+faststart",
]
# 验证所有关键参数都在命令中
cmd = ["ffmpeg", "-y", "-i", "input.mp4"]
cmd.extend(expected_params)
cmd.append("output.mp4")
assert "-c:v" in cmd
assert "libx264" in cmd
assert "-crf" in cmd
assert "18" in cmd
assert "-pix_fmt" in cmd
assert "yuv420p" in cmd
assert "-movflags" in cmd
assert "+faststart" in cmd
def test_hevc_codec_case_insensitive(self):
"""验证 HEVC 检测不区分大小写"""
test_cases = ["hevc", "HEVC", "Hevc", "h265", "H265", "hvh1", "HVH1"]
hevc_keywords = ("hevc", "h265", "hvh1")
for codec in test_cases:
assert codec.lower() in hevc_keywords, f"{codec} 应该被检测为 HEVC"
def test_non_hevc_codecs(self):
"""验证非 HEVC 编码不会触发转码"""
non_hevc_codecs = ["h264", "avc1", "vp9", "av1", "mpeg4", ""]
hevc_keywords = ("hevc", "h265", "hvh1")
for codec in non_hevc_codecs:
assert codec.lower() not in hevc_keywords, f"{codec} 不应触发转码"