Spaces:
Runtime error
Runtime error
| """ | |
| 视频处理模块 - 基于MoviePy | |
| Video Processing Module - Based on MoviePy | |
| 本模块提供视频编辑、剪辑、合成等功能 | |
| This module provides video editing, clipping, and composition features | |
| """ | |
| from typing import Optional, List, Tuple | |
| import warnings | |
| try: | |
| from moviepy.editor import ( | |
| VideoFileClip, | |
| AudioFileClip, | |
| ImageClip, | |
| TextClip, | |
| CompositeVideoClip, | |
| concatenate_videoclips, | |
| vfx | |
| ) | |
| MOVIEPY_AVAILABLE = True | |
| except ImportError: | |
| MOVIEPY_AVAILABLE = False | |
| warnings.warn("moviepy库未安装,请运行: pip install moviepy") | |
| class VideoEditor: | |
| """视频编辑器""" | |
| def __init__(self, video_path: Optional[str] = None): | |
| """ | |
| 初始化视频编辑器 | |
| Args: | |
| video_path: 视频文件路径 | |
| """ | |
| if not MOVIEPY_AVAILABLE: | |
| raise ImportError("需要安装moviepy库") | |
| self.video = None | |
| if video_path: | |
| self.load_video(video_path) | |
| def load_video(self, video_path: str): | |
| """ | |
| 加载视频文件 | |
| Args: | |
| video_path: 视频文件路径 | |
| """ | |
| self.video = VideoFileClip(video_path) | |
| return self | |
| def cut_video(self, start_time: float, end_time: float) -> 'VideoEditor': | |
| """ | |
| 剪辑视频 | |
| Args: | |
| start_time: 开始时间(秒) | |
| end_time: 结束时间(秒) | |
| Returns: | |
| VideoEditor实例 | |
| """ | |
| if self.video is None: | |
| raise ValueError("请先加载视频") | |
| self.video = self.video.subclip(start_time, end_time) | |
| return self | |
| def resize_video(self, width: Optional[int] = None, height: Optional[int] = None) -> 'VideoEditor': | |
| """ | |
| 调整视频尺寸 | |
| Args: | |
| width: 目标宽度 | |
| height: 目标高度 | |
| Returns: | |
| VideoEditor实例 | |
| """ | |
| if self.video is None: | |
| raise ValueError("请先加载视频") | |
| if width and height: | |
| self.video = self.video.resize((width, height)) | |
| elif width: | |
| self.video = self.video.resize(width=width) | |
| elif height: | |
| self.video = self.video.resize(height=height) | |
| return self | |
| def add_audio(self, audio_path: str) -> 'VideoEditor': | |
| """ | |
| 添加音频 | |
| Args: | |
| audio_path: 音频文件路径 | |
| Returns: | |
| VideoEditor实例 | |
| """ | |
| if self.video is None: | |
| raise ValueError("请先加载视频") | |
| audio = AudioFileClip(audio_path) | |
| self.video = self.video.set_audio(audio) | |
| return self | |
| def add_text(self, text: str, position: Tuple[int, int] = ('center', 'center'), | |
| duration: Optional[float] = None, fontsize: int = 50, color: str = 'white') -> 'VideoEditor': | |
| """ | |
| 添加文字 | |
| Args: | |
| text: 文字内容 | |
| position: 文字位置 | |
| duration: 显示时长(秒),None表示整个视频时长 | |
| fontsize: 字体大小 | |
| color: 字体颜色 | |
| Returns: | |
| VideoEditor实例 | |
| """ | |
| if self.video is None: | |
| raise ValueError("请先加载视频") | |
| if duration is None: | |
| duration = self.video.duration | |
| txt_clip = TextClip(text, fontsize=fontsize, color=color) | |
| txt_clip = txt_clip.set_position(position).set_duration(duration) | |
| self.video = CompositeVideoClip([self.video, txt_clip]) | |
| return self | |
| def speed_up(self, factor: float = 2.0) -> 'VideoEditor': | |
| """ | |
| 加速视频 | |
| Args: | |
| factor: 加速倍数 | |
| Returns: | |
| VideoEditor实例 | |
| """ | |
| if self.video is None: | |
| raise ValueError("请先加载视频") | |
| self.video = self.video.fx(vfx.speedx, factor) | |
| return self | |
| def fade_in(self, duration: float = 1.0) -> 'VideoEditor': | |
| """ | |
| 添加淡入效果 | |
| Args: | |
| duration: 淡入时长(秒) | |
| Returns: | |
| VideoEditor实例 | |
| """ | |
| if self.video is None: | |
| raise ValueError("请先加载视频") | |
| self.video = self.video.fx(vfx.fadein, duration) | |
| return self | |
| def fade_out(self, duration: float = 1.0) -> 'VideoEditor': | |
| """ | |
| 添加淡出效果 | |
| Args: | |
| duration: 淡出时长(秒) | |
| Returns: | |
| VideoEditor实例 | |
| """ | |
| if self.video is None: | |
| raise ValueError("请先加载视频") | |
| self.video = self.video.fx(vfx.fadeout, duration) | |
| return self | |
| def save(self, output_path: str, fps: int = 24, codec: str = 'libx264'): | |
| """ | |
| 保存视频 | |
| Args: | |
| output_path: 输出文件路径 | |
| fps: 帧率 | |
| codec: 视频编码器 | |
| """ | |
| if self.video is None: | |
| raise ValueError("请先加载视频") | |
| self.video.write_videofile(output_path, fps=fps, codec=codec) | |
| def get_info(self) -> dict: | |
| """ | |
| 获取视频信息 | |
| Returns: | |
| 包含视频信息的字典 | |
| """ | |
| if self.video is None: | |
| raise ValueError("请先加载视频") | |
| return { | |
| 'duration': self.video.duration, | |
| 'fps': self.video.fps, | |
| 'size': self.video.size, | |
| 'width': self.video.w, | |
| 'height': self.video.h | |
| } | |
| class VideoMerger: | |
| """视频合并器""" | |
| def merge_videos(video_paths: List[str], output_path: str, method: str = 'concatenate'): | |
| """ | |
| 合并多个视频 | |
| Args: | |
| video_paths: 视频文件路径列表 | |
| output_path: 输出文件路径 | |
| method: 合并方法,'concatenate'表示顺序连接 | |
| """ | |
| if not MOVIEPY_AVAILABLE: | |
| raise ImportError("需要安装moviepy库") | |
| clips = [VideoFileClip(path) for path in video_paths] | |
| if method == 'concatenate': | |
| final_clip = concatenate_videoclips(clips) | |
| else: | |
| raise ValueError(f"不支持的合并方法: {method}") | |
| final_clip.write_videofile(output_path) | |
| # 清理资源 | |
| for clip in clips: | |
| clip.close() | |
| final_clip.close() | |
| # 使用示例 | |
| if __name__ == "__main__": | |
| # 创建视频编辑器 | |
| editor = VideoEditor("input_video.mp4") | |
| # 剪辑视频(0-10秒) | |
| editor.cut_video(0, 10) | |
| # 调整尺寸为720p | |
| editor.resize_video(width=1280, height=720) | |
| # 添加淡入淡出效果 | |
| editor.fade_in(1.0).fade_out(1.0) | |
| # 保存视频 | |
| editor.save("output_video.mp4") | |
| # 获取视频信息 | |
| info = editor.get_info() | |
| print(f"视频时长: {info['duration']}秒") | |
| print(f"视频尺寸: {info['width']}x{info['height']}") | |