146 lines
4.7 KiB
Python
146 lines
4.7 KiB
Python
|
|
# -*- coding: utf-8 -*-
|
|||
|
|
"""
|
|||
|
|
视频按大小分割脚本
|
|||
|
|
将视频分割为指定大小(默认180MB)的片段
|
|||
|
|
"""
|
|||
|
|
import os
|
|||
|
|
import subprocess
|
|||
|
|
import json
|
|||
|
|
from pathlib import Path
|
|||
|
|
import imageio_ffmpeg
|
|||
|
|
|
|||
|
|
# 配置
|
|||
|
|
INPUT_DIR = Path(__file__).parent / "原视频"
|
|||
|
|
OUTPUT_DIR = Path(__file__).parent / "分割后"
|
|||
|
|
TARGET_DURATION_MIN = 30 # 目标片段时长(分钟)
|
|||
|
|
MERGE_THRESHOLD_MIN = 10 # 最后一段小于此时长则合并到前一段
|
|||
|
|
FFMPEG_PATH = imageio_ffmpeg.get_ffmpeg_exe()
|
|||
|
|
|
|||
|
|
def get_video_info(video_path):
|
|||
|
|
"""获取视频信息:时长和文件大小(快速,只读取头部)"""
|
|||
|
|
import re
|
|||
|
|
# 只读取文件头部信息,不处理整个视频
|
|||
|
|
cmd = [
|
|||
|
|
FFMPEG_PATH,
|
|||
|
|
'-i', str(video_path),
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
result = subprocess.run(cmd, capture_output=True, text=True, encoding='utf-8')
|
|||
|
|
# 从stderr中解析时长 (ffmpeg输出信息在stderr)
|
|||
|
|
duration_match = re.search(r'Duration: (\d+):(\d+):(\d+\.?\d*)', result.stderr)
|
|||
|
|
if duration_match:
|
|||
|
|
h, m, s = duration_match.groups()
|
|||
|
|
duration = int(h) * 3600 + int(m) * 60 + float(s)
|
|||
|
|
else:
|
|||
|
|
raise ValueError("无法获取视频时长")
|
|||
|
|
|
|||
|
|
file_size = os.path.getsize(video_path)
|
|||
|
|
return duration, file_size
|
|||
|
|
|
|||
|
|
def split_video(video_path):
|
|||
|
|
"""将视频按时长分割"""
|
|||
|
|
video_path = Path(video_path)
|
|||
|
|
video_name = video_path.stem
|
|||
|
|
video_ext = video_path.suffix
|
|||
|
|
|
|||
|
|
# 获取视频信息
|
|||
|
|
duration, file_size = get_video_info(video_path)
|
|||
|
|
file_size_mb = file_size / (1024 * 1024)
|
|||
|
|
duration_min = duration / 60
|
|||
|
|
|
|||
|
|
print(f"处理: {video_path.name}", flush=True)
|
|||
|
|
print(f" 时长: {duration_min:.1f}分钟, 大小: {file_size_mb:.1f}MB", flush=True)
|
|||
|
|
|
|||
|
|
target_duration = TARGET_DURATION_MIN * 60 # 转为秒
|
|||
|
|
merge_threshold = MERGE_THRESHOLD_MIN * 60 # 转为秒
|
|||
|
|
|
|||
|
|
# 如果视频短于目标时长,直接复制
|
|||
|
|
if duration <= target_duration:
|
|||
|
|
print(f" 视频短于{TARGET_DURATION_MIN}分钟,无需分割")
|
|||
|
|
output_path = OUTPUT_DIR / f"{video_name}-1{video_ext}"
|
|||
|
|
import shutil
|
|||
|
|
shutil.copy2(video_path, output_path)
|
|||
|
|
print(f" 已复制到: {output_path.name}")
|
|||
|
|
return [output_path]
|
|||
|
|
|
|||
|
|
# 计算片段时间点
|
|||
|
|
segments = []
|
|||
|
|
start_time = 0
|
|||
|
|
while start_time < duration:
|
|||
|
|
remaining = duration - start_time
|
|||
|
|
if remaining <= target_duration + merge_threshold:
|
|||
|
|
# 剩余时间不多,作为最后一段
|
|||
|
|
segments.append((start_time, remaining))
|
|||
|
|
break
|
|||
|
|
else:
|
|||
|
|
segments.append((start_time, target_duration))
|
|||
|
|
start_time += target_duration
|
|||
|
|
|
|||
|
|
print(f" 分割为 {len(segments)} 个片段,每段约 {TARGET_DURATION_MIN} 分钟", flush=True)
|
|||
|
|
|
|||
|
|
output_files = []
|
|||
|
|
for segment_index, (start_time, actual_duration) in enumerate(segments, 1):
|
|||
|
|
output_path = OUTPUT_DIR / f"{video_name}-{segment_index}{video_ext}"
|
|||
|
|
|
|||
|
|
cmd = [
|
|||
|
|
FFMPEG_PATH,
|
|||
|
|
'-y', # 覆盖输出文件
|
|||
|
|
'-i', str(video_path),
|
|||
|
|
'-ss', str(start_time),
|
|||
|
|
'-t', str(actual_duration),
|
|||
|
|
'-c', 'copy', # 不重新编码,速度快
|
|||
|
|
'-avoid_negative_ts', 'make_zero',
|
|||
|
|
str(output_path)
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
print(f" 分割片段 {segment_index}: {start_time/60:.1f}min - {(start_time + actual_duration)/60:.1f}min", flush=True)
|
|||
|
|
|
|||
|
|
subprocess.run(cmd, capture_output=True, encoding='utf-8')
|
|||
|
|
|
|||
|
|
# 检查输出文件大小
|
|||
|
|
if output_path.exists():
|
|||
|
|
out_size = os.path.getsize(output_path) / (1024 * 1024)
|
|||
|
|
print(f" -> {output_path.name} ({out_size:.1f}MB, {actual_duration/60:.1f}min)")
|
|||
|
|
output_files.append(output_path)
|
|||
|
|
|
|||
|
|
return output_files
|
|||
|
|
|
|||
|
|
def main():
|
|||
|
|
print("=" * 50)
|
|||
|
|
print("视频分割工具")
|
|||
|
|
print(f"输入目录: {INPUT_DIR}")
|
|||
|
|
print(f"输出目录: {OUTPUT_DIR}")
|
|||
|
|
print(f"目标时长: {TARGET_DURATION_MIN}分钟/段")
|
|||
|
|
print("=" * 50)
|
|||
|
|
|
|||
|
|
# 创建输出目录
|
|||
|
|
OUTPUT_DIR.mkdir(exist_ok=True)
|
|||
|
|
|
|||
|
|
# 支持的视频格式
|
|||
|
|
video_extensions = {'.mp4', '.avi', '.mkv', '.mov', '.wmv', '.flv', '.webm'}
|
|||
|
|
|
|||
|
|
# 获取所有视频文件
|
|||
|
|
video_files = [f for f in INPUT_DIR.iterdir()
|
|||
|
|
if f.is_file() and f.suffix.lower() in video_extensions]
|
|||
|
|
|
|||
|
|
if not video_files:
|
|||
|
|
print("未找到视频文件!")
|
|||
|
|
return
|
|||
|
|
|
|||
|
|
print(f"找到 {len(video_files)} 个视频文件\n")
|
|||
|
|
|
|||
|
|
# 处理每个视频
|
|||
|
|
for video_file in video_files:
|
|||
|
|
try:
|
|||
|
|
split_video(video_file)
|
|||
|
|
print()
|
|||
|
|
except Exception as e:
|
|||
|
|
print(f" 错误: {e}\n")
|
|||
|
|
|
|||
|
|
print("=" * 50)
|
|||
|
|
print("处理完成!")
|
|||
|
|
print("=" * 50)
|
|||
|
|
|
|||
|
|
if __name__ == "__main__":
|
|||
|
|
main()
|