Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
node_modules
dist
.git
.github
test
docs
media
examples
*.md
.husky
hooks
skills
.claude-plugin
marketplace.json
36 changes: 36 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: CI

on:
push:
branches: [main, master]
pull_request:
workflow_dispatch:

jobs:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
node: ['18', '20', '22']
name: test (Node ${{ matrix.node }})
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
cache: npm
- run: npm ci
- run: npm run build
- run: npm test

lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: npm
- run: npm ci
- run: npm run lint
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,25 @@

All notable changes to capcut-cli are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); the project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.6.0] — 2026-05-29

Distribution and integration release. No breaking changes to existing commands; everything stays zero-dep, JSON-by-default, and pipeable.

### Added

- **`capcut doctor`** — environment preflight that inspects the machine, not a draft: Node version (hard requirement, ≥ 18), a whisper binary on `PATH` (for `caption`), `ANTHROPIC_API_KEY` (for `translate`), and the default per-OS CapCut/JianYing project directory. JSON by default, `-H` for a human checklist. Exits `1` only on a hard failure.
- **Importable Node library** — `import { loadDraft, saveDraft, findSegment, findMaterial, getTracksByType, extractText, updateTextContent, lintDraft, detectVersion, runDoctor } from "capcut-cli"`, with types. New `src/lib.ts` entry point; `package.json` `exports`/`main`/`types` map to `dist/lib.js`; `tsconfig` now emits `.d.ts`. Importing the package no longer executes the CLI.
- **Dockerfile + `.dockerignore`** — zero-dep multi-stage build; the final image is Node + `dist/` + `templates/`. Drafts mount at `/work`. Also runs `serve` over a stdin pipe.
- **GitHub Action (`action.yml`)** — composite action wrapping `capcut lint` so drafts can be gated in CI; `lint` exit code `2` (errors) fails the job. `uses: renezander030/capcut-cli@v0.6`.
- **Three new shipped templates** — `caption-pop` (bold white center subtitle), `lower-third` (handle/name attribution), `hook-question` (large top-of-frame hook). Catalogue grows 3 → 6, all validated by the roundtrip suite.
- **`serve-automation.md` example** — JSONL job/result contract and four integration paths (local pipe, n8n Execute Command, cloud builders via webhook→queue-file, Docker).

### CI / Quality

- **GitHub Actions CI** — test matrix across Node 18 / 20 / 22 plus a Biome lint job, on every push and pull request.
- **Fuzz / injection test suite** — 12 malformed `draft_content.json` inputs (non-JSON, truncated, wrong-shape, prototype-pollution attempts, deep nesting) across six read commands assert graceful failure: no hang, no leaked stack trace, single-line JSON error on stderr. Plus a prototype-pollution non-regression check.
- Test suite grew to 113 passing tests (doctor, fuzz, library, and the three new templates added their own coverage).

## [0.5.0] — 2026-05-25

Six new commands voted in from [Discussion #1](https://github.com/renezander030/capcut-cli/discussions/1), shipped as a single release. All keep the zero-dep, JSON-by-default, pipeable design.
Expand Down
23 changes: 23 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# capcut-cli — zero runtime deps, so the final image is just Node + dist/.
# Build: docker build -t capcut-cli .
# Run: docker run --rm -v "$PWD:/work" capcut-cli info /work/draft_content.json
# cat jobs.jsonl | docker run --rm -i -v "$PWD:/work" capcut-cli serve

FROM node:20-alpine AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY tsconfig.json biome.json ./
COPY src ./src
RUN npm run build

FROM node:20-alpine
WORKDIR /app
# No `npm install` here: the package declares zero runtime dependencies.
COPY --from=build /app/dist ./dist
COPY templates ./templates
COPY package.json ./
# Drafts are mounted at runtime; /work is the conventional mount point.
WORKDIR /work
ENTRYPOINT ["node", "/app/dist/index.js"]
CMD ["--help"]
74 changes: 68 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,32 @@
# capcut-cli

[![CI](https://github.com/renezander030/capcut-cli/actions/workflows/ci.yml/badge.svg)](https://github.com/renezander030/capcut-cli/actions/workflows/ci.yml)
[![npm version](https://img.shields.io/npm/v/capcut-cli.svg)](https://www.npmjs.com/package/capcut-cli)
[![npm downloads](https://img.shields.io/npm/dm/capcut-cli.svg)](https://www.npmjs.com/package/capcut-cli)
[![node](https://img.shields.io/node/v/capcut-cli.svg)](https://nodejs.org)
[![license](https://img.shields.io/npm/l/capcut-cli.svg)](./LICENSE)

English | [中文](./README.zh-CN.md)

**The CapCut/JianYing toolkit that survives the next ByteDance update — and auto-captions with real caption objects.**
**The CapCut/JianYing CLI any LLM agent can drive — zero dependencies, no server, both namespaces in one binary.**

A pure CLI any LLM can drive: no MCP server, no HTTP daemon, no state. Inspect drafts, build from scratch, add media, modify subtitles, auto-caption with whisper, translate to N languages, cut long-form to shorts. Zero runtime deps, both CapCut and JianYing namespaces in one binary, JSON output by default.
Every command reads and writes `draft_content.json` directly: JSON in, JSON out, no MCP server, no HTTP daemon, no state to babysit. That makes it a deterministic boundary any model (Claude, DeepSeek, GLM, Kimi) can call from a pipeline. Inspect drafts, build from scratch, add media, edit subtitles, auto-caption with whisper, translate to N languages, and cut long-form to shorts. Because there is no private API in the loop, it keeps working across ByteDance updates — and `caption` writes real caption objects, not the text-segment mimics other tools settle for.

**Use it three ways:**

- **CLI** — `npm install -g capcut-cli`, then `capcut <command> <project>`
- **Library** — `import { loadDraft, lintDraft, saveDraft } from "capcut-cli"` (typed, zero-dep)
- **Queue runner** — `capcut serve` reads JSONL jobs from stdin and drops into [n8n / Make / Coze](./examples/serve-automation.md)

Run `capcut doctor` first to verify your environment (Node, whisper, draft directory).

**New in v0.6** — `doctor` (environment preflight), an importable Node library (`import { … } from "capcut-cli"`), an official [Dockerfile](./Dockerfile), a [GitHub Action](./action.yml) that lints drafts in CI, three new shipped templates (`caption-pop`, `lower-third`, `hook-question`), and a CI matrix across Node 18/20/22.

**New in v0.4** — `caption` (whisper → real caption objects, not the import-srt text-mimics), `migrate` (mask ↔ common_masks across CapCut/JianYing version jumps), `lint` (schema-aware checks: overlaps, line length, missing files), `version` (detect support status), `translate` (Anthropic-API multi-language draft clone), `add-sfx`, `chroma`, `serve` (stateless JSONL queue runner for n8n/Coze/Make), and `export --batch` (EXPERIMENTAL macOS UI-automated render queue).

## v0.5 — vote on what ships next
## v0.5 — shipped, community-voted

Open for community vote on **[Discussion #1](https://github.com/renezander030/capcut-cli/discussions/1)**. 👍 the comments for the features you want most — I ship the top 3-5 as a single v0.5 release within ~2 weeks.
All six features below were voted in on **[Discussion #1](https://github.com/renezander030/capcut-cli/discussions/1)** and shipped together in v0.5. Want a say in what lands next? 👍 the comments there, or open a new discussion.

- ✅ `audio-fade <project> <id> --in <s> --fade-out <s>` — fade-in / fade-out on audio segments (proper `audio_fades` objects, not volume keyframes) **shipped in v0.5**
- ✅ `bubble-text <project> <id> --bubble <slug>` / 花字 — bubble / decorative text effects + `enums --bubbles` discovery **shipped in v0.5**
Expand All @@ -24,7 +35,7 @@ Open for community vote on **[Discussion #1](https://github.com/renezander030/ca
- ✅ `import-ass <project> <ass-path>` — ASS subtitle import alongside existing `import-srt` **shipped in v0.5**
- ✅ `mix-mode <project> <id> <mode>` — blend modes per video segment (multiply, screen, overlay, …) **shipped in v0.5**

> Voting closes when v0.5 ships. If your feature is missing, drop a comment on Discussion #1.
> All six shipped in v0.5.0. If the feature you want is missing, drop a comment on Discussion #1.

## Workflow

Expand Down Expand Up @@ -129,7 +140,15 @@ Status of every feature shipped. ✅ = implemented, ⬜ = roadmap. Section ancho
- ✅ `caption` — whisper shell-out (openai-whisper / whisper.cpp / faster-whisper) → real caption-track segments with `sub_type` + `caption_template_info` (addresses pyJianYingDraft #148 — no more text-segment mimics)
- ✅ `translate` — Anthropic-API multi-language draft clone, zero runtime deps (uses built-in `fetch`). `--dry-run` for safe inspection. Original stays untouched

### v0.5 — new commands (in progress)
### v0.6 — distribution & integration
- ✅ `doctor` — environment preflight: Node version, whisper binary (for `caption`), `ANTHROPIC_API_KEY` (for `translate`), default CapCut/JianYing project directory. Exits 1 only on hard failures
- ✅ Node **library** — `import { loadDraft, lintDraft, saveDraft, detectVersion, runDoctor } from "capcut-cli"` — the core, typed and zero-dep, importable without running the CLI
- ✅ [**Dockerfile**](./Dockerfile) — zero-dep multi-stage image; `docker run --rm -v "$PWD:/work" capcut-cli info /work/draft_content.json`
- ✅ [**GitHub Action**](./action.yml) — `uses: renezander030/capcut-cli@v0.6` to lint drafts in CI (exit 2 on errors fails the job)
- ✅ Three new templates — `caption-pop`, `lower-third`, `hook-question` (six shipped templates total)
- ✅ CI matrix across Node 18 / 20 / 22 + Biome lint on every push and PR

### v0.5 — new commands (shipped)
- ✅ `mix-mode` — set blend mode on a video segment (normal · multiply · screen · overlay · soft-light · hard-light · color-dodge · color-burn · darken · lighten · difference · exclusion)
- ✅ `audio-fade` — fade-in / fade-out on an audio segment via `materials.audio_fades[]` (real fade material, not `volume` keyframes)
- ✅ `add-cover` — set the draft's cover frame (thumbnail) to a local image (PNG/JPG); `--time <ms>` defaults to 0
Expand Down Expand Up @@ -187,6 +206,49 @@ Add the marketplace, then enable the plugin:

This gives Claude Code the `/capcut-cli:capcut-edit` skill -- it learns every command, the progressive disclosure navigation pattern, and how to find your CapCut projects on macOS/Windows. Auto-installs the CLI on first enable.

### Use as a Node library

The core is importable and typed — no shelling out, no CLI process:

```ts
import { loadDraft, lintDraft, saveDraft, detectVersion } from "capcut-cli";

const { draft, filePath } = loadDraft("./my-project/draft_content.json");
console.log(detectVersion(draft).support.status); // supported | untested | known-broken
const issues = lintDraft(draft); // [{ severity, code, message, location }]
saveDraft(filePath, draft);
```

Importing the package never runs the CLI. Exposed: `loadDraft`, `saveDraft`, `findSegment`, `findMaterial`, `getTracksByType`, `extractText`, `updateTextContent`, `lintDraft`, `detectVersion`, `runDoctor`, plus their types.

### Docker

Zero runtime deps, so the image is just Node + the build output. Mount your drafts at `/work`:

```bash
docker build -t capcut-cli .
docker run --rm -v "$PWD:/work" capcut-cli info /work/draft_content.json
cat jobs.jsonl | docker run --rm -i -v "$PWD:/work" capcut-cli serve
```

### GitHub Action — lint drafts in CI

Gate caption quality (overlaps, line length, missing files) on every push. `lint` exits `2` on errors, which fails the job:

```yaml
- uses: renezander030/capcut-cli@v0.6
with:
project: ./drafts/my-short
args: --max-chars 32 --max-cue-secs 6
```

### Verify your environment

```bash
capcut doctor # JSON report; exit 1 only on a hard failure (Node < 18)
capcut doctor -H # human-readable checklist
```

### Why a CLI, not an MCP server

Other CapCut / JianYing tooling exposes an HTTP API or MCP server. `capcut-cli` deliberately does not:
Expand Down
21 changes: 16 additions & 5 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,32 @@
# capcut-cli

[![CI](https://github.com/renezander030/capcut-cli/actions/workflows/ci.yml/badge.svg)](https://github.com/renezander030/capcut-cli/actions/workflows/ci.yml)
[![npm version](https://img.shields.io/npm/v/capcut-cli.svg)](https://www.npmjs.com/package/capcut-cli)
[![npm downloads](https://img.shields.io/npm/dm/capcut-cli.svg)](https://www.npmjs.com/package/capcut-cli)
[![node](https://img.shields.io/node/v/capcut-cli.svg)](https://nodejs.org)
[![license](https://img.shields.io/npm/l/capcut-cli.svg)](./LICENSE)

[English](./README.md) | 中文

**剪映 / CapCut 工具链,扛得住字节跳动下次改版 —— 自动加的字幕也是真字幕对象(不是 import-srt 那种文本伪装)。**
**任何大模型 Agent 都能驱动的剪映 / CapCut 命令行 —— 零依赖、无服务、CapCut + 剪映共用一个二进制。**

任何能输出 JSON 的大模型都能驱动它:不用 MCP 服务,不用 HTTP 守护进程,无状态。命令行查看工程、从零搭草稿、加素材、改字幕、用 whisper 自动打字幕、一键克隆成多语言版本、把长视频切成短片。直接读写 `draft_content.json`,零运行时依赖,CapCut + 剪映两个命名空间共用一个二进制。
每个命令都直接读写 `draft_content.json`:JSON 进、JSON 出,不用 MCP 服务,不用 HTTP 守护进程,无状态。任何模型(Claude、DeepSeek、GLM、Kimi)都能在流水线里直接调用这个确定性边界。命令行查看工程、从零搭草稿、加素材、改字幕、用 whisper 自动打字幕、一键克隆成多语言版本、把长视频切成短片。因为链路里没有任何私有 API,所以扛得住字节跳动下次改版 —— 而且 `caption` 写的是真字幕对象,不是别的工具那种文本伪装。

**三种用法:**

- **命令行** —— `npm install -g capcut-cli`,然后 `capcut <command> <project>`
- **代码库** —— `import { loadDraft, lintDraft, saveDraft } from "capcut-cli"`(带类型、零依赖)
- **队列执行器** —— `capcut serve` 从 stdin 读 JSONL 任务,可对接 [n8n / Make / 扣子 Coze](./examples/serve-automation.md)

先跑 `capcut doctor` 检查环境(Node、whisper、草稿目录)。

**v0.6 新增** —— `doctor`(环境预检)、可导入的 Node 代码库(`import { … } from "capcut-cli"`)、官方 [Dockerfile](./Dockerfile)、在 CI 里检查草稿的 [GitHub Action](./action.yml)、三个新模板(`caption-pop`、`lower-third`、`hook-question`),以及覆盖 Node 18/20/22 的 CI 矩阵。

**v0.4 新增** —— `caption`(whisper → 真字幕对象,不再是 import-srt 那种文本伪装)、`migrate`(剪映 5.9 / CapCut 9.6 之间的 `mask` ↔ `common_masks` schema 迁移)、`lint`(字幕检查:重叠、行长、缺失素材文件)、`version`(检测兼容状态)、`translate`(多语言草稿克隆,走 Anthropic API)、`add-sfx`、`chroma`、`serve`(无状态 JSONL 队列 —— 对接 n8n / Coze / 扣子 / Make)、`export --batch`(**实验性** macOS UI 自动化批量导出)。

## v0.5 投票决定下一步
## v0.5 已发布(社区投票决定)

下面是 v0.5 候选功能,欢迎到 **[Discussion #1](https://github.com/renezander030/capcut-cli/discussions/1)** 给你想要的功能 👍 —— 我会按票数把前 3-5 个打包进一个 v0.5 release(目标 2 周内出)
下面六个功能都是在 **[Discussion #1](https://github.com/renezander030/capcut-cli/discussions/1)** 投票选出、并在 v0.5 一起发布的。想决定下一步加什么?去那里给评论点 👍,或者开一个新 discussion

- `audio-fade <project> <id> --in <秒> --out <秒>` —— 音频淡入淡出(写真正的 `audio_fades` 对象,不再用音量关键帧凑)
- `bubble-text <project> <id> --bubble <slug>` / 花字 —— 文本气泡 / 花字特效 + `enums --bubbles` 枚举发现
Expand All @@ -24,7 +35,7 @@
- `import-ass <project> <ass-path>` —— ASS 字幕导入(跟现有 `import-srt` 并存)
- `mix-mode <project> <id> <模式>` —— 视频片段混合模式(正片叠底 / 滤色 / 叠加 …)

> 投票截止到 v0.5 发布为止。如果你想要的功能不在列表里,去 Discussion #1 留言。
> 六个功能都已在 v0.5.0 发布。如果你想要的功能不在列表里,去 Discussion #1 留言。

> **想要完整的国产大模型 + 剪映短视频流水线?** `capcut-cli` 是引擎,配套的 **[病毒短视频蓝图(完整教程 + 蓝图下载)](https://renezander.com/zh-cn/guides/automate-xiaohongshu-capcut-cli/?utm_source=capcut-cli&utm_medium=readme&utm_campaign=hero-cn)** 给你完整方法 —— DeepSeek / GLM / Kimi / Qwen 都能跑,专为 **小红书 + 抖音** 优化(不是 YouTube),**支付宝 / 微信支付** 通过 Stripe 直接下单。

Expand Down
30 changes: 30 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: capcut-cli
description: Lint CapCut/JianYing drafts in CI — caption overlaps, line length, cue duration, missing material refs and files.
author: renezander030
branding:
icon: check-square
color: green

inputs:
project:
description: Path to draft_content.json (or its parent directory) to lint.
required: true
args:
description: 'Extra flags passed to `capcut lint` (e.g. "--max-chars 32 --max-cue-secs 6").'
required: false
default: ''
version:
description: capcut-cli version to install from npm.
required: false
default: latest

runs:
using: composite
steps:
- name: Install capcut-cli
shell: bash
run: npm install -g "capcut-cli@${{ inputs.version }}"
- name: Lint draft
shell: bash
# `lint` exits 0 (clean) / 1 (warnings) / 2 (errors) — exit 2 fails the job.
run: capcut lint "${{ inputs.project }}" ${{ inputs.args }} -H
1 change: 1 addition & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Copy-paste recipes for common CapCut / JianYing workflows. Every recipe is one s
| [keyframe-zoom.md](./keyframe-zoom.md) | Programmatic Ken Burns zoom-in/out keyframes on one segment |
| [keyframe-pan.md](./keyframe-pan.md) | Unfinished-pan keyframe pattern for epilogue / payoff stills |
| [verify-vo-alignment.md](./verify-vo-alignment.md) | Pre-flight check on ElevenLabs voiceover + word-level timestamps |
| [serve-automation.md](./serve-automation.md) | Wire the stateless JSONL queue runner into n8n / Make / Coze / Docker |

All shell-only recipes assume `capcut` is on your `$PATH` (`npm install -g capcut-cli`).
The three keyframe / VO recipes ship with companion Python scripts under [`./scripts/`](./scripts/) — Python 3.9+, no external deps.
Expand Down
Loading
Loading