Spaces:
Running
Running
""" | |
使用 moviepy 合并视频、音频、字幕和背景音乐 | |
""" | |
from moviepy.editor import ( | |
VideoFileClip, | |
AudioFileClip, | |
TextClip, | |
CompositeVideoClip, | |
concatenate_videoclips | |
) | |
# from moviepy.config import change_settings | |
import os | |
# 设置字体文件路径(用于中文字幕显示) | |
FONT_PATH = "../../resource/fonts/STHeitiMedium.ttc" # 请确保此路径下有对应字体文件 | |
# change_settings( | |
# {"IMAGEMAGICK_BINARY": r"C:\Program Files\ImageMagick-7.1.1-Q16\magick.exe"}) # Windows系统需要设置 ImageMagick 路径 | |
class VideoMerger: | |
"""视频合并处理类""" | |
def __init__(self, output_path: str = "../../resource/videos/merged_video.mp4"): | |
""" | |
初始化视频合并器 | |
参数: | |
output_path: 输出文件路径 | |
""" | |
self.output_path = output_path | |
self.video_clips = [] | |
self.background_music = None | |
self.subtitles = [] | |
def add_video(self, video_path: str, start_time: str = None, end_time: str = None) -> None: | |
""" | |
添加视频片段 | |
参数: | |
video_path: 视频文件路径 | |
start_time: 开始时间 (格式: "MM:SS") | |
end_time: 结束时间 (格式: "MM:SS") | |
""" | |
video = VideoFileClip(video_path) | |
if start_time and end_time: | |
video = video.subclip(self._time_to_seconds(start_time), | |
self._time_to_seconds(end_time)) | |
self.video_clips.append(video) | |
def add_audio(self, audio_path: str, volume: float = 1.0) -> None: | |
""" | |
添加背景音乐 | |
参数: | |
audio_path: 音频文件路径 | |
volume: 音量大小 (0.0-1.0) | |
""" | |
self.background_music = AudioFileClip(audio_path).volumex(volume) | |
def add_subtitle(self, text: str, start_time: str, end_time: str, | |
position: tuple = ('center', 'bottom'), fontsize: int = 24) -> None: | |
""" | |
添加字幕 | |
参数: | |
text: 字幕文本 | |
start_time: 开始时间 (格式: "MM:SS") | |
end_time: 结束时间 (格式: "MM:SS") | |
position: 字幕位置 | |
fontsize: 字体大小 | |
""" | |
subtitle = TextClip( | |
text, | |
font=FONT_PATH, | |
fontsize=fontsize, | |
color='white', | |
stroke_color='black', | |
stroke_width=2 | |
) | |
subtitle = subtitle.set_position(position).set_duration( | |
self._time_to_seconds(end_time) - self._time_to_seconds(start_time) | |
).set_start(self._time_to_seconds(start_time)) | |
self.subtitles.append(subtitle) | |
def merge(self) -> None: | |
"""合并所有媒体元素并导出视频""" | |
if not self.video_clips: | |
raise ValueError("至少需要添加一个视频片段") | |
# 合并视频片段 | |
final_video = concatenate_videoclips(self.video_clips) | |
# 如果有背景音乐,设置其持续时间与视频相同 | |
if self.background_music: | |
self.background_music = self.background_music.set_duration(final_video.duration) | |
final_video = final_video.set_audio(self.background_music) | |
# 添加字幕 | |
if self.subtitles: | |
final_video = CompositeVideoClip([final_video] + self.subtitles) | |
# 导出最终视频 | |
final_video.write_videofile( | |
self.output_path, | |
fps=24, | |
codec='libx264', | |
audio_codec='aac' | |
) | |
# 释放资源 | |
final_video.close() | |
for clip in self.video_clips: | |
clip.close() | |
if self.background_music: | |
self.background_music.close() | |
def _time_to_seconds(time_str: str) -> float: | |
"""将时间字符串转换为秒数""" | |
minutes, seconds = map(int, time_str.split(':')) | |
return minutes * 60 + seconds | |
def test_merge_video(): | |
"""测试视频合并功能""" | |
merger = VideoMerger() | |
# 添加两个视频片段 | |
merger.add_video("../../resource/videos/cut_video.mp4", "00:00", "01:00") | |
merger.add_video("../../resource/videos/demo.mp4", "00:00", "00:30") | |
# 添加背景音乐 | |
merger.add_audio("../../resource/songs/output000.mp3", volume=0.3) | |
# 添加字幕 | |
merger.add_subtitle("第一个精彩片段", "00:00", "00:05") | |
merger.add_subtitle("第二个精彩片段", "01:00", "01:05") | |
# 合并并导出 | |
merger.merge() | |
if __name__ == "__main__": | |
test_merge_video() | |