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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ All notable changes to capcut-cli are documented here. The format follows [Keep

## [Unreleased]

### Fixed

- `fixture` redacts JSON-escaped Windows home paths, including paths inside JSON strings (#134). Every new bundle automatically runs the existing residual-value check; the CLI exits nonzero and `SANITIZE_REPORT.json` records failure if a recognizable leak remains. `fixture <bundle> --check` still verifies an existing bundle.
- `compile` resolves ratio-only and explicit-dimension canvases through the same resolver as `init` and `quickstart`, including `--check`. Video/audio material and registration durations use the full probed source duration while segments retain their requested durations and in-points. Source ranges, including speed, are validated before any draft is created (#133).
- On the fixture-backed CapCut 8.7.0 Windows layout, reads follow the validated `Timelines/project.json` active pointer. Normal writes synchronize that document, its readable mirrors, and the readable root mirrors through the transactional write path, preserving document IDs and other timelines. `sync-timelines` uses active → root on this layout, including with `--nested`; existing divergent root edits remain visible in its plan and newer-mirror gate. Assets and metadata remain at the project root. Other versions/OSes keep their prior selection behavior; a patched app round-trip remains pending (#50).

## [0.26.0] — 2026-09-25

### Added
Expand Down
6 changes: 3 additions & 3 deletions docs/command-reference.json
Original file line number Diff line number Diff line change
Expand Up @@ -5137,7 +5137,7 @@
],
"type": "boolean",
"required": false,
"description": "Scan the finished bundle (SANITIZE_REPORT.json and README included) for residual home paths, emails, device ids and the account name, reporting file:line per finding and exiting non-zero on any. With only a bundle directory as the argument, re-checks an existing bundle without rebuilding."
"description": "Scan the finished bundle (SANITIZE_REPORT.json and README included) for residual home paths, emails, device ids and the account name, reporting file:line per finding and exiting non-zero on any. New bundles are checked automatically. With only a bundle directory as the argument, re-checks an existing bundle without rebuilding."
}
],
"mutates": false,
Expand Down Expand Up @@ -5170,7 +5170,7 @@
],
"type": "boolean",
"required": false,
"description": "Rewrite only the drifted mirror files from draft_content.json (default: print the plan only)."
"description": "Rewrite only the drifted mirrors from the canonical timeline (default: print the plan only). On the evidenced Windows 8.7.0 active layout, the selected nested document is canonical."
},
{
"name": "nested",
Expand All @@ -5179,7 +5179,7 @@
],
"type": "boolean",
"required": false,
"description": "Also reconcile the nested Timelines/<id>/ documents (draft_info.json, draft_content.json, template-2.tmp), each keeping its own GUID — the workaround verified on CapCut Mac 9.2.8 in issue #50, as an explicit opt-in. Timelines/project.json is never touched."
"description": "Also reconcile the nested Timelines/<id>/ documents (draft_info.json, draft_content.json, template-2.tmp), each keeping its own GUID — the workaround verified on CapCut Mac 9.2.8 in issue #50, as an explicit opt-in. On Windows 8.7.0 active layouts only the selected timeline and root mirrors are reconciled, even with this flag. Timelines/project.json is never touched."
}
],
"mutates": true,
Expand Down
86 changes: 86 additions & 0 deletions docs/reviews/issues-50-133-134.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Review: active timelines, compilation, and fixture redaction

This patch addresses #133 and #134 and the CapCut **8.7.0 Windows** case in #50.
The Windows field fixture is credited in `test/fixtures/capcut-8.7-windows-active/README.md`.
It proves file structure and divergence. The reporter supplied the sentinel
open/close observation; a patched CLI round-trip still needs to run in CapCut.

## Build and automated checks

```sh
npm ci
npm run build
npm run lint
node --test test/active-timeline.test.mjs test/compile-media-canvas.test.mjs test/fixture-check.test.mjs
npm test
```

## Fixture redaction (#134)

With CapCut closed, collect a bundle from a Windows project:

```sh
node dist/index.js fixture <project> --out <bundle>
node dist/index.js fixture <bundle> --check
```

Both should succeed. `redaction_check.ok` must be true without specifying
`--check` during creation. Inspect `draft_meta_info.json`: `draft_root_path`
must contain `C:\\Users\\USER`, with no real account name. JSON strings
inside other JSON must remain parseable. Check the reports too. Copy a test
JSON with an unredacted home path into the bundle and re-check: expect exit 1,
file/line findings, and no private value echoed to stderr. A failed check means
the bundle still needs review before sharing.

## Compiler (#133)

Use the issue's spec and a source whose duration is known. Run:

```sh
node dist/index.js compile <spec.json> --check
node dist/index.js compile <spec.json> --out <new-draft>
node dist/index.js lint <new-draft>
```

For `ratio: "9:16"`, the plan and every written document must say 1080×1920
and `9:16`. For the reported 89.28-second source, materials and sidecar entries
must have duration 89,280,000 µs. Segments stay six seconds long and retain
source starts 20,000,000 and 40,000,000 µs. The source-range warning should
disappear. Open the new draft in the app and verify the portrait canvas and
that the two cuts show their intended source moments. This also tests how
the app materializes a newly created, root-only project; that behavior is
separate from selecting a pre-existing active timeline.

Try a source start plus speed-adjusted duration beyond the source end. Both
`--check` and compilation should reject it before creating an output folder.

## Existing active project (#50)

Use a copy of an app-created project from CapCut 8.7.0.3685 on Windows, with
the app fully closed. Retain a backup of the complete folder.

```sh
node dist/index.js diagnose <project>
node dist/index.js texts <project>
node dist/index.js set-text <project> <segment-id> "ACTIVE TIMELINE PATCH"
node dist/index.js diagnose <project>
```

The first report must select `Timelines/<main_timeline_id>/draft_content.json`
and show the active timeline ID. If roots already contain unsynchronized old
CLI edits, they appear as divergence; review those copies before doing a new
edit, since a normal write starts from the active timeline. For an explicit
repair, `sync-timelines <project>` previews the active → root direction and
retains its newer-mirror refusal unless deliberately overridden.

After the write, the active document and readable mirrors must all contain
the new caption. IDs, envelopes, `Timelines/project.json`, and other timelines
must be preserved. The final diagnose report should show no content divergence.
Open CapCut: verify the new caption. Close the project, re-run `texts` and
`diagnose`, and verify that the caption survives. Repeat with `add-text` and
`add-video`; imported assets and `draft_materials` must live at the project
root. Test `restore` to confirm the active document and mirrors return together.

Record the desktop version, OS, commands, and before/after result in #50.
Keep #50 open until this patched round-trip passes. The patch makes no new
compatibility claim for 7.x, Mac 9.2.8-beta4, or other Windows versions.
1 change: 1 addition & 0 deletions docs/version-support.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Run `capcut version <project>` for schema flags and `capcut diagnose <project> -
| 6.5–8.0 | expected-compatible | unverified | No committed app-created fixtures. Enum/schema changes appear additive. |
| 7.9 / 8.9 (International, macOS) | reported | one field measured | A scan of 38 app-authored drafts on one machine established that `materials.texts[].content` → `styles[].range` holds UTF-16 code units, not UTF-16LE bytes: 211 text materials in code units, none in bytes ([#85](https://github.com/renezander030/capcut-cli/issues/85)). That settled a real writer bug, fixed in 0.19.1. It is a measurement of one field, not a suite run — no sanitized app-created folder from either version is committed yet, so these versions stay off "fixture-tested". |
| 8.4.0 (International, macOS) | reported | root → nested on first open | A draft created from a copy of a real 8.4 project (`init --template`), edited on the root files only, opened with every segment intact; the app then created `Timelines/project.json` and `Timelines/<id>/` itself from the root documents ([#50](https://github.com/renezander030/capcut-cli/issues/50), IrizaD, 2026-08-31). The bundled 6.5.0 template's draft was refused on the same build (see the 8.7 row). This is the materialisation direction v0.23's seeding relies on: a seeded draft ships no `Timelines/` and lets the app build it. |
| 8.7.0 Windows, existing active timeline | fixture-tested storage selection; patched desktop round-trip pending | active document → root mirrors | The app-authored fixture from [#50](https://github.com/renezander030/capcut-cli/issues/50#issuecomment-5954728670), fpisasale / Spirito Digitale, 2026-10-02, is committed under `test/fixtures/capcut-8.7-windows-active/`. A valid active pointer selects the nested document; ordinary writes synchronize only that timeline and the root mirrors. Other timelines are preserved. The reporter observed nested content winning on open and close on 8.7.0.3685; a patched app round-trip remains pending. This evidence is specific to this storage shape and OS; the version-only registry retains its narrower existing evidence label. |
| 8.7 Windows | reported + synthetic-tested + one real round-trip (negative with the bundled template, positive with a captured one) | adapter shipped; seed from the store | Issue #35 reports that `draft_content.json` edits may be ignored in favour of `template-2.tmp` / `draft_meta_info.json`. v0.11 discovers nested/string JSON timeline envelopes, selects modern storage, synchronizes every readable target, and provides `diagnose --bundle` and `fixture --out` (one-command sanitized bundle). v0.13 adds `sync-timelines` to reconcile an already-drifted mirror (plan by default, `--apply` to write); `diagnose` names it as the remedy. **Real validation ([#111](https://github.com/renezander030/capcut-cli/issues/111), 2026-09-10):** every draft built from the bundled 6.5.0 template — `quickstart` included — was refused by a real 8.7.0 Windows install as "from an unusual path"; `register --materials`, `relink` and `sync-timelines` changed nothing, because the cause is the template's stale markers (#67), not the path. Compiled against a template captured from the installed app, the same pipeline round-tripped on 9.3.0 (open, edit, save, close, reopen — edit persisted). v0.23 makes that the default: `init` / `quickstart` / `compile` seed new drafts from the store's newest app-authored project, and `migrate --from-store` restamps drafts already built. A reporter-provided real folder is still required before marking this fixture-tested. |
| 9.2.8-beta4 (International, macOS) | reported | nested document authoritative — artifact pending | On this build the app reads `Timelines/<main_timeline_id>/draft_info.json`; the root `draft_info.json` / `template-2.tmp` are legacy mirrors, so a root-only write is a silent no-op from the app's point of view ([#50](https://github.com/renezander030/capcut-cli/issues/50), k12ktv, 2026-08-23). `sync-timelines --nested --apply` is the opt-in repair for a project whose nested document exists; the canonical flip stays gated on the requested fixture. Note that `init --template <app project>` used to copy the donor's `Timelines/` too, which is exactly how the report's new draft opened with the donor's empty timeline — v0.23 never carries `Timelines/` over. |
| 9.3.0 (International, macOS) | reported | opens tool-built drafts seeded from a real project | The #111 reporter's real round-trip (above), and the shell-first recipe published at [zxypro1/capcut-shell-inject](https://github.com/zxypro1/capcut-shell-inject) (2026-09-11): create an empty project in the app, quit, write into that folder with this CLI, then `sync-timelines --nested --apply` and `register --materials --apply`. Both are the same finding — the app accepts a draft whose schema markers it wrote itself — and `--template auto` (the v0.23 default) is that recipe automated. Also on 9.3.0: a video material's blank `local_material_id` leaves the clip unresolvable with no in-app repair ([JmsLdrn/capcut-mcp#1](https://github.com/JmsLdrn/capcut-mcp/issues/1)); v0.23 links it at add time and `lint --fix` repairs existing drafts (`media-unlinked`). |
Expand Down
1 change: 1 addition & 0 deletions docs/version-support.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ CapCut 和剪映在不断演进一套没有文档记录的本地磁盘 schema。
| 6.2.8 | fixture-tested | 已支持 | 权威 fixture 位于 `test/draft_content.json`;完整命令集均可用。 |
| 6.5–8.0 | expected-compatible | 未验证 | 尚无已提交的、由应用创建的 fixture。枚举/schema 变化看起来是增量式的。 |
| 7.9 / 8.9(国际版,macOS) | reported | 仅实测一个字段 | 在一台机器上扫描 38 份由 App 创建的草稿后确认:`materials.texts[].content` → `styles[].range` 存的是 UTF-16 码元,而非 UTF-16LE 字节 —— 211 个文本素材全部是码元,没有一个是字节([#85](https://github.com/renezander030/capcut-cli/issues/85))。这解决了一个真实的写入 bug,已在 0.19.1 修复。这只是对一个字段的实测,不是完整套件的测试 —— 这两个版本目前都还没有已提交的、脱敏后的应用创建文件夹,因此暂不标注为 fixture-tested。 |
| 8.7.0 Windows,已有活动时间线 | 存储选择 fixture-tested;修补版桌面往返待验证 | 活动文档 → 根目录镜像 | [#50](https://github.com/renezander030/capcut-cli/issues/50#issuecomment-5954728670) 的应用创建样本已保存于 `test/fixtures/capcut-8.7-windows-active/`。有效活动指针选择嵌套文档,普通写入同步该时间线与根目录镜像,保留其他时间线。报告者在 8.7.0.3685 上观察到嵌套内容在打开及关闭时获胜;修补版仍需桌面往返测试。此证据仅适用于该 Windows 存储形状,按版本查询的注册表保持原有证据标签。 |
| 8.7 Windows | reported + synthetic-tested | 适配已发布,真实验证待定 | Issue #35 报告称,对 `draft_content.json` 的修改可能被应用忽略,转而采用 `template-2.tmp` / `draft_meta_info.json`。v0.11 起可发现嵌套/字符串形式的 JSON 时间线信封,会选择更新的存储,同步每一个可读的目标,并提供 `diagnose --bundle` 与 `fixture --out`(一条命令生成脱敏包)。v0.13 新增 `sync-timelines`,用于协调已经漂移的镜像(默认只出计划,`--apply` 才写入);`diagnose` 会把它列为应对方案。在标记为 fixture-tested 之前,仍需要报告者提供一份真实文件夹。 |
| 9.x | expected-compatible | 未验证 | `common_masks` 可能与旧版蒙版字段共存。请使用 `version`、`diagnose` 和 `migrate`;不要把这一行视为桌面应用层面的验证。 |
| 10.x(Mac 与 Windows) | reported | 写入被护栏拦截 | 据报告,新版本会把工具写入的草稿判定为已损坏(「内容已损坏」;pyJianYingDraft#177、#194 对应剪映 10.8 上的同类问题;Mac 主文件据报告为 `draft_info.json`,Jianying-CapCut2XML#4)。目前没有 fixture;修改类命令在没有 `--force-write` 时会拒绝执行。欢迎提供 fixture —— 见下方「写入时版本护栏」一节。 |
Expand Down
9 changes: 6 additions & 3 deletions src/command-specs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -726,23 +726,26 @@ const optionsByCommand: Record<string, OptionSpec[]> = {
"boolean",
"Scan the finished bundle (SANITIZE_REPORT.json and README included) for residual home paths, emails, " +
"device ids and the account name, reporting file:line per finding and exiting non-zero on any. " +
"With only a bundle directory as the argument, re-checks an existing bundle without rebuilding.",
"New bundles are checked automatically. With only a bundle directory as the argument, " +
"re-checks an existing bundle without rebuilding.",
),
],
"sync-timelines": [
option(
"apply",
["--apply"],
"boolean",
"Rewrite only the drifted mirror files from draft_content.json (default: print the plan only).",
"Rewrite only the drifted mirrors from the canonical timeline (default: print the plan only). " +
"On the evidenced Windows 8.7.0 active layout, the selected nested document is canonical.",
),
option(
"nested",
["--nested"],
"boolean",
"Also reconcile the nested Timelines/<id>/ documents (draft_info.json, draft_content.json, template-2.tmp), " +
"each keeping its own GUID — the workaround verified on CapCut Mac 9.2.8 in issue #50, as an explicit opt-in. " +
"Timelines/project.json is never touched.",
"On Windows 8.7.0 active layouts only the selected timeline and root mirrors are reconciled, " +
"even with this flag. Timelines/project.json is never touched.",
),
],
"replace-media": [
Expand Down
Loading
Loading