基于 Streamlit + FFmpeg 的批量视频处理工具,聚焦 macOS 场景。
当前提供两个功能页:
- 视频拼接:下载源视频后自动拼接落版片尾,输出 MP4
- 视频链接转附件:批量下载公开视频链接,并规范化输出为以
item_id命名的 MP4 附件
这个项目的目标很直接:让业务同学在网页里粘贴数据、预览校验、批量处理,再一键下载或打开本地结果目录,不需要关心 FFmpeg 命令和中间文件。
- 双页面工作流:同一应用内切换“视频拼接”和“视频链接转附件”
- 先预览后处理:必须先点击“确认数据并预览”,校验通过后才能开始处理
- 纯文本输入:两页统一使用双列文本输入,直接粘贴即可处理
- 批量并发执行:支持多条任务同时下载和处理,并实时展示日志与进度
- 落版视频可上传管理:拼接页顶部支持上传
mp4/mov落版视频,上传后持久化保存,可通过媒体直链预览、可重新上传 - 结果自动打包:单条成功时直接下载 MP4,多条结果下载 ZIP,并附带
result.csv - 结果自动落地:每次处理都会保存到
~/Downloads/video-splicer-results/下的独立目录 - 附件统一输出 MP4:优先无损封装,失败时自动回退到 H.264/AAC 转码
- 拼接链路更稳:会探测码率、补齐缺失音轨,并将落版视频缩放到源视频分辨率
- 支持打开结果目录:处理完成后可直接从页面打开本地结果文件夹
- 网页内可调参数:支持修改单条视频大小上限、并发数、单任务超时和下载重试次数
- 批量给商品视频统一拼接落版片尾
- 批量把公开视频链接整理成可交付的 MP4 附件
- 需要非技术同学在浏览器里完成批量视频处理
- 需要在本机保留结果归档、失败明细和导出记录
- 克隆仓库并进入目录
- macOS
- Python 3.11+
ffmpeg/ffprobe- 项目虚拟环境
.venv
git clone https://github.com/yycy134679/python-video-splicing.git
cd python-video-splicing安装 FFmpeg:
brew install ffmpeg创建虚拟环境并安装依赖:
python3 -m venv .venv
source .venv/bin/activate
pip install -U pip
pip install -r requirements.txt如果你使用 fish shell,可以改用:
source .venv/bin/activate.fish“视频拼接”页默认使用 assets/video/endcard.mp4 作为初始落版视频。
如果业务同学需要替换落版,不再需要改代码或手动改环境变量,直接在页面顶部“落版视频管理”区上传 mp4 / mov 文件即可。上传后的文件会持久化保存到本机,只保留一个“当前生效”的版本,页面刷新和应用重启后仍继续生效。
如果你确实需要在源码运行时指定另一份默认落版,仍可继续使用环境变量覆盖:
export SP_ENDCARD_PATH="/绝对路径/endcard.mp4"streamlit run app.py默认访问地址为 http://localhost:8501。
- 页面顶部提供紧凑的落版摘要条,可查看当前生效落版的来源、文件名和更新日期
- 点击“预览落版”会直接打开当前落版视频的媒体直链,交给浏览器原生播放器预览,不会再把主页面撑高
- 支持上传
mp4、mov两种落版视频;点击“更换落版”会直接弹出系统文件选择器,选中文件后立即覆盖当前落版 - 上传后的落版视频会持久化保存到本机,页面刷新和应用重启后仍继续生效
- 如果还没有上传过新落版,系统默认使用
assets/video/endcard.mp4 - 重新上传新落版只影响后续新发起的拼接任务,不会中途切换已在处理中的批次
- 左侧输入
pid,右侧输入video_url,按行一一对应 - 仅支持页面内双列文本输入,不再提供 CSV / Excel 上传入口
- 仅支持公开的
http/https视频链接 - 输出文件按输入顺序命名为
1.mp4、2.mp4、3.mp4... - 拼接前会校验运行环境,包括
ffmpeg、ffprobe和落版视频路径
注意:当前只保留一个“当前生效”的落版视频,不保留历史版本。
- 左侧输入
item_id,右侧输入video_url,按行一一对应 - 仅支持页面内双列文本输入,不再提供 CSV / Excel 上传入口
- 输出文件固定为
item_id.mp4 - 当
item_id重复时,自动追加__2、__3等后缀避免覆盖 - 该页面不依赖落版视频,只要求运行机器本身能访问目标链接
- 未点击“确认数据并预览”前,“开始处理”按钮不可点击
- 文本框中间的空行会被自动忽略,再按非空行进行配对
- 两列行数不一致时会直接阻止处理
- 仅校验页面内两列文本输入,空行会自动忽略
- 视频链接不合法、标识符为空时会在预览阶段直接拦截
- 单条成功结果:直接下载对应的
.mp4 - 单条失败结果:下载
result.csv - 多条结果:下载
results-时间戳.zip - ZIP 内只包含成功产出的 MP4 和
result.csv - 本地结果目录默认保存到
~/Downloads/video-splicer-results/ - 每次处理都会生成独立目录,例如
splice-20260318-120000/、attachment-20260318-120000/
侧边栏提供四个运行时参数,修改后仅影响当前网页里后续发起的任务:
| 参数 | 默认值 | 说明 |
|---|---|---|
单条视频大小上限 |
50 MB |
超过后直接拦截,避免误下载超大文件 |
同时处理数量 |
4 |
并行任务数,越大越快,也越占用本机资源 |
单条任务最长等待时间 |
180 秒 |
从下载到处理完成的总超时 |
下载失败自动重试次数 |
2 |
下载偶发失败时的自动重试次数 |
| 环境变量 | 默认值 | 说明 |
|---|---|---|
SP_ENDCARD_PATH |
assets/video/endcard.mp4 |
拼接页默认落版视频路径,源码运行时可覆盖 |
SP_RESULTS_ROOT_DIR |
~/Downloads/video-splicer-results |
本地结果保存根目录 |
SP_MAX_VIDEO_MB |
50 |
单条源视频最大体积(MB) |
SP_MAX_WORKERS |
4 |
最大并发线程数 |
SP_TASK_TIMEOUT_SEC |
180 |
单任务超时时间(秒) |
SP_DOWNLOAD_RETRIES |
2 |
下载最大重试次数 |
SP_APP_PORT |
8501 |
通过 launcher.py 启动时使用的端口 |
- 下载源视频
- 使用
ffprobe探测分辨率、时长、码率和音轨信息 - 当源视频无音轨时自动补静音轨
- 将落版视频缩放并补边到与源视频一致的分辨率
- 使用
ffmpeg拼接为最终 MP4
- 下载源视频
- 优先尝试直接封装为 MP4
- 若封装失败,则回退为 H.264/AAC 转码
- 输出统一为可交付的
.mp4
项目包含完整的 macOS 打包脚本,会先用 PyInstaller 生成 COLLECT 产物,再手动组装 .app,避免中文应用名和图标处理不稳定的问题。
执行打包:
bash build_macos_app.sh默认产物:
dist/视频拼接工具.appdist/视频拼接工具-v最终版.zip
如需额外生成 DMG:
BUILD_DMG=1 bash build_macos_app.shNote
打包脚本要求在 .venv 中执行,并且本机已安装 Homebrew 版 ffmpeg / ffprobe。打包后的 .app 会携带运行所需依赖,交付同事后通常无需再安装 Python 或 FFmpeg。
请在虚拟环境中执行:
pytest当前测试覆盖的重点包括:
- 输入解析与校验
- 附件命名与重名处理
- 结果打包与落盘策略
- 运行时配置覆盖
launcher.py的单实例与端口复用逻辑
.
├── app.py
├── launcher.py
├── build_macos_app.sh
├── video_splicing.spec
├── requirements.txt
├── assets/
│ └── logo.png
├── video_splicer/
│ ├── artifact.py
│ ├── config.py
│ ├── endcard_store.py
│ ├── downloader.py
│ ├── ffmpeg_pipeline.py
│ ├── input_parser.py
│ ├── models.py
│ └── runner.py
└── tests/
- 文档和注释默认使用简体中文
- 新依赖、打包流程或核心交互有变化时,应同步更新本 README
- 如果改动影响业务逻辑、公共接口、数据结构或构建过程,优先在工作分支完成开发后再合并
