一、项目概述
1.1 项目背景与意义
针对剪映草稿文件自动化生成、编辑及批量导出的需求,用于构建全自动视频剪辑 / 混剪流水线,降低剪映草稿手动操作成本,提升批量剪辑效率。
1.2 项目目标
实现剪映草稿的程序化生成、素材替换、特效添加及批量导出,支持主流剪映版本,提供简洁易用的 API 供二次开发(如集成到 Flask 服务、Coze 智能体等场景)。
1.3 适用场景与用户
适用场景:批量视频混剪、模板化视频生成、智能剪辑流水线搭建
目标用户:Python 开发者、视频批量制作从业者、智能剪辑项目开发人员
二、总体流程与工作流设计
工作流程:
1、创建工作流,生成所需要素材的下载链接
2、引用开源项目pyJianYingDraft (主要功能创建一个json文件,保存的是剪映流程规则,剪映打开即对视频操作的效果)
3、写一个 Python 服务暴露接口
4、使用 Coze 的代码节点,调用这个接口,创建剪映素材
5、使用剪映,打开这个素材文件
工作流设计:

三、工作流模块详细设计
1. 开始节点

api_url单独来说
这里输入的是自己内网穿透做的外部服务+(代码)路径
coze在公网,而服务在本地启动即在内网中,外部是访问不到的。
一般来说有两种方式来连接,一个是云部署服务器 、二是做内网穿透 ,后者成本低,适合个人使用,如果分享给多人使用或者在外地使用需要前者。
内网穿透可以问一下豆包,很简单,用cpolar暴露一下就ok。
2. 构造生成图提示词
创建一个大模型节点,功能为构造生成图提示词

系统提示词
powershell
请你根据用户输入的诗歌题目,生成一段提示词,用于生成这这个诗歌所呈现的意境的图像。
另外再生成一个符合意境的贴纸的提示词,我会和图像合成在一起。
最后还要返回这首诗最精华的两句诗词的内容(注意,只需要两句)。
输出内容请遵循这个格式,不需要任何其他内容:
{
"image_prompt":"你生成的提示词",
"sticker_prompt":"贴纸图像提示词",
"sentences":"精华诗句"
}
用户提示词 {{topic}}
输出 为一个object,包含模型生成的三个内容

3. 提示词优化
创建两个提示词优化节点,用于优化上一节点生成的图像与贴纸提示词。并行于大模型节点后面

4. 生成视频首帧图像
创建一个图像生成节点,功能为生成视频的首帧图像。连接于优化生成图像提示词后面。

5. 生成主题视频
创建一个视频生成节点,功能为基于首帧图像生成一个视频

6. 文本转语言
创建一个文本配音节点(在检索框检索),功能为将第一个构造生成图提示词节点生成的句子转为语音配在视频中。

7. 生成视频贴纸
创建一个生成图像节点,功能为生成贴纸,连接于优化贴纸提示词节点后。可以起水印效果。参考生成视频首帧图像节点,更改输入引用即可
- 创建剪映草稿
创建一个代码节点,通过代码节点调用python封装的接口,实现剪映草稿json文件的生成。
输入

之前生成的语音、图像、视频都是url。
代码对传入的参数进行解析,生成接口的请求头并进行post访问,返回访问状态等信息。
python
import asyncio
import time
import json
async def main(args):
start_time = time.time()
try:
# 导入异步请求库
import requests_async as requests
# 获取输入参数
params = args.params
# 必要参数验证
required_params = [
'url', # API接口地址
'draft_path', # 草稿路径
'draft_name', # 草稿名称
'audio_url', # 音频URL
'image_url', # 图片URL
'sticker_url', # 贴纸URL
'video_url', # 视频URL
'content' # 内容文本
]
# 检查必要参数
missing_params = []
for param in required_params:
if param not in params or not params[param]:
missing_params.append(param)
if missing_params:
return {
"result": "",
"status": "error",
"statistics": json.dumps({
"processing_time": time.time() - start_time,
"missing_params_count": len(missing_params),
"success": False
}),
"message": f"缺少必要参数: {', '.join(missing_params)}",
"data": []
}
# 获取参数值
api_url = params['url']
draft_path = params['draft_path']
draft_name = params['draft_name']
audio_url = params['audio_url']
image_url = params['image_url']
sticker_url = params['sticker_url']
video_url = params['video_url']
content = params['content']
# 获取可选参数(分辨率,默认1920x1080)
resolution = params.get('resolution', [1920, 1080])
if not isinstance(resolution, list) or len(resolution) != 2:
resolution = [1920, 1080]
# 获取视频相关参数
video_duration = params.get('video_duration', '5s')
video_animation = params.get('video_animation', '淡入')
# 获取音频相关参数
audio_duration = params.get('audio_duration', '3s')
audio_volume = params.get('audio_volume', 1.0)
audio_fade_duration = params.get('audio_fade_duration', '0.5s')
# 确保音频音量为浮点数
try:
audio_volume = float(audio_volume)
except (ValueError, TypeError):
audio_volume = 1.0
# 构建请求数据
request_data = {
"draft_path": draft_path,
"draft_name": draft_name,
"resolution": resolution,
"resources": {
"audio_url": audio_url,
"video_url": video_url,
"sticker_url": sticker_url,
"image_url": image_url
},
"content": content,
"video": {
"duration": video_duration,
"animation": video_animation
},
"audio": {
"duration": audio_duration,
"volume": audio_volume,
"fade_duration": audio_fade_duration
}
}
# 设置请求头
headers = {
"Content-Type": "application/json",
"Accept": "application/json",
"User-Agent": "CozeVideoBot/1.0"
}
# 记录请求开始时间
request_start_time = time.time()
# 发送异步POST请求,设置超时时间为600秒(10分钟)
response = await requests.post(
api_url,
json=request_data,
headers=headers,
timeout=600
)
# 计算请求耗时
request_duration = time.time() - request_start_time
total_processing_time = time.time() - start_time
# 检查响应状态码
if response.status_code == 200:
try:
# 解析API返回的JSON数据
api_result = response.json()
# 构建成功统计信息
stats = {
"processing_time": total_processing_time,
"request_duration": request_duration,
"api_status_code": response.status_code,
"request_size": len(json.dumps(request_data)),
"response_size": len(response.text),
"draft_name": draft_name,
"content_length": len(content),
"success": True
}
# 返回成功结果
return {
"result": json.dumps(api_result, ensure_ascii=False),
"status": "success",
"statistics": json.dumps(stats),
"message": f"视频生成请求成功提交,草稿名称:{draft_name},内容:{content}",
"data": [
{
"api_url": api_url,
"draft_name": draft_name,
"content": content,
"resolution": f"{resolution[0]}x{resolution[1]}",
"video_duration": video_duration,
"audio_duration": audio_duration,
"response": api_result
}
]
}
except Exception as json_error:
# JSON解析失败
stats = {
"processing_time": total_processing_time,
"request_duration": request_duration,
"api_status_code": response.status_code,
"json_parse_error": str(json_error),
"response_preview": response.text[:200],
"success": False
}
return {
"result": "",
"status": "error",
"statistics": json.dumps(stats),
"message": f"API返回数据格式错误,无法解析JSON: {str(json_error)}",
"data": []
}
else:
# API请求失败的情况
try:
error_detail = response.text
# 尝试解析错误详情
try:
error_json = response.json()
if isinstance(error_json, dict):
error_detail = error_json.get('message', error_detail)
except:
pass
except:
error_detail = "无法获取错误详情"
stats = {
"processing_time": total_processing_time,
"request_duration": request_duration,
"api_status_code": response.status_code,
"error_detail": error_detail[:200], # 限制错误详情长度
"success": False
}
return {
"result": "",
"status": "error",
"statistics": json.dumps(stats),
"message": f"API请求失败,状态码: {response.status_code},错误详情: {error_detail}",
"data": []
}
except asyncio.TimeoutError:
stats = {
"processing_time": time.time() - start_time,
"error_type": "TimeoutError",
"timeout_duration": 600,
"success": False
}
return {
"result": "",
"status": "error",
"statistics": json.dumps(stats),
"message": "请求超时(超过10分钟),请稍后重试或检查视频生成任务是否过于复杂",
"data": []
}
except Exception as e:
error_type = type(e).__name__
error_message = str(e)
# 处理常见的网络错误
if "ConnectionError" in error_type or "ConnectTimeout" in error_type:
user_message = f"无法连接到服务器 {params.get('url', '未知地址')},请检查网络状态后重试"
elif "RequestException" in error_type:
user_message = f"请求异常: {error_message}"
elif "ImportError" in error_type:
user_message = "缺少必要的依赖库,请检查环境配置"
else:
user_message = f"处理过程中发生错误: {error_message}"
stats = {
"processing_time": time.time() - start_time,
"error_type": error_type,
"error_message": error_message,
"success": False
}
return {
"result": "",
"status": "error",
"statistics": json.dumps(stats),
"message": user_message,
"data": []
}
输出

本地服务与运行
本地服务代码
可参考的代码地址(等我等我上传github[手动苦笑])
或者可以尝试用大模型生成,以下是可参考的提示词
powershell
请编写一个基于Flask的剪映草稿自动生成API服务,要求如下:
【核心功能】
1. 基础配置:
- 设置500MB文件大小限制,支持UTF-8编码,JSON响应不转义中文
- 自定义JSON编码器确保UTF-8输出,添加响应中间件统一编码
2. 核心类:
- SmartMaterialAnalyzer:用pymediainfo分析音视频时长、分辨率等信息
- DraftService:实现素材下载、参数适配、草稿创建逻辑
3. 接口设计:
- /health:健康检查
- /create_draft:创建剪映草稿(必填draft_path,支持音频/视频/文本参数配置)
- /debug_info:调试信息(仅debug模式可用)
- /api_docs:API文档
- /create_simple_draft:简化版创建接口
4. 关键逻辑:
- 素材下载:支持URL重定向,验证文件完整性,保存到assets目录
- 参数适配:智能检测素材时长,自动调整用户指定的时长参数
- 草稿生成:创建音频/视频/文本轨道,添加动画、转场、淡入淡出效果
5. 异常处理:
- 捕获所有异常,返回详细错误信息(debug模式返回堆栈)
- 素材下载失败不中断整体流程,给出警告日志
【技术要求】
- 使用pyJianYingDraft库操作剪映草稿
- 代码结构清晰,添加详细注释
- 兼容Windows/Linux路径处理
- 生成的代码可直接运行,包含必要的导入和初始化逻辑
运行流程
- 先在本地启动服务,将暴露的本地接口(暴露地址+简化创建路由,如https:/******.cn/create_simple_draft)填入api_url,启动工作流。

- 成功运行后会生成一个文件夹包含一个资源文件与两个剪映配置json文件保存在draft_path。

- 之后打开剪映进入编辑项目,把"江雪"文件夹直接拖拽到剪映里面,即可进行后续的编辑。

不过我这个字幕没添加成功,哈哈哈,后面把图像等也放在轨道上了。
对比coze中其他的剪映插件
coze中提供了很多插件可直接使用,但是这种低代码方式比使用coze中提供的插件工作流结构会更加简洁,并且降低人力去设置参数。
