前言

  • 众所周知,我们可以将 Telegram 当作一个无限容量的云盘,但是 Telegram 对普通用户做出了限制,即单个文件不可超过 2GB。当需要上传较大体积的视频文件又不想过度压缩损失清晰度的时候,就可以选择将文件分割为数段上传。
  • FFmpeg 是一款非常强大的音视频处理工具,应用非常广泛,几乎所有跟音视频处理相关的工具都有用到它,可以称为最伟大的开源工具之一。
  • FFmpeg 虽然很强大,但作为一个命令行工具,还有非常多的命令参数,普通用户使用起来还是多有不便。不过这里只要简单地将视频文件切割,所以只要用个简单的脚本处理就好了。
  • 自用脚本,由 ChatGPT 创作,仅适用 macOS。

使用

创建脚本文件

1
sudo vim ~/.local/bin/split_video.sh

写入脚本内容

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
#!/bin/bash

# ========= 环境检查 =========
if ! command -v ffmpeg &>/dev/null || ! command -v ffprobe &>/dev/null; then
echo "请先安装 ffmpeg 和 ffprobe"
exit 1
fi

echo "🎬 欢迎使用 视频分割小工具 for Mac"

# ========= 文件大小格式化 =========
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(关键帧对齐,实际略有浮动)"

# ========= 稳定无损分割 =========
ffmpeg -loglevel quiet \
-fflags +genpts \
-i "$input" \
-c copy \
-map 0 \
-f segment \
-segment_time "$part_duration_int" \
-segment_time_delta 0.5 \
-reset_timestamps 1 \
-avoid_negative_ts make_zero \
"$output_dir/${basename_no_ext}_part%03d.${filename##*.}"

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

赋权

1
sudo chmod +x ~/.local/bin/split_video.sh

执行

1
split_video.sh

后记

  • 直接将需要切割的视频文件拖入终端,即可指定切割完成后的单个文件大小。
  • 脚本会在原视频同级目录下创建名为video-name_parts的文件夹,分割完成后的文件会自动命名并放入此文件夹。
  • 支持中文路径和中文文件名。