|
| 1 | +# capcut(Python 客户端) · capcut (Python client) |
| 2 | + |
| 3 | +中文 | [English](#english) |
| 4 | + |
| 5 | +用 Python 创建和编辑 CapCut / 剪映草稿。这是 [capcut-cli](https://github.com/renezander030/capcut-cli) 的一层薄封装:每次调用启动一次 `capcut` 命令,不经过 shell,返回它打印的那一份 JSON。没有服务、没有守护进程,磁盘上的草稿就是全部状态。打开剪映时,每一轨都还是可编辑的。 |
| 6 | + |
| 7 | +## 安装 |
| 8 | + |
| 9 | +```bash |
| 10 | +npm install -g capcut-cli # 命令行本体,需要 Node ≥ 18 |
| 11 | +pip install capcut # 本包,纯 Python,无依赖 |
| 12 | +capcut doctor # 检查环境 |
| 13 | +``` |
| 14 | + |
| 15 | +## 五行起步 |
| 16 | + |
| 17 | +```python |
| 18 | +import capcut |
| 19 | + |
| 20 | +d = capcut.run("quickstart", "旁白短视频", video="clip.mp4", ratio="9:16") |
| 21 | +capcut.run("add-text", d["draft_path"], "0s", "3s", "你好,世界", font_size=16) |
| 22 | +print(capcut.run("lint", d["draft_path"])["summary"]) |
| 23 | +``` |
| 24 | + |
| 25 | +- **关键字参数就是命令行选项**:`font_size=16` → `--font-size 16`,`karaoke=True` → `--karaoke`,列表会重复该选项,`None` / `False` 直接省略。 |
| 26 | +- **位置参数原样传递**,每个参数就是一个 argv,中文、空格、引号都不需要转义。 |
| 27 | +- 全部命令、参数和选项见[命令参考(中文)](https://github.com/renezander030/capcut-cli/blob/master/docs/command-reference.zh-CN.md),或者在 Python 里 `capcut.describe()`。 |
| 28 | + |
| 29 | +## 出错时 |
| 30 | + |
| 31 | +命令非零退出会抛出 `capcut.CommandError`,带 `status`、`data`(CLI 打印的 JSON,通常含 `error`)、`stdout`、`stderr`: |
| 32 | + |
| 33 | +```python |
| 34 | +try: |
| 35 | + capcut.run("lint", path) |
| 36 | +except capcut.CommandError as e: |
| 37 | + print(e.status, e.data) # lint 有错误时退出码为 2 |
| 38 | +``` |
| 39 | + |
| 40 | +不想抛异常就用 `capcut.run_raw(...)`,它返回 `Result`(`ok`、`status`、`data`、`error`)。找不到 `capcut` 命令时抛 `capcut.CliNotFound`,提示里有安装命令;也可以用环境变量 `CAPCUT_CLI` 指定,例如 `CAPCUT_CLI="node /path/to/capcut-cli/dist/index.js"`。 |
| 41 | + |
| 42 | +## 批量:`serve` |
| 43 | + |
| 44 | +`capcut serve` 是一个无状态的 JSONL 任务队列。从 Python 喂任务进去,拿回每个任务一条结果: |
| 45 | + |
| 46 | +```python |
| 47 | +results = capcut.serve([ |
| 48 | + capcut.Job("add-text", project=path, args=["8s", "2s", "关注我"], id="title"), |
| 49 | + capcut.Job("lint", project=path), |
| 50 | +], workers=2) |
| 51 | +for r in results: |
| 52 | + print(r["id"], r["ok"], r["status"], r["stdout"]) |
| 53 | +``` |
| 54 | + |
| 55 | +失败的任务是一条 `ok: false` 的结果,不是异常。 |
| 56 | + |
| 57 | +## 剪映 6.0+ 用户 |
| 58 | + |
| 59 | +新建的草稿是明文,据报告剪映 11.4(macOS)能打开并就地升级,其他版本未验证;已有的加密草稿本 CLI 不读取。`capcut.doctor()` 会报告环境,`capcut.run("decrypt", path)` 会报告某个草稿的加密状态;来龙去脉见 [jianying-encryption.zh-CN.md](https://github.com/renezander030/capcut-cli/blob/master/docs/jianying-encryption.zh-CN.md)。 |
| 60 | + |
| 61 | +--- |
| 62 | + |
| 63 | +## English |
| 64 | + |
| 65 | +Create and edit CapCut / JianYing drafts from Python. A thin layer over [capcut-cli](https://github.com/renezander030/capcut-cli): each call spawns the `capcut` binary once, without a shell, and returns the one JSON document it prints. No server, no daemon; the draft on disk is the only state, and every track stays editable in the app. |
| 66 | + |
| 67 | +### Install |
| 68 | + |
| 69 | +```bash |
| 70 | +npm install -g capcut-cli # the CLI itself, Node >= 18 |
| 71 | +pip install capcut # this package, pure Python, no dependencies |
| 72 | +capcut doctor # environment check |
| 73 | +``` |
| 74 | + |
| 75 | +### Five lines |
| 76 | + |
| 77 | +```python |
| 78 | +import capcut |
| 79 | + |
| 80 | +d = capcut.run("quickstart", "Narrated short", video="clip.mp4", ratio="9:16") |
| 81 | +capcut.run("add-text", d["draft_path"], "0s", "3s", "Hello, world", font_size=16) |
| 82 | +print(capcut.run("lint", d["draft_path"])["summary"]) |
| 83 | +``` |
| 84 | + |
| 85 | +- **Keyword arguments are flags**: `font_size=16` → `--font-size 16`, `karaoke=True` → `--karaoke`, a list repeats the flag, `None` / `False` are dropped. |
| 86 | +- **Positional arguments pass through as they are**, one argv token each: text with spaces or quotes never needs escaping. |
| 87 | +- Every command, argument and option: [command reference](https://github.com/renezander030/capcut-cli/blob/master/docs/command-reference.md), or `capcut.describe()` from Python. |
| 88 | + |
| 89 | +### Errors |
| 90 | + |
| 91 | +A non-zero exit raises `capcut.CommandError` with `status`, `data` (the CLI's JSON, usually with `error`), `stdout`, `stderr`: |
| 92 | + |
| 93 | +```python |
| 94 | +try: |
| 95 | + capcut.run("lint", path) |
| 96 | +except capcut.CommandError as e: |
| 97 | + print(e.status, e.data) # lint exits 2 on errors |
| 98 | +``` |
| 99 | + |
| 100 | +`capcut.run_raw(...)` never raises; it returns a `Result` (`ok`, `status`, `data`, `error`). A missing binary raises `capcut.CliNotFound` with the install line; `CAPCUT_CLI` can point at one explicitly, e.g. `CAPCUT_CLI="node /path/to/capcut-cli/dist/index.js"`. |
| 101 | + |
| 102 | +### Batch: `serve` |
| 103 | + |
| 104 | +`capcut serve` is a stateless JSONL job queue. Feed it jobs from Python and get one result per job: |
| 105 | + |
| 106 | +```python |
| 107 | +results = capcut.serve([ |
| 108 | + capcut.Job("add-text", project=path, args=["8s", "2s", "Subscribe"], id="title"), |
| 109 | + capcut.Job("lint", project=path), |
| 110 | +], workers=2) |
| 111 | +for r in results: |
| 112 | + print(r["id"], r["ok"], r["status"], r["stdout"]) |
| 113 | +``` |
| 114 | + |
| 115 | +A failed job is a result with `ok: false`, not an exception. |
| 116 | + |
| 117 | +### Development |
| 118 | + |
| 119 | +```bash |
| 120 | +cd python && python -m unittest discover -s tests -v |
| 121 | +python -m build |
| 122 | +``` |
| 123 | + |
| 124 | +MIT, same as capcut-cli. |
0 commit comments