style: normalize python formatting gates

This commit is contained in:
Xiaoxia AI
2026-06-21 06:52:19 +08:00
parent 0809a079c5
commit bfbaddbd9a
129 changed files with 3024 additions and 2485 deletions
+33 -33
View File
@@ -1,6 +1,7 @@
"""
视频处理核心类
"""
import os
import tempfile
from dataclasses import dataclass
@@ -13,6 +14,7 @@ import ffmpeg
@dataclass
class VideoResult:
"""视频生成结果"""
output_path: str
thumbnail_path: str
duration: float
@@ -24,16 +26,16 @@ class VideoResult:
class VideoProcessor:
"""视频处理器"""
def __init__(self, temp_dir: str = None):
"""
初始化视频处理器
Args:
temp_dir: 临时文件目录,默认使用系统临时目录
"""
self.temp_dir = temp_dir or tempfile.gettempdir()
def concatenate_videos(
self,
input_paths: List[str],
@@ -43,22 +45,22 @@ class VideoProcessor:
) -> VideoResult:
"""
拼接多个视频
Args:
input_paths: 输入视频路径列表
output_path: 输出视频路径
resolution: 输出分辨率 (width, height)
fps: 输出帧率
Returns:
VideoResult: 生成结果
"""
if not input_paths:
raise ValueError("input_paths cannot be empty")
# 确保输出目录存在
os.makedirs(os.path.dirname(output_path), exist_ok=True)
try:
# 创建临时文件列表
concat_file = os.path.join(self.temp_dir, f"concat_{os.getpid()}.txt")
@@ -66,12 +68,11 @@ class VideoProcessor:
for path in input_paths:
# FFmpeg concat demuxer 格式
f.write(f"file '{os.path.abspath(path)}'\n")
# 使用 FFmpeg 拼接视频
width, height = resolution
(
ffmpeg
.input(concat_file, format="concat", safe=0)
ffmpeg.input(concat_file, format="concat", safe=0)
.output(
output_path,
vcodec="libx264",
@@ -84,28 +85,28 @@ class VideoProcessor:
.overwrite_output()
.run(capture_stdout=True, capture_stderr=True)
)
# 清理临时文件
os.remove(concat_file)
# 获取视频元数据
probe = ffmpeg.probe(output_path)
video_info = next(s for s in probe["streams"] if s["codec_type"] == "video")
duration = float(probe["format"]["duration"])
width = int(video_info["width"])
height = int(video_info["height"])
# 计算帧率
fps_str = video_info.get("r_frame_rate", "25/1")
fps_parts = fps_str.split("/")
fps_value = float(fps_parts[0]) / float(fps_parts[1]) if len(fps_parts) == 2 else float(fps_parts[0])
file_size = os.path.getsize(output_path)
# 生成缩略图
thumbnail_path = self.generate_thumbnail(output_path)
return VideoResult(
output_path=output_path,
thumbnail_path=thumbnail_path,
@@ -115,11 +116,11 @@ class VideoProcessor:
fps=fps_value,
file_size=file_size,
)
except ffmpeg.Error as e:
stderr = e.stderr.decode() if e.stderr else ""
raise RuntimeError(f"FFmpeg error: {stderr}") from e
def generate_thumbnail(
self,
video_path: str,
@@ -128,55 +129,54 @@ class VideoProcessor:
) -> str:
"""
生成视频缩略图
Args:
video_path: 视频文件路径
timestamp: 截图时间点(秒)
output_path: 输出路径,默认为视频路径 + .jpg
Returns:
缩略图路径
"""
if output_path is None:
output_path = f"{os.path.splitext(video_path)[0]}_thumb.jpg"
try:
(
ffmpeg
.input(video_path, ss=timestamp)
ffmpeg.input(video_path, ss=timestamp)
.output(output_path, vframes=1, format="image2", vcodec="mjpeg")
.overwrite_output()
.run(capture_stdout=True, capture_stderr=True)
)
return output_path
except ffmpeg.Error as e:
stderr = e.stderr.decode() if e.stderr else ""
raise RuntimeError(f"FFmpeg thumbnail error: {stderr}") from e
def get_video_info(self, video_path: str) -> dict:
"""
获取视频信息
Args:
video_path: 视频文件路径
Returns:
视频元数据字典
"""
try:
probe = ffmpeg.probe(video_path)
video_info = next(s for s in probe["streams"] if s["codec_type"] == "video")
duration = float(probe["format"]["duration"])
width = int(video_info["width"])
height = int(video_info["height"])
fps_str = video_info.get("r_frame_rate", "25/1")
fps_parts = fps_str.split("/")
fps_value = float(fps_parts[0]) / float(fps_parts[1]) if len(fps_parts) == 2 else float(fps_parts[0])
return {
"duration": duration,
"width": width,
@@ -185,7 +185,7 @@ class VideoProcessor:
"codec": video_info.get("codec_name"),
"bitrate": int(probe["format"].get("bit_rate", 0)),
}
except ffmpeg.Error as e:
stderr = e.stderr.decode() if e.stderr else ""
raise RuntimeError(f"FFmpeg probe error: {stderr}") from e