1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
| #!/bin/bash
# ========= 环境检查 ========= if ! command -v ffmpeg &>/dev/null || ! command -v ffprobe &>/dev/null; then echo "请先安装 ffmpeg 和 ffprobe" exit 1 fi
echo "🎬 欢迎使用 视频分割小工具"
# ========= 文件大小格式化 ========= format_size() { local bytes=$1 if (( bytes < 1073741824 )); then size=$(echo "scale=2; $bytes / (1024 * 1024)" | bc) printf "%.2fMB" "$size" else size=$(echo "scale=2; $bytes / (1024 * 1024 * 1024)" | bc) printf "%.2fGB" "$size" fi }
# ========= 主循环 ========= while true; do echo "" echo "📥 拖入视频文件,或输入 q 退出:" IFS= read -r input_line [[ "$input_line" == "q" ]] && echo "👋 已退出" && exit 0
eval "set -- $input_line" inputs=() for arg in "$@"; do inputs+=("$arg") done
for input in "${inputs[@]}"; do if [ ! -f "$input" ]; then echo "❗ 文件不存在:$input,跳过..." continue fi
echo "" echo "🚀 正在处理:$input"
filename=$(basename "$input") basename_no_ext="${filename%.*}" dir=$(dirname "$input") output_dir="$dir/${basename_no_ext}_parts" mkdir -p "$output_dir"
filesize=$(stat -f%z "$input") filesize_human=$(format_size "$filesize") echo "📋 文件大小:$filesize_human"
# ========= 输入每段大小 ========= echo "" read -p "请输入每段大小(GB,默认 1.9): " size_gb if [[ -z "$size_gb" ]]; then size_gb=1.9 echo "📦 使用默认大小:1.9GB" else if ! [[ "$size_gb" =~ ^[0-9]+(\.[0-9]+)?$ ]]; then echo "❌ 输入格式错误" continue fi echo "📦 每段大小:${size_gb}GB" fi
max_size=$(echo "$size_gb * 1024^3" | bc | cut -d'.' -f1)
duration=$(ffprobe -v error -show_entries format=duration -of csv=p=0 "$input") duration_int=$(printf "%.0f" "$duration") echo "📋 总时长:$duration_int 秒"
# ========= 智能估算分段 ========= estimated_parts=$(echo "($filesize + $max_size - 1) / $max_size" | bc) part_duration=$(echo "$duration / $estimated_parts" | bc) part_duration_int=$(printf "%.0f" "$part_duration") ((part_duration_int <= 0)) && part_duration_int=1
echo "🚀 预计分割为 $estimated_parts 段" echo "⏱ 每段约 ${part_duration_int}s(关键帧对齐)"
# ========= 临时输出 ========= tmp_dir="$output_dir/_tmp_segments" mkdir -p "$tmp_dir"
ffmpeg -loglevel quiet \ -fflags +genpts \ -i "$input" \ -c copy \ -map 0 \ -f segment \ -segment_time "$part_duration_int" \ -segment_time_delta 5 \ -reset_timestamps 1 \ -avoid_negative_ts make_zero \ "$tmp_dir/segment%03d.${filename##*.}"
# ========= 重命名 + 尾段合并 ========= idx=1 files=($(ls "$tmp_dir" | sort)) num_files=${#files[@]}
for i in "${!files[@]}"; do f="${files[$i]}" file_path="$tmp_dir/$f" size=$(stat -f%z "$file_path") # 检查是否是最后一段且小于前一段 5% if (( i == num_files - 1 && num_files > 1 )); then prev_file="$output_dir/${basename_no_ext}_part$(printf "%03d" $((idx-1))).${filename##*.}" prev_size=$(stat -f%z "$prev_file") threshold=$(echo "$prev_size * 0.05" | bc | cut -d'.' -f1) if (( size < threshold )); then # 合并到前一段 echo "⚡ 尾段太小,合并到上一段..." ffmpeg -loglevel quiet -i "concat:$prev_file|$file_path" -c copy "$prev_file.tmp" && mv "$prev_file.tmp" "$prev_file" continue fi fi
mv "$file_path" "$output_dir/${basename_no_ext}_part$(printf "%03d" $idx).${filename##*.}" ((idx++)) done
rm -rf "$tmp_dir"
echo "" echo "✅ 分割完成" echo "📂 输出目录:$output_dir"
for file in "$output_dir"/*; do size_human=$(du -h "$file" | cut -f1) echo " - $(basename "$file") [$size_human]" done
open "$output_dir" done done
|