This commit is contained in:
2025-09-24 04:51:12 +08:00
parent a54c157f9a
commit 6d37e7c23c
12 changed files with 548 additions and 101 deletions

View File

@@ -2,6 +2,9 @@ import json
import logging
import os
import threading
import time
from urllib3.util.retry import Retry
from requests.adapters import HTTPAdapter
import requests
from opentelemetry.trace import Status, StatusCode
@@ -10,7 +13,30 @@ import util.system
from telemetry import get_tracer
from util import oss
# 创建带有连接池和重试策略的会话
session = requests.Session()
# 配置重试策略
retry_strategy = Retry(
total=3,
status_forcelist=[429, 500, 502, 503, 504],
backoff_factor=1,
respect_retry_after_header=True
)
# 配置HTTP适配器(连接池)
adapter = HTTPAdapter(
pool_connections=10,
pool_maxsize=20,
max_retries=retry_strategy
)
session.mount("http://", adapter)
session.mount("https://", adapter)
# 设置默认超时
session.timeout = 30
logger = logging.getLogger(__name__)

127
util/ffmpeg_utils.py Normal file
View File

@@ -0,0 +1,127 @@
"""
FFmpeg工具模块 - 提供FFmpeg命令构建和处理的公共函数
"""
import logging
from typing import List, Tuple, Optional
from config.settings import get_ffmpeg_config
logger = logging.getLogger(__name__)
def build_base_ffmpeg_args() -> List[str]:
"""
构建基础FFmpeg参数
Returns:
基础参数列表
"""
config = get_ffmpeg_config()
args = ["ffmpeg", "-y", "-hide_banner"]
args.extend(config.progress_args)
args.extend(config.loglevel_args)
return args
def build_null_audio_input() -> List[str]:
"""
构建空音频输入参数
Returns:
空音频输入参数列表
"""
config = get_ffmpeg_config()
return config.null_audio_args
def build_amix_filter(input1: str, input2: str, output: str) -> str:
"""
构建音频混合滤镜
Args:
input1: 第一个音频输入
input2: 第二个音频输入
output: 输出流名称
Returns:
混合滤镜字符串
"""
config = get_ffmpeg_config()
return f"{input1}[{input2}]{config.amix_args[0]}[{output}]"
def build_overlay_scale_filter(video_input: str, overlay_input: str, output: str) -> str:
"""
构建覆盖层缩放滤镜
Args:
video_input: 视频输入流
overlay_input: 覆盖层输入流
output: 输出流名称
Returns:
缩放滤镜字符串
"""
config = get_ffmpeg_config()
if config.overlay_scale_mode == "scale":
return f"{video_input}[{overlay_input}]scale=iw:ih[{output}]"
else:
return f"{video_input}[{overlay_input}]{config.overlay_scale_mode}=iw:ih[{output}]"
def get_annexb_filter() -> str:
"""
获取annexb转换滤镜
Returns:
annexb滤镜名称
"""
config = get_ffmpeg_config()
encoder_args_str = " ".join(config.encoder_args).lower()
if "hevc" in encoder_args_str:
return "hevc_mp4toannexb"
return "h264_mp4toannexb"
def build_standard_output_args() -> List[str]:
"""
构建标准输出参数
Returns:
输出参数列表
"""
config = get_ffmpeg_config()
return [
*config.video_args,
*config.audio_args,
*config.encoder_args,
*config.default_args
]
def validate_ffmpeg_file_extensions(file_path: str) -> bool:
"""
验证文件扩展名是否为FFmpeg支持的格式
Args:
file_path: 文件路径
Returns:
是否为支持的格式
"""
supported_extensions = {
'.mp4', '.avi', '.mov', '.mkv', '.flv', '.wmv', '.webm',
'.ts', '.m2ts', '.mts', '.m4v', '.3gp', '.asf', '.rm',
'.mp3', '.wav', '.aac', '.flac', '.ogg', '.m4a', '.wma'
}
import os
_, ext = os.path.splitext(file_path.lower())
return ext in supported_extensions
def estimate_processing_time(input_duration: float, complexity_factor: float = 1.0) -> float:
"""
估算处理时间
Args:
input_duration: 输入文件时长(秒)
complexity_factor: 复杂度因子(1.0为普通处理)
Returns:
预估处理时间(秒)
"""
# 基础处理速度假设为实时的0.5倍(即处理1秒视频需要2秒)
base_processing_ratio = 2.0
return input_duration * base_processing_ratio * complexity_factor

92
util/json_utils.py Normal file
View File

@@ -0,0 +1,92 @@
"""
JSON处理工具模块 - 提供安全的JSON解析和处理功能
"""
import json
import logging
from typing import Dict, Any, Optional, Union
logger = logging.getLogger(__name__)
def safe_json_loads(json_str: Union[str, bytes], default: Any = None) -> Any:
"""
安全解析JSON字符串
Args:
json_str: JSON字符串
default: 解析失败时返回的默认值
Returns:
解析后的对象,或默认值
"""
if not json_str or json_str == '{}':
return default or {}
try:
return json.loads(json_str)
except (json.JSONDecodeError, TypeError) as e:
logger.warning(f"Failed to parse JSON: {e}, input: {json_str}")
return default or {}
def safe_json_dumps(obj: Any, indent: Optional[int] = None, ensure_ascii: bool = False) -> str:
"""
安全序列化对象为JSON字符串
Args:
obj: 要序列化的对象
indent: 缩进空格数
ensure_ascii: 是否确保ASCII编码
Returns:
JSON字符串
"""
try:
return json.dumps(obj, indent=indent, ensure_ascii=ensure_ascii)
except (TypeError, ValueError) as e:
logger.error(f"Failed to serialize to JSON: {e}")
return "{}"
def get_nested_value(data: Dict[str, Any], key_path: str, default: Any = None) -> Any:
"""
从嵌套字典中安全获取值
Args:
data: 字典数据
key_path: 键路径,用点分隔(如 "user.profile.name"
default: 默认值
Returns:
找到的值或默认值
"""
if not isinstance(data, dict):
return default
try:
keys = key_path.split('.')
current = data
for key in keys:
if isinstance(current, dict) and key in current:
current = current[key]
else:
return default
return current
except Exception as e:
logger.warning(f"Failed to get nested value for path '{key_path}': {e}")
return default
def merge_dicts(*dicts: Dict[str, Any]) -> Dict[str, Any]:
"""
合并多个字典,后面的字典会覆盖前面的字典中相同的键
Args:
*dicts: 要合并的字典
Returns:
合并后的字典
"""
result = {}
for d in dicts:
if isinstance(d, dict):
result.update(d)
return result