基于MoviePy拼接视频:组合主/子视频生成≤60秒视频
问题背景与需求
1. 已分类的子视频集合
现有按时长分类的子视频集合sub_videos:
# sub_videos ____________________________________________________________________________________ ten_second_or_below_videos = [ten_sec_video1, ten_sec_video2, ...] twenty_second_or_below_videos = [twenty_sec_video10, twenty_sec_video12, ...] thirty_second_or_below_videos = [thirty_sec_video10, thirty_sec_video12, ...] forty_second_or_below_videos = [forty_sec_video15, forty_sec_video17, ...] fifty_second_or_below_videos = [fifty_sec_video13, fifty_sec_video14, ...] sixty_or_more_second_videos = [sixty_sec_video100, sixty_sec_video101, ...] # ^^^^^^^^^^^^^ everything above this will be called "sub_videos" ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
各列表时长范围说明:
ten_second_or_below_videos:时长≤10秒twenty_second_or_below_videos:10秒<时长≤20秒thirty_second_or_below_videos:20秒<时长≤30秒forty_second_or_below_videos:30秒<时长≤40秒fifty_second_or_below_videos:40秒<时长≤50秒sixty_or_more_second_videos:时长≥60秒
2. 未分类的主视频列表
另有未按时长排序的主视频列表:
main_videos = [ten_second_video1, twenty_second_video2, ten_second_video2, ...]
3. 核心需求
- 从
main_videos中选取一个视频 - 搭配
sub_videos中的任意数量视频(可重复选同类型)拼接 - 最终拼接后的总时长必须≤60秒
- 使用MoviePy工具完成拼接并导出视频文件
示例拼接结果:
FINAL_OUTPUT = [ten_second_main_video, ten_second_sub_video,ten_second_sub_video, ten_second_sub_video, ten_second_sub_video, ten_second_sub_video] or FINAL_OUTPUT = [EXACTLY_twenty_second_main_video, EXACTLY_twenty_second_sub_video, EXACTLY_twenty_second_sub_video] # 大写EXACTLY表示视频时长刚好为20秒 or FINAL_OUTPUT = [ten_second_main_video, EXACTLY_thirty_second_sub_video, ten_second_sub_video, EXACTLY_ten_second_sub_video] etc... # 导出视频 concatenate_videoclips(FINAL_OUTPUT).write_videofile('video.mp4') # 导出文件时长≤60秒
实现方案
步骤1:预处理视频时长
先计算所有视频的精确时长,方便后续判断:
from moviepy.editor import VideoFileClip # 计算主视频的精确时长,支持文件路径或已加载的VideoFileClip对象 main_video_durations = {} for video in main_videos: if isinstance(video, str): clip = VideoFileClip(video) main_video_durations[video] = clip.duration clip.close() else: main_video_durations[video] = video.duration # 计算子视频的精确时长,同时过滤掉≥60秒的无效视频 sub_video_durations = {} # 把所有有效子视频(时长<60秒)整合到一起 valid_sub_groups = [ ten_second_or_below_videos, twenty_second_or_below_videos, thirty_second_or_below_videos, forty_second_or_below_videos, fifty_second_or_below_videos ] for group in valid_sub_groups: for video in group: if isinstance(video, str): clip = VideoFileClip(video) sub_video_durations[video] = clip.duration clip.close() else: sub_video_durations[video] = video.duration
步骤2:挑选符合条件的主视频
遍历主视频,筛选出时长≤60秒的视频,并计算剩余可用于拼接子视频的时长:
# 这里选第一个符合条件的主视频,也可以改成随机选或让用户指定 selected_main = None remaining_time = 0 for video, dur in main_video_durations.items(): if dur <= 60: selected_main = video remaining_time = 60 - dur break if not selected_main: raise ValueError("所有主视频时长都超过60秒,无法进行拼接")
步骤3:挑选子视频组合
提供两种挑选逻辑,按需选择:
逻辑A:优先凑满接近60秒的组合
从最长的有效子视频开始选,尽量用最少的视频填满剩余时长:
def pick_max_fill_subs(remaining_time, sub_durations): # 过滤出时长≤剩余时间的子视频,按时长从大到小排序 valid_subs = sorted( [(v, d) for v, d in sub_durations.items() if d <= remaining_time], key=lambda x: -x[1] ) selected = [] current_total = 0 for video, dur in valid_subs: if current_total + dur <= remaining_time: selected.append(video) current_total += dur if current_total == remaining_time: break return selected # 调用函数挑选子视频 selected_subs = pick_max_fill_subs(remaining_time, sub_video_durations)
逻辑B:随机挑选符合条件的子视频
适合需要多样化拼接结果的场景:
import random def pick_random_subs(remaining_time, sub_durations): valid_subs = [v for v, d in sub_durations.items() if d <= remaining_time] selected = [] current_total = 0 while valid_subs: candidate = random.choice(valid_subs) candidate_dur = sub_durations[candidate] if current_total + candidate_dur <= remaining_time: selected.append(candidate) current_total += candidate_dur else: valid_subs.remove(candidate) # 剩余时间不够选最小的子视频时停止 min_dur = min(sub_durations[v] for v in valid_subs) if valid_subs else float('inf') if remaining_time - current_total < min_dur: break return selected # 调用函数挑选子视频 selected_subs = pick_random_subs(remaining_time, sub_video_durations)
步骤4:拼接并导出视频
from moviepy.editor import concatenate_videoclips # 组合最终视频列表 final_output = [selected_main] + selected_subs # 转换为VideoFileClip对象(如果传入的是文件路径) final_clips = [] for item in final_output: if isinstance(item, str): final_clips.append(VideoFileClip(item)) else: final_clips.append(item) # 拼接并导出视频 concatenate_videoclips(final_clips).write_videofile('video.mp4') # 关闭所有视频剪辑,释放资源 for clip in final_clips: clip.close()
补充说明
- 自动过滤了
sixty_or_more_second_videos中的视频,因为它们时长≥60秒,无法和任何主视频拼接 - 如果需要精确凑满60秒,可以在挑选子视频时加入裁剪逻辑,比如用
video.subclip(0, target_duration)截取子视频的部分内容 - 可以扩展逻辑,支持批量生成多组不同的拼接组合,或者让用户手动选择主视频
内容的提问来源于stack exchange,提问作者HELPME
相关产品推荐
相关产品推荐

