简介:面向视频批量制作与自动化剪辑场景,一款Python实现的小工具基于剪映项目文件采用JSON存储的机制,通过生成draft_content.json、draft_meta_info.json等草稿结构,快速创建剪映轨道并自动生成视频草稿。资源共13个文件,以9个Python脚本为核心,覆盖草稿对象、素材管理、轨道操作、模板加载及主程序入口等模块;另有2个JSON模板、1份Markdown说明文档和.gitignore,压缩包仅11KB,轻量易部署。目前已有1982人学习下载,适合具备一定Python基础、希望简化剪映草稿创建流程的创作者、视频自媒体或自动化运维人员。脚本实现了“草稿媒体库→内容媒体库→轨道片段”的完整添加链路,add_media_to_track可自动识别音频、视频类型并加入对应轨道,无视频轨道时还会先创建视频轨道;用户只需在main.py中指定本地媒体路径,即可将音乐与视频一键转为可编辑的剪映草稿,省去手动拖拽编排的重复操作,同时保留模板目录便于二次扩展。 先交代一下背景。我当时接到一个很具体的需求:每周要交付几十条不同文案、不同素材的混剪短视频。如果每一条都在剪映里手动拖素材、排轨道、加字幕,一天也就做十几条,而且机械操作特别容易出错。后来我开始研究剪映草稿的文件格式,发现剪映草稿本身就是一套 JSON 数据,轨道、素材、字幕、音频全部是结构化的字段。于是就有了用 Python 直接写草稿轨道的想法,也就是 JianYingProDraft 这类项目的核心思路:不打开剪映,直接生成可以识别、可以继续编辑的剪映草稿文件,然后让剪映自动渲染出成片。
这篇文章会把整个方案拆开讲:为什么这条路可行、环境怎么搭、核心代码怎么写、常见坑怎么避,以及怎么把它扩展成真正的批量生产工具。内容面向有一定 Python 基础、不了解剪映草稿结构的朋友;如果你完全没写过 Python,前半部分看完也能知道值不值得学,后半部分可以直接抄代码。
1. 为什么剪映草稿能被 Python 接管:结构里的秘密
1.1 剪映草稿不止是软件工程,更是一套公开的 JSON 协议
很多人以为剪映草稿是某种加密的私有格式,实际上当你用剪映保存一个草稿后,在草稿目录下会看到两个关键文件:draft_content.json和draft_meta_info.json。前者存的是用户真正编辑的内容——素材列表、轨道、转场、字幕、贴纸、音频、画布参数全在这里;后者存的是草稿的元信息,比如缩略图、最近编辑时间、界面面板状态。
我最早是抱着“试一试”的心态打开这个 JSON 的,结果发现结构相当清晰。顶层有几个大块:canvas_config控制画布分辨率、帧率、背景色;materials存放所有导入的素材,包括视频、图片、音频、文本、贴纸、转场;tracks则是时间线上的轨道数组,每个轨道又包含segments,每个 segment 指向一个material_id,并声明自己在时间轴上的target_timerange。只要把这两个关键值理解透,就等于掌握了剪映草稿的骨架。
draft_content.json本质上是一个树形状态描述文件,剪映打开草稿时做的事情,就是把这个 JSON 反序列化,然后把每个节点渲染成时间线上的轨道块。反过来说,只要我们能生成一个符合剪映解析规则、字段完整、UUID 不冲突的 JSON,剪映就会把它当作正常草稿来打开。这就是整个自动化方案的根基。
1.2 为什么选 Python,而不是直接改 JSON
有人会问:JSON 是纯文本,直接用文本编辑器手写不就行了?瓶颈在于两点:第一,素材多了之后,JSON 动辄几万行,手写几乎不可能维护;第二,我们需要的不是“改一条草稿”,而是“批量生成几十条结构和逻辑各不相同、但模板统一的草稿”,这本质上是程序化的事情。
Python 在这个场景的优势非常明显。它本身就是处理文本和数据的利器,标准库里的json、uuid、os、re基本够用;再加上脚本可以写循环、写配置、接字幕文件、接音频文件名,一套代码跑下来就能批量产出草稿。相比用剪映的“草稿另存为”再手工替换,Python 方案快捷且可复现,改一个文案只需要重新跑一次脚本。
另一个实际原因是:剪映草稿素材的路径、字幕内容、声音文件都存在同一个 JSON 里,Python 处理这些字符串特别顺手。比如从 Excel 读一批商品名,自动生成对应字幕文件和配音文件,再把它们组合进草稿,整个过程不需要打开剪映也能基本完成,只在最后渲染成片时才打开剪映。这也是我认同这类工具的最重要理由:它把“剪辑”从手工劳动变成了“数据处理+渲染”两步。
2. 准备环境:装 Python、建工程、先解剖一个真实草稿
2.1 Python 环境安装与项目依赖
我用的是 Python 3.8+,实际上 3.6 以上都可以跑。Windows 用户直接去 python.org 下载安装包,安装时记得勾选 “Add Python to PATH”,否则后面在命令行敲python会提示找不到命令。macOS 用户建议用 Homebrew 装,命令是brew install python3。
这个项目不需要额外安装像 TensorFlow 那样的重型依赖,标准库就够了。实操时只需确保 Python 能用import json、import uuid、import os这三个库。一个空目录、一个.py文件,就是整个项目的基础工程。我的目录结构是:
jianying_draft_generator/ ├── generate_draft.py # 主脚本,负责拼装 JSON ├── templates/ # 模板 JSON,从剪映导出的草稿提取 ├── assets/ # 图片、视频、音频素材 └── output/ # 生成的草稿目录如果已经安装了剪映,并且有一个手动建好的空白草稿,强烈建议先把这个草稿的draft_content.json复制一份到templates/里。它就是你最好的“字段参考手册”,比任何网上的文档都准。
2.2 解剖样例:从模板草稿里找到必填字段
我当年踩的第一个坑就是“自作聪明”地按网上看到的字段写了一版,结果剪映直接报“草稿打开失败”。后来学乖了:先用剪映随便建一个项目,放一张图、一段文本,然后导出草稿目录,把draft_content.json拿到代码编辑器里逐层展开,对照着写。
这里说的必填字段有几个关键位置:
canvas_config.ratio:画布比例,比如16:9、9:16,同时还有width和height。实测如果不匹配,素材会被拉伸或裁剪。materials下的videos、images、audios、texts:每个素材对象都带一个全局唯一的id,剪映内部靠这个 id 引用素材,不是靠素材路径。tracks数组:轨道顺序代表层级,最底下的主视频轨通常type为3,文本轨一般为12,音频轨一般带独立 type。其实不同剪映版本会对轨道类型做调整,但 track 里的segments结构大体一致。
看模板草稿时,重点不是背字段,而是理解“素材在 materials 里定义,轨道在 tracks 里引用”这一层关系。只要这个引用关系不断,剪映打开草稿基本就不会崩。
简单说一下为什么不能用网上随意找的字段。剪映每个小版本的草稿结构都可能增减字段,某次升级后我发现旧草稿仍能打开,但新版本生成的草稿却打不开旧版剪映,原因就出在版本兼容。所以最稳妥的办法:导出你自己当前版本的草稿模板,再围绕它做程序化填充。
3. 核心代码:从零生成一个可被剪映识别的草稿
3.1 填充基础结构:画布、材料容器、空轨道
现在进入动手阶段。下面这段代码的目标是:生成一个最简的draft_content.json,剪映能打开,并且跑完代码后我们可以继续往里面加素材和轨道。
import json import uuid import os def new_id(): # 剪映中的素材 id 一般是没有横杠的大写 UUID return uuid.uuid4().hex.upper() class DraftBuilder: def __init__(self, ratio="16:9", fps=25, width=1920, height=1080): self.ratio = ratio self.fps = fps self.width = width self.height = height self.data = self._init_base() def _init_base(self): draft = { "canvas_config": { "ratio": self.ratio, "width": self.width, "height": self.height, "fps": self.fps, "background_color": "#000000" }, "materials": { "videos": [], "images": [], "audios": [], "texts": [], "transitions": [], "effects": [], "stickers": [], "amazons": [], "vocal_separate_configs": [], "text_codes": [] }, "tracks": [] } return draft def save(self, output_dir): os.makedirs(output_dir, exist_ok=True) content_path = os.path.join(output_dir, "draft_content.json") meta_path = os.path.join(output_dir, "draft_meta_info.json") with open(content_path, "w", encoding="utf-8") as f: json.dump(self.data, f, ensure_ascii=False, indent=2) # draft_meta_info.json 是元信息,本方案可先写入最简内容 meta = { "draft_fold_path": output_dir, "edit_start_time": 0, "last_modified_time": 0 } with open(meta_path, "w", encoding="utf-8") as f: json.dump(meta, f, ensure_ascii=False, indent=2) return content_path这段代码没有什么高深逻辑,核心是先把“容器”建好。剪映打开草稿时,如果materials缺少某些子列表,可能导致黑屏或识别异常;所以我们一开始就把所有常用容器全部初始化成空列表。canvas_config里的宽高和比例也要提前定好,后面加素材时才好计算时长和坐标。
3.2 添加图片素材和主视频轨道
接下来是重头戏:往材料列表里添加一张图片,并且在主视频轨道上放入对应的 segment。这样剪映打开后就能在时间轴上看到一张静帧图。
class DraftBuilder(DraftBuilder): def add_image_to_track(self, image_path, duration=5, start=0): # 1. 构造 image 素材 image_id = new_id() image_material = { "id": image_id, "path": image_path, "duration": duration * 1_000_000, # 剪映内部时间单位是微秒 "type": "image", "width": self.width, "height": self.height } self.data["materials"]["images"].append(image_material) # 2. 找到或创建主视频轨道(type=3) main_track = None for track in self.data["tracks"]: if track.get("type") == 3: main_track = track break if main_track is None: main_track = { "id": new_id(), "type": 3, "name": "主视频轨道", "segments": [], "is_default": True } self.data["tracks"].append(main_track) # 3. 在轨道上追加 segment main_track["segments"].append({ "id": new_id(), "material_id": image_id, "target_timerange": { "start": start * 1_000_000, "duration": duration * 1_000_000 } })这里需要特别说明两个时间单位的细节。剪映草稿 JSON 里的时间单位是微秒,不是秒。如果直接把 5 填进duration,剪映会认为这个素材只持续 5 微秒,时间轴上根本看不见。所以代码里统一用duration * 1_000_000做转换。这是很多初版脚本跑完打开草稿后“一片空白”的原因。
另一个细节是“素材路径”。Windows 下剪映素材路径通常形如C:\Users\xxx\assets\1.jpg,在 JSON 字符串里反斜杠会被转义。我建议代码里统一用正斜杠,即image_path.replace("\\", "/"),这样保存出来的 JSON 可读性好,剪映也能正确识别。实测剪映对正斜杠路径支持没问题,省去很多转义烦恼。
3.3 添加字幕文本:内容与 time_range 要同步
做混剪类视频,字幕几乎是刚需。文本轨道和图片轨道的逻辑类似,也是先建materials.texts素材,再在轨道上放 segment。区别是文本素材需要带上具体的文字内容和字体属性。
def add_text(self, content, start=0, duration=5, font_size=12): text_id = new_id() text_material = { "id": text_id, "content": content, "font_name": "默认字体", "font_size": font_size, "alignment": 1, "color": "#FFFFFF", "duration": duration * 1_000_000 } self.data["materials"]["texts"].append(text_material) text_track = None for track in self.data["tracks"]: if track.get("type") == 12: text_track = track break if text_track is None: text_track = { "id": new_id(), "type": 12, "name": "文本轨道", "segments": [] } self.data["tracks"].append(text_track) text_track["segments"].append({ "id": new_id(), "material_id": text_id, "target_timerange": { "start": start * 1_000_000, "duration": duration * 1_000_000 } })一些版本还会在文本 segment 下额外写入text_codes,这是一个与歌词逐字时间轴相关的字段。如果只往content里写文字而不维护text_codes,某些版本剪映可能会把字幕时间轴识别得不准确。稳妥做法是先在自己的模板草稿里添加一条字幕,看导出的 JSON 里texts素材和轨道 segment 分别存了什么字段,然后照着填充。不同版本差异较大,唯有用你本机的模板去对齐才最可靠。
字体颜色、字号这些值不一定每个版本都一模一样,但content、start、duration这三个最核心的字段是通用的,优先保证它们正确。
3.4 保存草稿到剪映草稿目录
脚本最后生成的是一个完整草稿目录,目录名可以自己起。想让剪映客户端直接识别,需要把整个目录放到剪映的草稿目录下(通常是C:\Users\你的用户名\AppData\Local\JianyingPro\User Data\Projects\com.lveditor.draft)。脚本里可以增加一步自动复制:
def install_draft(self, draft_name): # 先生成到当前目录 out = self.save(draft_name) target_root = os.path.expandvars( r"%LOCALAPPDATA%\JianyingPro\User Data\Projects\com.lveditor.draft" ) target_dir = os.path.join(target_root, draft_name) os.makedirs(target_dir, exist_ok=True) content_path = os.path.join(target_dir, "draft_content.json") meta_path = os.path.join(target_dir, "draft_meta_info.json") shutil.copy2(out, content_path) # 把 meta 一起拷过去 meta_src = os.path.join(draft_name, "draft_meta_info.json") shutil.copy2(meta_src, meta_path) return target_dir%LOCALAPPDATA%是 Windows 环境变量,展开后就是剪映草稿存储的根目录。如果剪映版本不同,项目内的子目录名称可能有差异,最笨也最有效的办法是:手动存一个草稿,然后在资源管理器里搜draft_content.json,看它到底落在哪个绝对路径。后面脚本就指向那个路径。
4. 实测踩坑:剪映识别不了、黑屏、字幕不显示怎么办
4.1 草稿文件打不开或提示损坏
这是最常见的坑,我刚开始写这工具时几乎每改一次结构就会遇到一次。原因大致有三类:
draft_content.json里缺少某个剪映版本必需的字段。一个新版剪映草稿里可能有materials.amazons、vocal_separate_configs等字段,如果你从网上抄的旧模板没有,新版剪映就会认为草稿损坏。- UUID 重复或为空。个别偷懒代码用固定字符串“test”作为素材 id,一旦同一条草稿里出现重复引用,剪映会直接崩溃。
- JSON 格式本身不合法。比如多个对象之间漏了逗号或括号不匹配,剪映解析失败后会给出“草稿打开失败”之类提示。
排查思路很简单:先在自己本机用剪映导出一个只含一张图片和一行字幕的草稿,把它作为黄金模板,然后代码生成结果和黄金模板做 diff,字段差异在哪,问题基本就在哪。我写了一个独立函数,专门把两个 JSON 的顶层 key 集合对比打印出来,几分钟就能定位。
4.2 素材黑屏或显示“离线素材”
黑屏一般不是轨道问题,而是素材路径对不上。剪映打开草稿时发现materials里的某个素材路径不存在,就会在时间轴显示“离线”。这种情况在批量移动素材目录后最容易出现。
解决办法是确保生成草稿时,素材路径和最终剪映打开草稿时素材所在位置一致。我一般在本地开发目录里用相对路径拼接,最后批量执行前先做一次路径检查,打开每个image_material["path"]确认文件真实存在。如果素材是网络下载的,先下载到本地再写草稿,不要直接把 URL 写在路径里。
4.3 字幕不显示或时间轴对不上
字幕不显示的原因通常是文本素材里的字段不完整。有的剪映版本需要font_size大于 0,并且alignment设成有效值;有的则要求内容同时写在content和text_codes相关字段里。只改content不写text_codes,在最严格的情况下会出现“字幕块在轨道上,但播放预览时看不见文字”。
如果发现时间轴对不上,要检查target_timerange里的start和duration是否按微秒计算。举个例子:我希望字幕从第 2 秒开始、持续 3 秒,那么代码里必须填start=2_000_000、duration=3_000_000,而不是 2 和 3。漏掉_000_000是最隐蔽的低级错误,排查时优先怀疑这里。
我把常见问题和排查思路整理成一张表,方便对照:
| 现象 | 优先排查点 | 参考解法 |
|---|---|---|
| 剪映提示草稿损坏 | JSON 缺失字段、UUID 重复、格式错误 | 与本地模板草稿 diff,补齐字段 |
| 素材黑屏或离线 | 素材路径不存在、存在中文路径转义问题 | 统一正斜杠路径,生成前检查文件存在 |
| 字幕不显示 | 文本素材缺字体/颜色字段,或只写了 content | 对齐模板里的 texts 字段;检查 text_codes |
| 时间轴错位 | 数字没乘 1_000_000 | 确认 target_timerange 使用微秒 |
| 比例变形 | canvas_config 宽高比与素材不一致 | 先固定画布比例,再按比例准备素材 |
5. 扩展玩法:把脚本变成批量视频生产线
5.1 用模板渲染代替手写 JSON
当脚本能稳定生成一条草稿后,下一个目标就是批量。批量生成不是把“加一张图、加一行字”循环十几次这么简单,而是要先把“每条视频的差异点”抽出来,比如图片路径、标题文字、背景音乐、字幕内容、时长。把这些差异点定义成一个列表,然后遍历列表,每遍历一次生成一个独立草稿。
tasks = [ { "image": "assets/product_01.jpg", "title": "2025春季新品推荐", "music": "assets/bgm_01.mp3", "duration": 8 }, { "image": "assets/product_02.jpg", "title": "经典返场:夏日限时优惠", "music": "assets/bgm_02.mp3", "duration": 10 } ] for index, task in enumerate(tasks, 1): builder = DraftBuilder(ratio="9:16", width=1080, height=1920) builder.add_image_to_track(task["image"], duration=task["duration"]) builder.add_text(task["title"], start=0, duration=task["duration"]) # 如果有配音或背景音乐,再加 add_audio 方法 builder.save(f"output/draft_{index}")这个循环里最核心的变化在于:每轮循环都新建一个DraftBuilder,防止上一轮数据污染下一轮。如果复用同一个对象,素材、轨道会不断叠加,最终草稿会包含前面所有素材的残余,导出的成片完全不是你想要的。分而治之,一次任务一个 builder,是最稳妥的做法。
5.2 与 TTS、Excel、素材命名规范打通
批量生产的真正瓶颈其实不是 JSON 生成,而是素材准备。如果 50 条视频对应 50 张图和 50 段音频,手工准备素材仍然很累。我实践的路径是:
- 用 Excel 或 CSV 存放每条视频的标题、正文、音频文件名、图片文件名。
- 脚本逐行读取 Excel,用 Python 的
openpyxl或标准库csv读数据。 - 用 TTS 工具把文案批量转成音频,输出文件名与 Excel 中记录的音频字段保持一致。
- 图片按统一规范命名,比如
01.jpg、02.jpg,脚本直接按序号拼接路径。
这样真正需要人工介入的只有两步:整理文案、检查最终成片。中间过程脚本自动完成。我见过有人把电商上品、详情页描述全部自动化接进来,每天定时生成一批商品视频,流程跑通了之后效率提升非常明显。
建议还是先小规模验证:先用脚本生成 3 条草稿,手工打开剪映渲染 3 条成片,确认没问题后再扩大到完整批次。否则一次性生成 50 条,如果有一半因为路径问题打不开,返工成本也不低。
5.3 模板版本管理的小习惯
最后分享一个我自己的习惯。因为剪映会隔段时间提示升级,而升级后草稿 JSON 结构大概率会有细微变化,我会在每次升级后做一次“模板快照”备份:手动新建一个简单空白草稿,把draft_content.json存成templates/template_vX.Y.Z.json。脚本里默认引用最新模板,但如果换到旧机器或换到旧版剪映环境,可以手动切换模板版本。
这样做的价值在于,脚本运行环境变化后,你能快速确认是剪映版本变化导致的问题,还是代码逻辑的问题。这比对着报错信息瞎猜高效得多。
整个方案跑起来之后,我的工作流就变成了:准备文案 → 跑脚本生成草稿 → 打开剪映批量导出 → 抽检成片。剪映在这里回归到了它最擅长的“渲染器”角色,而内容结构、轨道排布这些体力活全部交给了 Python。对这种用程序改造重复劳动的做法,我也越来越深信:先把数据结构搞清楚,再想自动化,绝大多数工具类需求都能找到类似突破口。
本文还有配套的精品资源,点击获取