Video To Short Processor
这是 video-to-short MVP 的 Modal 视频处理器。
它是主 Cloudflare 应用背后的“视频加工服务”。它不负责用户、任务列表、计费、页面 UI 或业务数据库,只负责把一个源视频处理成一个 MVP 版竖屏短视频,并把结果写回 Cloudflare R2。
当前 MVP 能力
- 通过
sourceKey从 R2 读取源视频,或者通过sourceDownloadUrl从临时 HTTP URL 下载源视频。 - 使用 FFmpeg 截取一段开头片段和一段中间片段。
- 拼接片段并转成竖屏 MP4。
- 上传最终视频到 R2。
- 可选接收
jobId、stepKey、stepName/name,并向 Job Hub 上报当前 step 进度。 - 如果传入
webhookUrl,会回调进度和完成状态。
当前默认输出位置:
generated/{jobId}/final.mp4
这里的 {jobId} 是处理器运行 ID。HTTP endpoint 会优先使用请求里的 jobId,也兼容 renderTaskId;如果都没有传,则生成 modal-<uuid>。如果需要稳定输出路径,建议显式传入 outputKey。只有请求同时传入 jobId、stepKey 和 stepName/name 时,才会更新 Job Hub。
示例:
generated/test-001/final.mp4
目录与入口
所有命令建议从 modal-processor 项目根目录执行:
cd ~/projects/modal-processor
source .venv/bin/activate
当前 app 入口文件:
apps/video_to_short/modal_app.py
必需的 Modal Secret
需要在 Modal 后台创建一个 secret,名称必须是:
video-to-short-r2
里面需要包含这些环境变量:
R2_ACCOUNT_ID=...
R2_ACCESS_KEY_ID=...
R2_SECRET_ACCESS_KEY=...
R2_BUCKET=omninote
说明:
R2_ACCESS_KEY_ID 和 R2_SECRET_ACCESS_KEY 是 Cloudflare R2 的 S3-compatible access key。不要写进代码,不要提交到 Git,也不要放到 README 或聊天记录里。
安装依赖
pip install -r requirements.txt
本地触发测试
这个命令是在本地终端触发 Modal 远端任务。真正的视频处理发生在 Modal 远端容器里,不是在本机。
modal run apps/video_to_short/modal_app.py \
--source-key youtube_video/youtube-video-1776311042955.mp4 \
--job-id test-001
显式指定输出位置:
modal run apps/video_to_short/modal_app.py \
--source-key youtube_video/youtube-video-1776311042955.mp4 \
--job-id test-001 \
--output-key generated/test-001/final.mp4
如果源视频不是 R2 key,而是一个临时下载 URL,可以这样运行:
modal run apps/video_to_short/modal_app.py \
--source-download-url "https://example.com/source.mp4" \
--job-id test-001
成功时输出里会包含类似内容:
'status': 'completed'
'outputKey': 'generated/test-001/final.mp4'
生成的视频保存在 R2,不在本地机器上。
部署
执行部署后,Modal 会创建一个稳定的 HTTP endpoint,供 Cloudflare 主站线上调用。
modal deploy apps/video_to_short/modal_app.py
部署成功后,Modal 会输出 process_video 函数对应的 .modal.run URL,形式类似:
https://<workspace>--video-to-short-processor-process-video.modal.run
这个 URL 就是主站里的 PROCESSOR_ENDPOINT。
外部 HTTP 调用
部署后,可以通过 POST JSON 调用这个 endpoint:
当前线上示例 endpoint:https://zhengkinson--video-to-short-processor-process-video.modal.run
curl -X POST "https://<your-modal-endpoint>.modal.run" \
-H "Content-Type: application/json" \
-d '{
"sourceKey": "youtube_video/youtube-video-1776311042955.mp4",
"outputKey": "generated/test-002/final.mp4",
"platform": "tiktok",
"quality": "balanced"
}'
预期会立刻返回:
{"accepted": true, "jobId": "modal-..."}
注意:这个返回只表示 Modal 已接收任务。返回里的 jobId 是当前 Modal 处理任务 ID:如果请求传了 jobId,会返回传入值;如果没传,则由 Modal endpoint 生成。视频处理会在后台继续执行。处理完成后,到 R2 里检查:
generated/test-002/final.mp4
jobId 是可选字段,表示当前 Modal 处理任务 ID;如果不传,会由 Modal endpoint 自动生成。请求同时传入 jobId、stepKey 和 stepName/name 时,处理器会通过 Job Hub step 接口上报进度;缺少任一字段时,不进行 Job Hub 更新。job_id、step_key、step_name 兼容下划线写法。
Webhook 格式
如果请求里传了 webhookUrl,处理器会回调进度和最终结果。旧字段 callbackUrl 会被兼容识别为 webhookUrl。
请求示例:
{
"jobId": "job-123",
"stepKey": "render-short",
"stepName": "生成短视频",
"jobProgressStart": 60,
"jobProgressEnd": 100,
"sourceDownloadUrl": "https://temporary-source-url.example.com/video.mp4",
"outputKey": "generated/job-123/final.mp4",
"platform": "tiktok",
"quality": "balanced",
"webhookUrl": "https://your-cloudflare-app.com/api/processor/webhook"
}
进度 webhook 示例:
{
"jobId": "job-123",
"stepKey": "render-short",
"stepName": "生成短视频",
"outputKey": "generated/job-123/final.mp4",
"platform": "tiktok",
"quality": "balanced",
"status": "processing",
"stage": "rendering",
"progress": 45,
"message": "Rendering MVP vertical short."
}
完成 webhook 示例:
{
"jobId": "job-123",
"stepKey": "render-short",
"stepName": "生成短视频",
"outputKey": "generated/parent-job-123/final.mp4",
"platform": "tiktok",
"quality": "balanced",
"status": "completed",
"stage": "completed",
"progress": 100,
"message": "MVP video rendered successfully.",
"renderInfo": {
"sourceDurationSeconds": 222.4936,
"clipWindows": [
{ "start": 0, "duration": 15 },
{ "start": 103.7468, "duration": 15 }
],
"width": 720,
"height": 1280
}
}
webhook 里的 jobId 是 Modal 处理器运行 ID;如果请求传入了 stepKey 和 stepName/name,webhook 会同时带回这两个字段。处理失败且 Job Hub 字段完整时,处理器会上报 status=failed、失败原因 message 和 failJob=true。
质量参数
fast 720x1280,编码更快
balanced 720x1280,默认
high 1080x1920,编码更慢,质量更高
常见错误
Secret 'video-to-short-r2' not found
Modal 里没有创建 video-to-short-r2,或者 secret 名字写错了。
Missing required environment variable: R2_ACCOUNT_ID
Modal secret 存在,但里面缺少必要字段。
NoSuchKey
传入的 sourceKey 在当前 R2 bucket 里不存在。
AccessDenied
R2 S3 access key 不正确,或者没有当前 bucket 的读写权限。
Web endpoint Functions require FastAPI
镜像缺少 FastAPI。执行 pip install -r requirements.txt 后重新部署。当前 requirements.txt 已包含 fastapi[standard]。
后续计划
- 给公开 Modal endpoint 增加请求 token 校验。
- 用字幕和 LLM 高光分析替代当前的简单前段/中段截取。
- 增加自动字幕。
- 增加更智能的 9:16 裁剪,例如人脸居中。