Kdenlive CLI-Anything 接线器实战:用 JSON 项目状态驱动 MLT XML 的命令行非线性剪辑工作流
Kdenlive 是知名的开源非线性视频编辑器,而 CLI-Anything 生态的做法是给它套一层"Agent 原生"的命令行外壳:本项目中的 Kdenlive CLI harness 把"建项目、导素材、排时间线、加特效、做转场、标记向导、导出工程"全流程收敛为一组有状态、可撤销的 CLI 命令,并把工程状态持久化为 JSON、在导出阶段生成 Kdenlive/MLT 可识别的 XML。读完本文你将掌握:如何零 GUI 依赖地建立剪辑工程、如何用命令流完成从素材入库到渲染导出的完整 SOP,以及其底层 JSON 模型、撤销机制与 MLT XML 生成原理,从而把视频剪辑接入自动化脚本或 Agent 流水线。
本文以 KDENLIVE.md(Standard Operating Procedure)为骨架,并结合该目录下 cli_anything/kdenlive 包的真实源码、README.md 与 setup.py 做纵深展开。
一、项目定位与整体架构
Kdenlive CLI harness 提供一个有状态(stateful) 的命令行接口,用于非线性视频剪辑。与一次性工具不同,它在你与终端交互过程中始终维护一个"当前工程"的会话状态,每一次变更都以 JSON 形式落在工程对象上,最终能够输出合法的 Kdenlive/MLT XML 供渲染或进一步加工。
从其源码结构与 README.md 的架构图可以归纳出以下设计要点:
- JSON 工程格式:所有剪辑状态(素材库 bin、轨道、剪辑片段、特效、转场、向导、元数据)都以 JSON 存储,易于查看、diff 与程序化生成;
- MLT XML 导出:由 JSON 生成带 Kdenlive 元数据的合法 MLT XML(
.kdenlive文件),可被 Kdenlive 或 melt 打开渲染; - 零二进制依赖:剪辑工程编辑与 XML 生成不需要安装 Kdenlive 或 melt,仅依赖 Python 标准库与 click;
- Undo/Redo:所有修改操作通过会话历史与深拷贝快照实现完整撤销/重做;
- 时间码支持:底层提供秒(float)与
HH:MM:SS.mmm时间码的互转,并进一步支持秒与帧的换算。
二、环境准备与安装
按照 KDENLIVE.md 的说明,进入 harness 目录并安装依赖即可:
cd /root/cli-anything/kdenlive/agent-harness
pip install click
需要指出的是,这只满足"编辑工程 + 生成 XML"的最小需求。若要以包的方式安装并获得命令行入口,可使用该目录下的 setup.py:
pip install -e .
setup.py 中明确了运行前提:Python >= 3.10,运行时依赖 click>=8.0.0 与 prompt-toolkit>=3.0.0,并注册了控制台入口:
cli-anything-kdenlive=cli_anything.kdenlive.kdenlive_cli:main
安装后有两种等价的调用方式:
| 调用方式 | 场景 |
|---|---|
cli-anything-kdenlive <子命令>... |
已安装包后的标准入口 |
python3 -m cli_anything.kdenlive <子命令>... |
在源码树中直接运行(__main__.py 透传 main()) |
提示:Kdenlive/melt 只有在"打开并渲染 XML"环节才需要;仅做工程编辑、导入导出时完全不需要,这也正是该 harness 适合服务器端与 Agent 调用的原因。
三、核心工作流:从零到导出的七步 SOP
下面把 KDENLIVE.md 的核心工作流逐步展开,并补充源码可验证的参数细节。
第 1 步:创建工程(project new)
cli-anything-kdenlive project new --name "MyProject" --profile hd1080p30 -o project.json
--profile 缺省时不会报错——project.py 的 create_project() 允许以 --width/--height/--fps-num/--fps-den 直接定义自定义分辨率与帧率。若传入 --profile,则整组宽高、帧率、逐行/隔行与显示宽高比都会被预设覆盖;传入未知 profile 名会抛出 ValueError 并列出可用项。
KDENLIVE.md 列出的可用 profile 在 project.py 的 PROFILES 字典中逐一落地,展开后如下表:
| Profile | 分辨率 | 帧率(fps) | 扫描方式 | 宽高比 |
|---|---|---|---|---|
| hd1080p30 | 1920×1080 | 30 | 逐行 | 16:9 |
| hd1080p25 | 1920×1080 | 25 | 逐行 | 16:9 |
| hd1080p24 | 1920×1080 | 24 | 逐行 | 16:9 |
| hd1080p60 | 1920×1080 | 60 | 逐行 | 16:9 |
| hd720p30 | 1280×720 | 30 | 逐行 | 16:9 |
| hd720p25 | 1280×720 | 25 | 逐行 | 16:9 |
| hd720p60 | 1280×720 | 60 | 逐行 | 16:9 |
| 4k30 | 3840×2160 | 30 | 逐行 | 16:9 |
| 4k60 | 3840×2160 | 60 | 逐行 | 16:9 |
| sd_ntsc | 720×480 | 30000/1001(≈29.97) | 隔行 | 4:3 |
| sd_pal | 720×576 | 25 | 隔行 | 4:3 |
创建成功后,工程对象包含 version、name、profile、bin、tracks、transitions、guides 与 metadata 八个顶层字段(详见下文"JSON 工程格式"一节)。
第 2 步:导入素材到素材库(bin import)
cli-anything-kdenlive --project project.json bin import /path/to/video.mp4 --name "Main" -d 120.0
cli-anything-kdenlive --project project.json bin import /path/to/audio.wav --name "Music" -d 180.0 --type audio
bin import 支持 clip 类型(--type)为:video、audio、image、color、title(kdenlive_cli.py),其中:
--name/-n:素材在 bin 中的显示名,缺省会基于source推导;--duration/-d:素材时长(秒),供 XML 生成时计算 producer/chain 的length与out;- 未指定
source的抽象类型(如 color/title)也可通过该命令登记,XML 侧会按类型选择mlt_service(如 color 类型映射为colorproducer,见 mlt_xml.py)。
对应 bin 组的完整子命令为:import、remove、list、get,分别负责入库、移除、列表与详情查询。
第 3 步:搭建时间线(timeline)
先添加轨道,再把 bin 中的素材按位置放到轨道上:
# 添加轨道
cli-anything-kdenlive --project project.json timeline add-track --type video
cli-anything-kdenlive --project project.json timeline add-track --type audio
# 放置片段:轨道 0 上放 clip0,起点 0s,出点 30s
cli-anything-kdenlive --project project.json timeline add-clip 0 clip0 --position 0 --out 30.0
# 裁剪 / 切割 / 移动
cli-anything-kdenlive --project project.json timeline trim 0 0 --in 5.0 --out 25.0
cli-anything-kdenlive --project project.json timeline split 0 0 10.0
cli-anything-kdenlive --project project.json timeline move 0 0 5.0
从 kdenlive_cli.py 可以看到各子命令的完整签名:
add-track:--type只能取video|audio,另有--name、--mute、--hide、--locked用于设置轨道属性,这些属性在 XML 导出的 tractorhide属性中被体现;add-clip <track_id> <clip_id>:--position(时间轴位置,秒,默认 0)、--in/--out(剪辑片段自身的入出点,秒);trim <track_id> <clip_index>:修改某一片段的--in/--out;split <track_id> <clip_index> <split_at>:在给定时间点把片段一分为二;move <track_id> <clip_index> <new_position>:平移片段;remove-track、remove-clip、list对应删除与查看。
注意所有 position/in/out/split_at 均以秒为单位的浮点数录入工程对象。
第 4 步:添加滤镜与特效(filter)
# 亮度提升 30%
cli-anything-kdenlive --project project.json filter add 0 0 brightness -p level=1.3
# 双向模糊
cli-anything-kdenlive --project project.json filter add 0 0 blur -p hblur=5 -p vblur=5
KDENLIVE.md 列出的可用滤镜为:brightness、contrast、saturation、blur、fade_in_video、fade_out_video、fade_in_audio、fade_out_audio、volume、crop、rotate、speed、chroma_key。这 13 个滤镜并非空壳,在 filters.py 的 FILTER_REGISTRY 中每个都定义了 MLT 底层服务、分类与参数规格(类型/默认值/范围),add_filter 会先做参数白名单与取值范围校验(越界即抛 ValueError),然后把带默认值的完整参数写入片段。各滤镜参数展开如下:
| 滤镜 | MLT 服务(mlt_service) | 参数(类型,默认值,范围) |
|---|---|---|
| brightness | brightness | level(float,1.0,0~5) |
| contrast | brightness | level(float,1.0,0~5) |
| saturation | avfilter.eq | saturation(float,1.0,0~3) |
| blur | boxblur | hblur/vblur(int,2,0~100) |
| fade_in_video | brightness(kdenlive_id=fade_from_black) | duration(float,1.0,0.01~60) |
| fade_out_video | brightness(kdenlive_id=fade_to_black) | duration(float,1.0,0.01~60) |
| fade_in_audio | volume(kdenlive_id=fadein) | duration(float,1.0,0.01~60) |
| fade_out_audio | volume(kdenlive_id=fadeout) | duration(float,1.0,0.01~60) |
| volume | volume | gain(float,1.0,0~10) |
| crop | crop | left/right/top/bottom(int,0,0~9999) |
| rotate | affine | angle(float,0.0,-360~360) |
| speed | timewarp(kdenlive_id=speed) | speed(float,1.0,0.01~100) |
| chroma_key | frei0r.select0r | color(str,#00ff00)、variance(float,0.15,0~1) |
命令层同时提供 filter remove <track> <clip> <index>、filter set <track> <clip> <index> <param> <value>(对已有滤镜改参数并复校验范围)、filter list 与 filter available [--category]。
第 5 步:添加转场(transition)
# 在轨道 0 与轨道 1 之间加 dissolve 转场,位置 5s、持续 2s
cli-anything-kdenlive --project project.json transition add dissolve 0 1 -p 5.0 -d 2.0
可用转场类型为:dissolve、wipe、slide、composite、affine。由 transitions.py 的 TRANSITION_TYPES 可见其底层实现与服务绑定:
| 类型 | MLT 服务 | 说明与关键参数 |
|---|---|---|
| dissolve | luma | 叠化;duration(默认 1s)、softness(0~1) |
| wipe | luma | 擦拭;resource(擦除图资源)、softness |
| slide | affine | 滑动;direction(默认 left) |
| composite | composite | 合成叠加;fill/aligned(0/1) |
| affine | affine | 仿射变换;distort(0/1) |
transition add 内部会校验:轨道必须存在、track_a != track_b、position 非负、duration 为正,未知参数会直接拒绝(transitions.py)。配套命令还有 transition remove <id>、transition set <id> <param> <value>(其中 position/duration 是顶层字段,可单独设置)、transition list。
第 6 步:添加向导/标记(guide)
cli-anything-kdenlive --project project.json guide add 30.0 --label "Scene 2"
在 30 秒处放置一个带标签的向导点。guide add 的完整参数还包括 --type(default|chapter|segment,默认 default)与 --comment(附加注释)。删除与查看对应 guide remove <guide_id>、guide list。向导在导出 XML 时会被序列化到 sequence 的 kdenlive:sequenceproperties.guides 属性中(mlt_xml.py),回到 Kdenlive 时间轴上依然可见。
第 7 步:导出 XML 并渲染
# 生成供 Kdenlive/melt 使用的 MLT XML
cli-anything-kdenlive --project project.json export xml -o output.kdenlive
# 本机装有 Kdenlive 时直接打开
kdenlive output.kdenlive
export xml 把 JSON 工程转换为 XML 字符串,指定 -o 则落盘并回显路径与字节大小;不指定时直接打印到标准输出(便于管道处理)。渲染预设信息可用 export presets 查看,export.py 内置了 8 种常见目标:
| 预设名 | 视频/音频编码 | 容器 |
|---|---|---|
| h264_hq | libx264 / aac(8M/192k) | mp4 |
| h264_fast | libx264 / aac(4M/128k) | mp4 |
| h265_hq | libx265 / aac | mp4 |
| webm_vp9 | libvpx-vp9 / libvorbis | webm |
| prores | prores_ks / pcm_s16le | mov |
| lossless | ffv1 / flac | mkv |
| gif | gif(无音频) | gif |
| audio_only | 无视频 / pcm_s16le | wav |
说明:当前仓库的 CLI 完成"工程 JSON → XML 工程文件"的职责;真正渲染仍需借助 Kdenlive/melt 打开产物执行(这也是 setup.py 描述中注明 "Requires: melt" 的原因),渲染预设则服务于后续自动化调用的编码选择。
四、会话管理:Undo/Redo 与自动保存
所有修改类操作(import、add-track、add-clip、trim、split、move、filter、transition、guide)在执行前都会先 sess.snapshot(...),因此整条工作流天然可回退:
cli-anything-kdenlive --project project.json session undo
cli-anything-kdenlive --project project.json session redo
cli-anything-kdenlive --project project.json session history
cli-anything-kdenlive --project project.json session status
session.py 的实现要点(可作为工程事实引用):
- 深拷贝快照:
snapshot()用copy.deepcopy保存当前工程的完整状态,附带操作描述与 ISO 时间戳压入_undo_stack; - 容量上限:
MAX_UNDO = 50,超出时从队首弹出最旧快照; - redo 语义:执行新的修改会清空
_redo_stack;undo()会把当前态作为 redo 点,redo()与之对称; - 锁定的原子写盘:
_locked_save_json()在 POSIX 下通过fcntl.flock加独占锁后seek(0)+truncate()+dump,避免多进程并发写坏工程文件(session.py); - 一次性命令自动保存:
cli组通过 result_callback(auto_save_on_exit,见 kdenlive_cli.py)在每次一次性命令结束后,若会话被修改且配置了--project路径,就自动落盘——无需手动project save。这一行为也可用全局开关--dry-run关闭,便于预览不落盘。
session status 会输出是否载入工程、工程路径、dirty 标记以及 undo/redo 栈深度,方便 Agent 在每一步确认状态。
五、机器可读输出:全局 --json
为便于脚本与 Agent 消费,所有命令都支持全局 --json 标志(在 cli 组上定义,kdenlive_cli.py):
cli-anything-kdenlive --json --project project.json bin list
cli-anything-kdenlive --json --project project.json timeline list
开启后,普通输出函数 output() 改为打印 json.dumps(data, indent=2, default=str);错误路径同样结构化——FileNotFoundError、ValueError/IndexError/RuntimeError、FileExistsError 分别被包装为带 type 字段的 JSON error 对象(如 {"error": "...", "type": "file_not_found"},见 kdenlive_cli.py),在 REPL 中错误则不会导致进程退出。这让上层 Agent 可以用统一 schema 解析成功与失败结果,而无需抓取人类可读文案。
六、JSON 工程格式详解
工程文件本质是一个自描述的 JSON 文档。README.md 给出了结构样例,结合 project.py 可以确认各字段的取值语义:
{
"version": "1.0",
"name": "my_video",
"profile": {
"name": "hd1080p30", "width": 1920, "height": 1080,
"fps_num": 30, "fps_den": 1, "progressive": true,
"dar_num": 16, "dar_den": 9
},
"bin": [
{"id": "clip0", "name": "Interview", "source": "/path/to/video.mp4",
"duration": 120.5, "type": "video"}
],
"tracks": [
{"id": 0, "name": "V1", "type": "video", "mute": false, "hide": false,
"locked": false, "clips": [
{"clip_id": "clip0", "in": 0.0, "out": 30.0, "position": 0.0, "filters": []}
]}
],
"transitions": [],
"guides": [],
"metadata": {}
}
各字段说明:
| 字段 | 含义 | 备注 |
|---|---|---|
version |
工程格式版本 | 当前常量 "1.0"(PROJECT_VERSION) |
profile |
工程参数 | name/width/height/fps_num/fps_den/progressive/dar_num/dar_den |
bin |
素材库列表 | 每个素材含 id/name/source/duration/type |
tracks |
时间线轨道 | id/name/type(video |
tracks[].clips[] |
轨道上的剪辑实例 | clip_id 引用 bin 中素材、in/out 为素材内裁剪区间、position 为时间轴位置、filters 为该实例专属特效链 |
transitions |
轨道间转场 | 见第 3.5 节字段 |
guides |
向导标记 | position/label/type/comment |
metadata |
元信息 | created/modified 时间戳与 software 标识,保存时自动刷新 |
这种"bin 只存一次素材、timeline 片段通过 clip_id 引用"的设计让 JSON 保持归一化、易 diff,也方便程序化批量修改。可用 project json 直接输出当前工程原始 JSON,或 project info 查看聚合摘要(profile 的 fps 计算、bin/tracks/transition/guide 计数等,见 project.py)。
七、交互式 REPL:无需反复敲 --project
除了逐条一次性命令,CLI 还支持交互式 REPL,加载工程后进入提示符逐行输入,省去每次都带 --project:
cli-anything-kdenlive repl --project project.json
# 在 REPL 内直接输入:
# bin list
# timeline list
# export xml -o out.kdenlive
# quit
REPL 逻辑见 kdenlive_cli.py:它基于 prompt-toolkit 建立补全会话,用 shlex.split 解析输入行后以 standalone_mode=False 递归调用 cli.main(args);未加载工程时若执行 project new 亦可现场建工程。内置的 help 会打印各命令组速查表,quit/exit/q 退出,遇到 EOFError/KeyboardInterrupt 同样优雅退出。不传任何子命令直接运行 cli-anything-kdenlive 也会进入 REPL(kdenlive_cli.py)。
八、MLT XML 生成原理(源码级)
export xml 的底层是 mlt_xml.py 的 build_mlt_xml(),它对 Kdenlive 生成器 5(Gen 5 / doc version 1.1)兼容文档有系统性地构建,值得关注的关键点包括:
- profile 元素:把 JSON profile 换算为 MLT 的宽高、逐行标记、帧率与像素宽高比(SAR = DAR × height / width),colorspace 固定 709;
- 轨道排序:音频轨在前、视频轨在后(
audio_tracks + video_tracks),保证视频轨在 Kdenlive 中位于视觉上层(mlt_xml.py); - 一轨两 playlist:每个轨道用
chain(素材 producer)+ 双 playlist(一个放条目、一个预留空位给 mix)+ 包裹 tractor 的 Kdenlive 标准结构;轨道hide属性按音频/视频、mute/hide 状态推导为both/video/audio; - 滤镜落点:片段级滤镜直接挂在 playlist entry 的
<filter>上,写入mlt_service与从FILTER_REGISTRY反查的kdenlive_id(保证在 Kdenlive UI 中显示正确的特效名),参数逐个以<property>落盘(mlt_xml.py); - 内部转场:音频轨自动补
mix、视频轨自动补qtblend的always_active内部转场,用户添加的 dissolve/wipe 等则落在 sequence tractor 上并换算为帧坐标的in/out/a_track/b_track; - kdenlive 元数据:sequence 携带大量
kdenlive:sequenceproperties.*(时长、轨道数、缩放、zone、guides JSON 等),最终 project tractor 为melt播放的入口,并回写kdenlive:docproperties.*的 version/uuid/open sequences。
同时,时间换算工具 seconds_to_timecode() / timecode_to_seconds() / seconds_to_frames() / frames_to_seconds() 都集中在 mlt_xml.py:秒与 HH:MM:SS.mmm 时间码、秒与帧之间的转换被统一到这里,导出 XML 时所有时间点都以帧整数写入,保证与 profile 帧率一致、无浮点漂移。
九、测试与验证
harness 自带双层级测试,位于 cli_anything/kdenlive/tests:
cd /root/cli-anything/kdenlive/agent-harness
# 运行全部测试
python3 -m pytest cli_anything/kdenlive/tests/ -v
# 仅单元测试(仓库文档标注 60+ 用例,覆盖核心状态模型)
python3 -m pytest cli_anything/kdenlive/tests/test_core.py -v
# 仅端到端测试(仓库文档标注 40+ 用例,覆盖完整建工程→导出链路)
python3 -m pytest cli_anything/kdenlive/tests/test_full_e2e.py -v
源码目录还附带 tests/TEST.md 与供 Agent 直接消费的 skills/SKILL.md,后者把命令组整理为面向 Agent 的分组速查(Project/Bin/Timeline/Filter/Transition/Guide/Export/Session),便于将 harness 能力一键接入 cli-anything 的技能体系。
十、关键文件索引
结语:适合自动化与 Agent 的剪辑后端
从 KDENLIVE.md 的 SOP 可以看到,Kdenlive CLI harness 的价值不在于替代 GUI,而是把剪辑决策抽象成可审计、可撤销、可机器读取的命令序列:JSON 即工程真相,XML 导出即交付物,--json 即机器接口。无论是定时渲染流水线、批量字幕压制前处理,还是由 Agent 依据剧本脚本自动编排的多机位粗剪,都可以在完全不触碰图形界面的前提下完成工程搭建,再把 .kdenlive 交给 Kdenlive/melt 完成最终渲染。若需进一步把该 harness 注册为 Agent 技能,可参考同仓库 skills/README 与各 skill 目录的组织方式。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0632
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
video-shotcraftAI宣传片skill,使用 Remotion 制作电影级产品视频:提供106 张镜头配方卡和可复用的视频魔板。适用于 Claude Code 与 Codex以及所有其他智能体Markdown00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python09
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00