@remixmate/cli 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +100 -83
- package/README.zh-CN.md +183 -0
- package/dist/handlers/gen-digital-human.d.ts +12 -0
- package/dist/handlers/gen-digital-human.js +157 -0
- package/dist/handlers/gen-image.js +5 -41
- package/dist/handlers/gen-video.d.ts +18 -0
- package/dist/handlers/gen-video.js +153 -0
- package/dist/handlers/gen-voice.js +2 -12
- package/dist/handlers/index.js +4 -2
- package/dist/handlers/shared.d.ts +26 -0
- package/dist/handlers/shared.js +66 -0
- package/dist/http.d.ts +4 -2
- package/dist/http.js +5 -3
- package/dist/manifest.json +27 -24
- package/dist/registry.d.ts +5 -16
- package/dist/registry.js +11 -17
- package/dist/skill-schema.d.ts +47 -0
- package/dist/skill-schema.js +49 -0
- package/package.json +5 -4
- package/skills/export-jianying/scripts/gen_jianying_draft.py +4 -4
- package/skills/gen-digital-human/SKILL.md +23 -46
- package/skills/gen-digital-human/skill.json +3 -2
- package/skills/gen-digital-human/version.json +1 -1
- package/skills/gen-image/SKILL.md +2 -2
- package/skills/gen-image/skill.json +2 -1
- package/skills/gen-image/version.json +1 -1
- package/skills/gen-script/SKILL.md +13 -13
- package/skills/gen-script/scripts/gen_script.py +11 -12
- package/skills/gen-video/SKILL.md +22 -33
- package/skills/gen-video/skill.json +3 -2
- package/skills/gen-video/version.json +1 -1
- package/skills/gen-voice/SKILL.md +1 -1
- package/skills/gen-voice/version.json +1 -1
- package/skills/prepare-video-assets/SKILL.md +2 -2
- package/skills/prepare-video-assets/skill.json +1 -1
- package/skills/render-video/scripts/render_video.py +8 -8
- package/skills/{template-bind → template-registry}/README.md +4 -4
- package/skills/{template-bind → template-registry}/SKILL.md +14 -12
- package/skills/template-registry/scripts/list_templates.py +129 -0
- package/skills/{template-bind → template-registry}/scripts/match_template.py +4 -3
- package/skills/{template-bind → template-registry}/scripts/registry_loader.py +1 -1
- package/skills/{template-bind → template-registry}/scripts/template_paths.py +7 -7
- package/skills/{template-bind → template-registry}/skill.json +6 -5
- package/skills/template-registry/version.json +7 -0
- package/skills/{template-bind → template-registry}/video_dsl/README.md +2 -2
- package/skills/{template-bind → template-registry}/video_dsl/runtime/__init__.py +1 -1
- package/skills/video-parser/SKILL.md +47 -160
- package/skills/video-parser/skill.json +3 -4
- package/skills/video-parser/version.json +1 -1
- package/skills/web-capture/SKILL.md +407 -116
- package/skills/web-capture/scripts/_media_screenshot/__init__.py +21 -0
- package/skills/web-capture/scripts/_media_screenshot/bootstrap.py +34 -0
- package/skills/web-capture/scripts/_media_screenshot/browser.py +86 -0
- package/skills/web-capture/scripts/_media_screenshot/cli_args.py +111 -0
- package/skills/web-capture/scripts/_media_screenshot/js/arrow.js +86 -0
- package/skills/web-capture/scripts/_media_screenshot/js/caption.js +27 -0
- package/skills/web-capture/scripts/_media_screenshot/js/clear_overlay.js +5 -0
- package/skills/web-capture/scripts/_media_screenshot/js/cursor_bootstrap.js +26 -0
- package/skills/web-capture/scripts/_media_screenshot/js/cursor_hide.js +6 -0
- package/skills/web-capture/scripts/_media_screenshot/js/cursor_move.js +12 -0
- package/skills/web-capture/scripts/_media_screenshot/js/cursor_ripple.js +25 -0
- package/skills/web-capture/scripts/_media_screenshot/js/element_zoom.js +72 -0
- package/skills/web-capture/scripts/_media_screenshot/js/element_zoom_restore.js +21 -0
- package/skills/web-capture/scripts/_media_screenshot/js/fade_out_overlays.js +10 -0
- package/skills/web-capture/scripts/_media_screenshot/js/focus.js +38 -0
- package/skills/web-capture/scripts/_media_screenshot/js/highlight.js +97 -0
- package/skills/web-capture/scripts/_media_screenshot/js/overlay_bootstrap.js +19 -0
- package/skills/web-capture/scripts/_media_screenshot/js/page_zoom_in.js +54 -0
- package/skills/web-capture/scripts/_media_screenshot/js/page_zoom_out.js +11 -0
- package/skills/web-capture/scripts/_media_screenshot/js/page_zoom_restore.js +13 -0
- package/skills/web-capture/scripts/_media_screenshot/js/redact.js +33 -0
- package/skills/web-capture/scripts/_media_screenshot/js/scroll.js +26 -0
- package/skills/web-capture/scripts/_media_screenshot/js/scroll_through.js +18 -0
- package/skills/web-capture/scripts/_media_screenshot/js/sequence.js +76 -0
- package/skills/web-capture/scripts/_media_screenshot/js/settled_check.js +15 -0
- package/skills/web-capture/scripts/_media_screenshot/js/title_card.js +71 -0
- package/skills/web-capture/scripts/_media_screenshot/js_loader.py +14 -0
- package/skills/web-capture/scripts/_media_screenshot/overlay.py +17 -0
- package/skills/web-capture/scripts/_media_screenshot/recording.py +100 -0
- package/skills/web-capture/scripts/_media_screenshot/scenes/__init__.py +39 -0
- package/skills/web-capture/scripts/_media_screenshot/scenes/arrow.py +50 -0
- package/skills/web-capture/scripts/_media_screenshot/scenes/caption.py +20 -0
- package/skills/web-capture/scripts/_media_screenshot/scenes/click.py +35 -0
- package/skills/web-capture/scripts/_media_screenshot/scenes/code_lines.py +54 -0
- package/skills/web-capture/scripts/_media_screenshot/scenes/focus.py +24 -0
- package/skills/web-capture/scripts/_media_screenshot/scenes/highlight.py +54 -0
- package/skills/web-capture/scripts/_media_screenshot/scenes/hover.py +28 -0
- package/skills/web-capture/scripts/_media_screenshot/scenes/redact.py +32 -0
- package/skills/web-capture/scripts/_media_screenshot/scenes/scroll.py +32 -0
- package/skills/web-capture/scripts/_media_screenshot/scenes/sequence.py +43 -0
- package/skills/web-capture/scripts/_media_screenshot/scenes/title_card.py +20 -0
- package/skills/web-capture/scripts/_media_screenshot/scenes/type.py +42 -0
- package/skills/web-capture/scripts/_media_screenshot/scenes/wait.py +9 -0
- package/skills/web-capture/scripts/_media_screenshot/scenes/zoom.py +72 -0
- package/skills/web-capture/scripts/_media_screenshot/screenshot.py +166 -0
- package/skills/web-capture/scripts/_media_screenshot/storyboard.py +88 -0
- package/skills/web-capture/scripts/_media_screenshot/template.py +119 -0
- package/skills/web-capture/scripts/_media_screenshot/trim.py +82 -0
- package/skills/web-capture/scripts/record.py +186 -0
- package/skills/web-capture/scripts/screenshot.py +94 -0
- package/skills/web-capture/skill.json +14 -14
- package/skills/web-capture/templates/github-code-tour.json +59 -0
- package/skills/web-capture/templates/github-readme-tour.json +77 -0
- package/skills/web-capture/templates/github-repo-intro.json +74 -0
- package/skills/web-capture/version.json +2 -2
- package/dist/handlers/template-bind.d.ts +0 -15
- package/dist/handlers/template-bind.js +0 -149
- package/skills/gen-digital-human/scripts/gen_digital_human_video.py +0 -574
- package/skills/gen-video/scripts/gen_video.py +0 -591
- package/skills/template-bind/version.json +0 -7
- package/skills/web-capture/scripts/web_capture.py +0 -461
- /package/skills/{template-bind → template-registry}/scripts/check_contracts.py +0 -0
- /package/skills/{template-bind → template-registry}/scripts/render_job_client.py +0 -0
- /package/skills/{template-bind → template-registry}/scripts/sync_registry.py +0 -0
- /package/skills/{template-bind → template-registry}/video_dsl/runtime/dsl_validator.py +0 -0
- /package/skills/{template-bind → template-registry}/video_dsl/runtime/prompt_enhancer.py +0 -0
- /package/skills/{template-bind → template-registry}/video_dsl/runtime/template_binder.py +0 -0
- /package/skills/{template-bind → template-registry}/video_dsl/runtime/timeline_compiler.py +0 -0
- /package/skills/{template-bind → template-registry}/video_dsl/schema/render-plan-v1alpha1.json +0 -0
- /package/skills/{template-bind → template-registry}/video_dsl/schema/template-binding-v1alpha1.json +0 -0
- /package/skills/{template-bind → template-registry}/video_dsl/schema/template-definition-v1alpha1.json +0 -0
- /package/skills/{template-bind → template-registry}/video_dsl/schema/video-dsl-v1alpha1.json +0 -0
package/README.md
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
# @remixmate/cli
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
English | [简体中文](./README.zh-CN.md)
|
|
4
|
+
|
|
5
|
+
> Formerly `ab-skill-cli` (bin `ab-skill`), now renamed to **`@remixmate/cli`** (bin **`remixmate`**). The old package is deprecated on npm — please migrate to the new one.
|
|
4
6
|
|
|
5
7
|
AI media generation skills for Claude Code / Codex.
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
11 skills covering the full short-video production pipeline: image / video / voice / digital-human asset generation, web page capture, script planning, template binding, Remotion rendering, Jianying (CapCut) draft export, and video deconstruction.
|
|
8
10
|
|
|
9
11
|
## Install
|
|
10
12
|
|
|
@@ -46,121 +48,136 @@ cp .env.example .env
|
|
|
46
48
|
source .env
|
|
47
49
|
```
|
|
48
50
|
|
|
49
|
-
Python skills (6 of
|
|
50
|
-
|
|
51
|
-
---
|
|
51
|
+
Python skills (6 of 11) require `python3 >= 3.10`. `web-capture` requires Playwright (auto-installs chromium on first run). `ffmpeg` is only needed for `video-parser`'s optional local tool (`deconstruct_video.py`); the default `video-parser` entry runs server-side via ab-render and needs no local ffmpeg.
|
|
52
52
|
|
|
53
|
-
##
|
|
53
|
+
## Skills
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
This project contains 11 AI media generation skills covering the full pipeline from asset generation, script orchestration, template binding, and video rendering to Jianying export.
|
|
56
56
|
|
|
57
|
-
###
|
|
57
|
+
### Skill layers
|
|
58
58
|
|
|
59
59
|
```
|
|
60
60
|
┌─────────────────────────────────────────────────────────────┐
|
|
61
|
-
│
|
|
62
|
-
│ gen-script
|
|
63
|
-
│ template-
|
|
64
|
-
│ prepare-video-assets DSL + Binding →
|
|
65
|
-
│ render-video RenderPlan → Remotion
|
|
66
|
-
│ export-jianying
|
|
61
|
+
│ Orchestration Skills │
|
|
62
|
+
│ gen-script Topic → Video DSL (script generation) │
|
|
63
|
+
│ template-registry DSL → TemplateBinding (template list) │
|
|
64
|
+
│ prepare-video-assets DSL + Binding → asset resolve (Phase1)│
|
|
65
|
+
│ render-video RenderPlan → Remotion render (Phase 3)│
|
|
66
|
+
│ export-jianying Assets → Jianying draft ZIP │
|
|
67
67
|
├─────────────────────────────────────────────────────────────┤
|
|
68
|
-
│
|
|
69
|
-
│ gen-image
|
|
70
|
-
│ gen-video
|
|
71
|
-
│ gen-voice
|
|
72
|
-
│ gen-digital-human
|
|
68
|
+
│ Atomic Skills │
|
|
69
|
+
│ gen-image Text/Image-to-image (Seedream / Gemini) │
|
|
70
|
+
│ gen-video Text-to-video (Seedance / Veo) │
|
|
71
|
+
│ gen-voice Text-to-speech (Minimax TTS) │
|
|
72
|
+
│ gen-digital-human Talking-head (Jimeng / Feiying) │
|
|
73
73
|
├─────────────────────────────────────────────────────────────┤
|
|
74
|
-
│
|
|
75
|
-
│ video-parser
|
|
74
|
+
│ Tool Skills │
|
|
75
|
+
│ video-parser Video deconstruction (audio / ASR / kf) │
|
|
76
|
+
│ web-capture Web page screenshot / scroll-record │
|
|
76
77
|
└─────────────────────────────────────────────────────────────┘
|
|
77
78
|
```
|
|
78
79
|
|
|
79
|
-
###
|
|
80
|
+
### Directory structure
|
|
80
81
|
|
|
81
82
|
```
|
|
82
83
|
├── docs/
|
|
83
|
-
│ └── video-production-architecture.md #
|
|
84
|
+
│ └── video-production-architecture.md # architecture docs
|
|
85
|
+
├── src/ # TypeScript CLI + http/builtin handlers
|
|
84
86
|
├── skills/
|
|
85
|
-
│ ├── gen-image/ #
|
|
86
|
-
│ ├── gen-video/ #
|
|
87
|
-
│ ├── gen-voice/ #
|
|
88
|
-
│ ├── gen-digital-human/ #
|
|
89
|
-
│ ├── gen-script/ #
|
|
90
|
-
│ ├── template-
|
|
91
|
-
│ ├── prepare-video-assets/ #
|
|
92
|
-
│ ├── render-video/ #
|
|
93
|
-
│ ├── export-jianying/ #
|
|
94
|
-
│
|
|
87
|
+
│ ├── gen-image/ # atomic: AI image generation (http handler)
|
|
88
|
+
│ ├── gen-video/ # atomic: AI video generation (http handler)
|
|
89
|
+
│ ├── gen-voice/ # atomic: text-to-speech (http handler)
|
|
90
|
+
│ ├── gen-digital-human/ # atomic: talking-head video (http handler)
|
|
91
|
+
│ ├── gen-script/ # orchestration: Topic → Video DSL
|
|
92
|
+
│ ├── template-registry/ # orchestration: template list (python list_templates.py)
|
|
93
|
+
│ ├── prepare-video-assets/ # orchestration: Phase 1 asset prep (thin wrapper)
|
|
94
|
+
│ ├── render-video/ # orchestration: Phase 3 Remotion render (canonical render_video.py)
|
|
95
|
+
│ ├── export-jianying/ # orchestration: export Jianying draft ZIP
|
|
96
|
+
│ ├── video-parser/ # tool: video deconstruction & analysis
|
|
97
|
+
│ └── web-capture/ # tool: headless-browser screenshot / recording
|
|
95
98
|
└── README.md
|
|
96
99
|
```
|
|
97
100
|
|
|
98
|
-
###
|
|
101
|
+
### Skill reference
|
|
99
102
|
|
|
100
|
-
|
|
|
101
|
-
|
|
102
|
-
| gen-image |
|
|
103
|
-
| gen-video |
|
|
104
|
-
| gen-voice |
|
|
105
|
-
| gen-digital-human |
|
|
106
|
-
| gen-script |
|
|
107
|
-
| template-
|
|
108
|
-
| prepare-video-assets |
|
|
109
|
-
| render-video |
|
|
110
|
-
| export-jianying |
|
|
111
|
-
| video-parser |
|
|
103
|
+
| Skill | Type | Description | Runtime / entry |
|
|
104
|
+
|-------|------|-------------|-----------------|
|
|
105
|
+
| gen-image | atomic | Text/Image-to-image (Seedream, Gemini) | http handler |
|
|
106
|
+
| gen-video | atomic | Text-to-video (Seedance, Veo) | http handler |
|
|
107
|
+
| gen-voice | atomic | Text-to-speech (Minimax TTS) | http handler |
|
|
108
|
+
| gen-digital-human | atomic | Talking-head (Jimeng / Feiying) | http handler |
|
|
109
|
+
| gen-script | orchestration | Topic → Video DSL JSON | python `scripts/gen_script.py` |
|
|
110
|
+
| template-registry | orchestration | Template list (binding logic embedded in prepare-video-assets) | python `scripts/list_templates.py` |
|
|
111
|
+
| prepare-video-assets | orchestration | DSL + Binding → asset resolve → persist RenderPlan | python `scripts/prepare_video_assets.py` (wraps `render_video.py --resolve-only`) |
|
|
112
|
+
| render-video | orchestration | job_id → Remotion render → upload | python `scripts/render_video.py` |
|
|
113
|
+
| export-jianying | orchestration | Asset URLs → Jianying draft ZIP (auto-converts from RenderPlan) | python `scripts/gen_jianying_draft.py` |
|
|
114
|
+
| video-parser | tool | Video → audio + ASR + keyframes + scene segmentation | python `scripts/parse_via_render.py` |
|
|
115
|
+
| web-capture | tool | Web page screenshot / scroll-record / storyboard video | python `scripts/screenshot.py`, `scripts/record.py` |
|
|
112
116
|
|
|
113
|
-
###
|
|
117
|
+
### Core workflows
|
|
114
118
|
|
|
115
|
-
|
|
119
|
+
See the [video production architecture doc](docs/video-production-architecture.md) and the [orchestration guide](docs/orchestration-guide.md).
|
|
116
120
|
|
|
117
|
-
|
|
121
|
+
**Workflow A: Topic → Remotion video**
|
|
118
122
|
```
|
|
119
|
-
gen-script →
|
|
120
|
-
→
|
|
123
|
+
gen-script → ✅ user confirms script → prepare-video-assets(--template-id)
|
|
124
|
+
→ ✅ user confirms assets → render-video(--job-id) → MP4
|
|
121
125
|
```
|
|
122
126
|
|
|
123
|
-
|
|
127
|
+
**Workflow B: Topic → Jianying draft**
|
|
124
128
|
```
|
|
125
|
-
gen-script →
|
|
126
|
-
→
|
|
129
|
+
gen-script → ✅ user confirms script → prepare-video-assets(--template-id)
|
|
130
|
+
→ ✅ user confirms assets → export-jianying(--from-job-id) → Jianying ZIP
|
|
127
131
|
```
|
|
128
132
|
|
|
129
|
-
###
|
|
133
|
+
### Testing
|
|
130
134
|
|
|
131
135
|
```bash
|
|
132
|
-
#
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
136
|
+
# List all skills (name, tool, entry type)
|
|
137
|
+
remixmate --list
|
|
138
|
+
|
|
139
|
+
# Offline registry smoke + spec guards
|
|
140
|
+
npm run smoke
|
|
141
|
+
|
|
142
|
+
# CLI unit tests (argv parser + skill schema)
|
|
143
|
+
npm run test:cli
|
|
144
|
+
|
|
145
|
+
# Python skills forward --help to the underlying script:
|
|
146
|
+
remixmate gen-script --help
|
|
147
|
+
remixmate prepare-video-assets --help
|
|
148
|
+
remixmate render-video --help
|
|
149
|
+
remixmate export-jianying --help
|
|
150
|
+
remixmate video-parser --help
|
|
151
|
+
remixmate web-capture --help
|
|
152
|
+
remixmate template-registry --help
|
|
153
|
+
|
|
154
|
+
# http skills (gen-image, gen-video, gen-voice, gen-digital-human) take no
|
|
155
|
+
# python --help; their flags live in each SKILL.md / skill.json.
|
|
156
|
+
|
|
157
|
+
# List available voices
|
|
158
|
+
remixmate gen-voice --list-voices
|
|
159
|
+
|
|
160
|
+
# List available templates
|
|
161
|
+
remixmate template-registry --list-templates
|
|
149
162
|
```
|
|
150
163
|
|
|
151
|
-
###
|
|
164
|
+
### Development
|
|
152
165
|
|
|
153
|
-
-
|
|
154
|
-
-
|
|
155
|
-
-
|
|
166
|
+
- Clone this project locally
|
|
167
|
+
- Develop inside each skill folder under `skills/`
|
|
168
|
+
- Each skill contains `SKILL.md` (skill docs), `version.json` (metadata), and `scripts/` (scripts)
|
|
156
169
|
|
|
157
|
-
###
|
|
170
|
+
### Quick start
|
|
158
171
|
|
|
159
172
|
```
|
|
160
|
-
@skills/gen-image/SKILL.md
|
|
161
|
-
@skills/gen-video/SKILL.md
|
|
162
|
-
@skills/gen-voice/SKILL.md
|
|
163
|
-
@skills/gen-digital-human/SKILL.md
|
|
164
|
-
@skills/template-
|
|
165
|
-
@skills/gen-script/SKILL.md
|
|
173
|
+
@skills/gen-image/SKILL.md Generate an image of a panda, 9:16, using gemini 3.1, Chinese-painting style + follow this doc strictly
|
|
174
|
+
@skills/gen-video/SKILL.md Generate a video of a panda running in a bamboo forest, 9:16, 6 seconds, using veo + follow this doc strictly
|
|
175
|
+
@skills/gen-voice/SKILL.md Generate a voiceover introducing panda habits, around 100 words + follow this doc strictly
|
|
176
|
+
@skills/gen-digital-human/SKILL.md Get the digital human list + follow this doc strictly
|
|
177
|
+
@skills/template-registry/SKILL.md Get the template list + follow this doc strictly
|
|
178
|
+
@skills/gen-script/SKILL.md Based on the image-slide template, create a video about AI learning methods + follow this doc strictly
|
|
166
179
|
```
|
|
180
|
+
|
|
181
|
+
## License
|
|
182
|
+
|
|
183
|
+
MIT
|
package/README.zh-CN.md
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# @remixmate/cli
|
|
2
|
+
|
|
3
|
+
[English](./README.md) | 简体中文
|
|
4
|
+
|
|
5
|
+
> 原名 `ab-skill-cli`(bin `ab-skill`),现已更名为 **`@remixmate/cli`**(bin **`remixmate`**)。旧包已在 npm 标记弃用,请迁移到新包。
|
|
6
|
+
|
|
7
|
+
面向 Claude Code / Codex 的 AI 媒体生成技能集。
|
|
8
|
+
|
|
9
|
+
包含 11 个技能,覆盖完整的短视频生产链路:图片 / 视频 / 语音 / 数字人素材生成、网页捕获、脚本编排、模板绑定、Remotion 渲染、剪映(CapCut)草稿导出,以及视频解构。
|
|
10
|
+
|
|
11
|
+
## 安装
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install -g @remixmate/cli
|
|
15
|
+
remixmate --help
|
|
16
|
+
remixmate --list
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## 在 Claude Code 中使用
|
|
20
|
+
|
|
21
|
+
把内置的 `skills/` 拷贝到项目级或全局的 `.claude/skills/` 目录,Claude Code 会自动识别:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
# 项目级
|
|
25
|
+
mkdir -p .claude/skills
|
|
26
|
+
cp -R "$(npm root -g)/@remixmate/cli/skills/." .claude/skills/
|
|
27
|
+
|
|
28
|
+
# 或全局
|
|
29
|
+
mkdir -p ~/.claude/skills
|
|
30
|
+
cp -R "$(npm root -g)/@remixmate/cli/skills/." ~/.claude/skills/
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## 在 Codex 中使用
|
|
34
|
+
|
|
35
|
+
Codex 不会原生加载 `SKILL.md`。在系统提示里告诉 Codex 这个 CLI,让它在需要媒体生成时调用 `remixmate <skill> ...`:
|
|
36
|
+
|
|
37
|
+
> 当你需要 AI 媒体生成(图片 / 视频 / 语音 / 数字人 / 剪映导出)时,先运行 `remixmate <skill-id> --help` 查看参数,再用 `remixmate <skill-id> --json-output ...` 执行。可用技能:`remixmate --list`。
|
|
38
|
+
|
|
39
|
+
完整介绍页(Claude Code / Codex 配置、环境变量、端到端工作流示例)见项目主页。
|
|
40
|
+
|
|
41
|
+
## 环境
|
|
42
|
+
|
|
43
|
+
多数技能通过 `PRIV_TOKEN` 向 ab-api 鉴权。没有有效 token(以及访问 ab-api 的网络)时,模板 / 媒体生成类技能不可用——代码是开源的,但生成能力托管在 ab-api 服务上。运行前把 `.env.example` 拷为 `.env` 并 source:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
cp .env.example .env
|
|
47
|
+
# 修改其中的值
|
|
48
|
+
source .env
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Python 技能(11 个中的 6 个)需要 `python3 >= 3.10`。`web-capture` 需要 Playwright(首次运行自动安装 chromium)。`ffmpeg` 仅 `video-parser` 的可选本地工具(`deconstruct_video.py`)需要;`video-parser` 默认入口走 ab-render 服务端解构,无需本地 ffmpeg。
|
|
52
|
+
|
|
53
|
+
## 技能
|
|
54
|
+
|
|
55
|
+
本项目包含 11 个 AI 媒体生成技能,覆盖从素材生成、脚本编排、模板绑定、视频渲染到剪映导出的完整视频内容生产链路。
|
|
56
|
+
|
|
57
|
+
### 技能分层
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
61
|
+
│ 编排层 Skills │
|
|
62
|
+
│ gen-script 主题 → Video DSL(脚本生成) │
|
|
63
|
+
│ template-registry DSL → TemplateBinding(模板列表) │
|
|
64
|
+
│ prepare-video-assets DSL + Binding → 素材补齐(Phase 1) │
|
|
65
|
+
│ render-video RenderPlan → Remotion 渲染(Phase 3) │
|
|
66
|
+
│ export-jianying 素材 → 剪映草稿 ZIP │
|
|
67
|
+
├─────────────────────────────────────────────────────────────┤
|
|
68
|
+
│ 原子层 Skills │
|
|
69
|
+
│ gen-image 文生图 / 图生图(Seedream / Gemini) │
|
|
70
|
+
│ gen-video 文生视频(Seedance / Veo) │
|
|
71
|
+
│ gen-voice 语音合成(Minimax TTS) │
|
|
72
|
+
│ gen-digital-human 数字人口播(即梦 / 飞影) │
|
|
73
|
+
├─────────────────────────────────────────────────────────────┤
|
|
74
|
+
│ 工具层 Skills │
|
|
75
|
+
│ video-parser 视频解构(音频提取 / ASR / 关键帧) │
|
|
76
|
+
│ web-capture 网页截图 / 滚动录屏 / 分镜视频 │
|
|
77
|
+
└─────────────────────────────────────────────────────────────┘
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### 目录结构
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
├── docs/
|
|
84
|
+
│ └── video-production-architecture.md # 架构文档
|
|
85
|
+
├── src/ # TypeScript CLI + http/builtin handlers
|
|
86
|
+
├── skills/
|
|
87
|
+
│ ├── gen-image/ # 原子: AI 生图(http handler)
|
|
88
|
+
│ ├── gen-video/ # 原子: AI 生视频(http handler)
|
|
89
|
+
│ ├── gen-voice/ # 原子: 语音合成(http handler)
|
|
90
|
+
│ ├── gen-digital-human/ # 原子: 数字人口播(http handler)
|
|
91
|
+
│ ├── gen-script/ # 编排: 主题 → Video DSL
|
|
92
|
+
│ ├── template-registry/ # 编排: 模板列表(python list_templates.py)
|
|
93
|
+
│ ├── prepare-video-assets/ # 编排: Phase 1 素材准备(thin wrapper)
|
|
94
|
+
│ ├── render-video/ # 编排: Phase 3 Remotion 渲染(含 render_video.py 实现)
|
|
95
|
+
│ ├── export-jianying/ # 编排: 导出剪映草稿 ZIP
|
|
96
|
+
│ ├── video-parser/ # 工具: 视频解构与分析
|
|
97
|
+
│ └── web-capture/ # 工具: 无头浏览器截图 / 录屏
|
|
98
|
+
└── README.md
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### 技能说明
|
|
102
|
+
|
|
103
|
+
| 技能 | 类型 | 说明 | 运行方式 / entry |
|
|
104
|
+
|------|------|------|------------------|
|
|
105
|
+
| gen-image | 原子 | 文生图 / 图生图(Seedream、Gemini) | http handler |
|
|
106
|
+
| gen-video | 原子 | 文生视频(Seedance、Veo) | http handler |
|
|
107
|
+
| gen-voice | 原子 | 语音合成(Minimax TTS) | http handler |
|
|
108
|
+
| gen-digital-human | 原子 | 数字人口播(即梦 / 飞影) | http handler |
|
|
109
|
+
| gen-script | 编排 | 主题 → Video DSL JSON | python `scripts/gen_script.py` |
|
|
110
|
+
| template-registry | 编排 | 模板列表(绑定逻辑内嵌于 prepare-video-assets) | python `scripts/list_templates.py` |
|
|
111
|
+
| prepare-video-assets | 编排 | DSL + Binding → 素材补齐 → 落库 RenderPlan | python `scripts/prepare_video_assets.py`(包装 `render_video.py --resolve-only`) |
|
|
112
|
+
| render-video | 编排 | job_id → Remotion 渲染 → 上传 | python `scripts/render_video.py` |
|
|
113
|
+
| export-jianying | 编排 | 素材 URL → 剪映草稿 ZIP(支持从 RenderPlan 自动转换) | python `scripts/gen_jianying_draft.py` |
|
|
114
|
+
| video-parser | 工具 | 视频 → 音频 + ASR + 关键帧 + 场景分段 | python `scripts/parse_via_render.py` |
|
|
115
|
+
| web-capture | 工具 | 网页截图 / 滚动录屏 / 分镜视频 | python `scripts/screenshot.py`、`scripts/record.py` |
|
|
116
|
+
|
|
117
|
+
### 核心链路
|
|
118
|
+
|
|
119
|
+
详见 [视频内容生产架构文档](docs/video-production-architecture.md) 和 [编排流程指南](docs/orchestration-guide.md)。
|
|
120
|
+
|
|
121
|
+
**链路 A:主题 → Remotion 视频**
|
|
122
|
+
```
|
|
123
|
+
gen-script → ✅用户确认脚本 → prepare-video-assets(--template-id)
|
|
124
|
+
→ ✅用户确认素材 → render-video(--job-id) → MP4
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
**链路 B:主题 → 剪映草稿**
|
|
128
|
+
```
|
|
129
|
+
gen-script → ✅用户确认脚本 → prepare-video-assets(--template-id)
|
|
130
|
+
→ ✅用户确认素材 → export-jianying(--from-job-id) → 剪映 ZIP
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### 测试
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
# 列出所有技能(名称、tool、entry 类型)
|
|
137
|
+
remixmate --list
|
|
138
|
+
|
|
139
|
+
# 离线注册表 smoke + 规范守卫
|
|
140
|
+
npm run smoke
|
|
141
|
+
|
|
142
|
+
# CLI 单元测试(argv 解析器 + skill schema)
|
|
143
|
+
npm run test:cli
|
|
144
|
+
|
|
145
|
+
# Python 技能会把 --help 透传给底层脚本:
|
|
146
|
+
remixmate gen-script --help
|
|
147
|
+
remixmate prepare-video-assets --help
|
|
148
|
+
remixmate render-video --help
|
|
149
|
+
remixmate export-jianying --help
|
|
150
|
+
remixmate video-parser --help
|
|
151
|
+
remixmate web-capture --help
|
|
152
|
+
remixmate template-registry --help
|
|
153
|
+
|
|
154
|
+
# http 技能(gen-image、gen-video、gen-voice、gen-digital-human)没有
|
|
155
|
+
# python --help;参数见各自的 SKILL.md / skill.json。
|
|
156
|
+
|
|
157
|
+
# 查看可用音色
|
|
158
|
+
remixmate gen-voice --list-voices
|
|
159
|
+
|
|
160
|
+
# 查看可用模板
|
|
161
|
+
remixmate template-registry --list-templates
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### 开发流程
|
|
165
|
+
|
|
166
|
+
- 克隆此项目到本地
|
|
167
|
+
- 在 skills 目录下各技能文件夹中开发
|
|
168
|
+
- 每个技能包含 `SKILL.md`(技能说明)、`version.json`(元数据)和 `scripts/`(脚本)
|
|
169
|
+
|
|
170
|
+
### 快速体验
|
|
171
|
+
|
|
172
|
+
```
|
|
173
|
+
@skills/gen-image/SKILL.md 生成一张熊猫的图片,9:16,调用gemini 3.1,国画风 + 严格按该文档执行
|
|
174
|
+
@skills/gen-video/SKILL.md 生成一段熊猫在竹林奔跑的视频,9:16,长度6秒,调用veo + 严格按该文档执行
|
|
175
|
+
@skills/gen-voice/SKILL.md 生成一段语音,介绍熊猫的习性,大概100字左右 + 严格按该文档执行
|
|
176
|
+
@skills/gen-digital-human/SKILL.md 获取数字人列表 + 严格按该文档执行
|
|
177
|
+
@skills/template-registry/SKILL.md 获取模版列表 + 严格按该文档执行
|
|
178
|
+
@skills/gen-script/SKILL.md 基于模版 image-slide,创作一个关于AI学习方法的视频 + 严格按该文档执行
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## 许可证
|
|
182
|
+
|
|
183
|
+
MIT
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* gen-digital-human handler — talking-head video, three modes:
|
|
3
|
+
* --list-avatars true → POST /digital-human/avatar/page (list)
|
|
4
|
+
* --check-status --generation-id <id> → POST /digital-human/video/status (one-shot)
|
|
5
|
+
* default (--avatar-id <id> ...) → generate: resolve avatar → submit → poll
|
|
6
|
+
*
|
|
7
|
+
* Ported from `scripts/gen_digital_human_video.py` (python → http) so it shares
|
|
8
|
+
* auth / base-URL / business-code handling with the other generators via
|
|
9
|
+
* http.ts. Endpoints and payloads match the backend DigitalHumanVideoGenerateDTO.
|
|
10
|
+
*/
|
|
11
|
+
import type { HandlerContext, HandlerInput } from './index.js';
|
|
12
|
+
export declare function genDigitalHuman(input: HandlerInput, ctxIn: HandlerContext): Promise<void>;
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* gen-digital-human handler — talking-head video, three modes:
|
|
3
|
+
* --list-avatars true → POST /digital-human/avatar/page (list)
|
|
4
|
+
* --check-status --generation-id <id> → POST /digital-human/video/status (one-shot)
|
|
5
|
+
* default (--avatar-id <id> ...) → generate: resolve avatar → submit → poll
|
|
6
|
+
*
|
|
7
|
+
* Ported from `scripts/gen_digital_human_video.py` (python → http) so it shares
|
|
8
|
+
* auth / base-URL / business-code handling with the other generators via
|
|
9
|
+
* http.ts. Endpoints and payloads match the backend DigitalHumanVideoGenerateDTO.
|
|
10
|
+
*/
|
|
11
|
+
import { mmPost, pollUntil, resolveHttpContext, SkillError } from '../http.js';
|
|
12
|
+
import { emitProgress } from '../progress.js';
|
|
13
|
+
import { isTrue, toNumber } from './shared.js';
|
|
14
|
+
const VALID_SOURCES = new Set(['jimeng', 'hifly']);
|
|
15
|
+
const DEFAULT_VOICE_ID = 'male-qn-qingse';
|
|
16
|
+
async function listAvatars(ctx, input) {
|
|
17
|
+
const payload = { current: 1, pageSize: 100 };
|
|
18
|
+
if (input.source)
|
|
19
|
+
payload.source = String(input.source);
|
|
20
|
+
if (input.gender)
|
|
21
|
+
payload.gender = String(input.gender);
|
|
22
|
+
if (isTrue(input.mine))
|
|
23
|
+
payload.mine = true;
|
|
24
|
+
if (input.name)
|
|
25
|
+
payload.name = String(input.name);
|
|
26
|
+
const data = await mmPost(ctx, '/digital-human/avatar/page', payload, { timeoutMs: 30_000 });
|
|
27
|
+
const avatars = data.list ?? [];
|
|
28
|
+
if (isTrue(input.json_output)) {
|
|
29
|
+
process.stdout.write(JSON.stringify({ avatars }) + '\n');
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
if (avatars.length === 0) {
|
|
33
|
+
process.stdout.write('⚠️ No avatars available\n');
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
process.stdout.write(`\n${'ID'.padEnd(8)} ${'Name'.padEnd(16)} ${'Source'.padEnd(10)} ${'Gender'.padEnd(8)} Ratio\n`);
|
|
37
|
+
process.stdout.write(`${'-'.repeat(8)} ${'-'.repeat(16)} ${'-'.repeat(10)} ${'-'.repeat(8)} ${'-'.repeat(10)}\n`);
|
|
38
|
+
for (const a of avatars) {
|
|
39
|
+
process.stdout.write(`${String(a.id ?? '').padEnd(8)} ${String(a.name ?? '').padEnd(16)} ${String(a.source ?? '').padEnd(10)} ${String(a.gender ?? '').padEnd(8)} ${a.aspectRatio ?? ''}\n`);
|
|
40
|
+
}
|
|
41
|
+
process.stdout.write(`\n${avatars.length} avatar(s) available\n`);
|
|
42
|
+
process.stdout.write('💡 Use --avatar-id <ID> to pick an avatar for generation\n');
|
|
43
|
+
}
|
|
44
|
+
/** Look up one avatar's metadata; checks the normal page then `mine: true`. */
|
|
45
|
+
async function fetchAvatarInfo(ctx, avatarId) {
|
|
46
|
+
for (const extra of [{}, { mine: true }]) {
|
|
47
|
+
const data = await mmPost(ctx, '/digital-human/avatar/page', { current: 1, pageSize: 100, ...extra }, { timeoutMs: 30_000 });
|
|
48
|
+
const found = (data.list ?? []).find((a) => String(a.id) === String(avatarId));
|
|
49
|
+
if (found)
|
|
50
|
+
return found;
|
|
51
|
+
}
|
|
52
|
+
throw new SkillError(`❌ avatar_id=${avatarId} not found; use --list-avatars [--mine] to see what is available`);
|
|
53
|
+
}
|
|
54
|
+
function resolveSource(explicit, avatar) {
|
|
55
|
+
const src = (explicit ?? '').trim() || (avatar.source ?? '').trim();
|
|
56
|
+
if (!VALID_SOURCES.has(src)) {
|
|
57
|
+
throw new SkillError('❌ Could not determine a valid source (jimeng/hifly). Pass --source, or confirm the avatar carries a source field.');
|
|
58
|
+
}
|
|
59
|
+
return src;
|
|
60
|
+
}
|
|
61
|
+
async function checkStatus(ctx, generationId, json) {
|
|
62
|
+
const data = await mmPost(ctx, '/digital-human/video/status', { generationId }, { timeoutMs: 30_000 });
|
|
63
|
+
const status = data.status ?? 'unknown';
|
|
64
|
+
if (json) {
|
|
65
|
+
process.stdout.write(JSON.stringify({ status, url: data.videoUrl ?? null, generationId }) + '\n');
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
process.stdout.write(`\ntask status: ${status}\n`);
|
|
69
|
+
if (data.videoUrl)
|
|
70
|
+
process.stdout.write(` video URL: ${data.videoUrl}\n`);
|
|
71
|
+
if (status === 'failed') {
|
|
72
|
+
const err = data.errorMessage ?? data.error ?? data.message ?? 'unknown error';
|
|
73
|
+
process.stdout.write(` failure reason: ${err}\n`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
async function pollVideoStatus(ctx, generationId) {
|
|
77
|
+
const url = await pollUntil(async () => {
|
|
78
|
+
const data = await mmPost(ctx, '/digital-human/video/status', { generationId }, { timeoutMs: 30_000 });
|
|
79
|
+
const status = data.status ?? 'unknown';
|
|
80
|
+
emitProgress({ phase: 'gen-digital-human:poll', generationId, status });
|
|
81
|
+
if (status === 'completed')
|
|
82
|
+
return data.videoUrl ?? '';
|
|
83
|
+
if (status === 'failed') {
|
|
84
|
+
const err = data.errorMessage ?? data.error ?? data.message ?? 'unknown error';
|
|
85
|
+
throw new SkillError(`❌ video generation failed: ${err}`);
|
|
86
|
+
}
|
|
87
|
+
return null;
|
|
88
|
+
}, { intervalMs: 5_000, timeoutMs: 600_000 });
|
|
89
|
+
return url;
|
|
90
|
+
}
|
|
91
|
+
export async function genDigitalHuman(input, ctxIn) {
|
|
92
|
+
const ctx = resolveHttpContext(ctxIn.skillName, {
|
|
93
|
+
apiBaseUrl: input.api_base_url,
|
|
94
|
+
privateToken: input.priv_token,
|
|
95
|
+
});
|
|
96
|
+
// ── Mode 1: list avatars ────────────────────────────────────────────────
|
|
97
|
+
if (isTrue(input.list_avatars)) {
|
|
98
|
+
return listAvatars(ctx, input);
|
|
99
|
+
}
|
|
100
|
+
// ── Mode 3: check status ────────────────────────────────────────────────
|
|
101
|
+
if (isTrue(input.check_status)) {
|
|
102
|
+
const generationId = toNumber(input.generation_id, 'generation_id');
|
|
103
|
+
if (generationId === undefined) {
|
|
104
|
+
throw new SkillError('❌ check-status mode requires --generation-id');
|
|
105
|
+
}
|
|
106
|
+
return checkStatus(ctx, generationId, isTrue(input.json_output));
|
|
107
|
+
}
|
|
108
|
+
// ── Mode 2: generate (default) ──────────────────────────────────────────
|
|
109
|
+
const avatarId = toNumber(input.avatar_id, 'avatar_id');
|
|
110
|
+
if (avatarId === undefined) {
|
|
111
|
+
throw new SkillError('❌ provide --avatar-id to pick the avatar (use --list-avatars to see available avatars)');
|
|
112
|
+
}
|
|
113
|
+
const audioUrl = (input.audio_url ?? '').trim();
|
|
114
|
+
const text = input.text ?? '';
|
|
115
|
+
if (!audioUrl) {
|
|
116
|
+
if (!text.trim()) {
|
|
117
|
+
throw new SkillError('❌ in TTS mode provide --text, or use --audio-url for audio-driven mode');
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
emitProgress({ phase: 'gen-digital-human:resolve-avatar', avatarId });
|
|
121
|
+
const avatar = await fetchAvatarInfo(ctx, avatarId);
|
|
122
|
+
const source = resolveSource(input.source, avatar);
|
|
123
|
+
const payload = { avatarId, source };
|
|
124
|
+
if (audioUrl) {
|
|
125
|
+
payload.audioUrl = audioUrl;
|
|
126
|
+
if (text.trim())
|
|
127
|
+
payload.text = text.trim();
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
payload.text = text;
|
|
131
|
+
payload.voiceId = input.voice_id ?? DEFAULT_VOICE_ID;
|
|
132
|
+
if (input.voice_name)
|
|
133
|
+
payload.voiceName = String(input.voice_name);
|
|
134
|
+
}
|
|
135
|
+
const aspectRatio = input.aspect_ratio ?? avatar.aspectRatio;
|
|
136
|
+
if (aspectRatio)
|
|
137
|
+
payload.aspectRatio = aspectRatio;
|
|
138
|
+
if (input.prompt)
|
|
139
|
+
payload.prompt = String(input.prompt);
|
|
140
|
+
emitProgress({ phase: 'gen-digital-human:request', avatarId, source, mode: audioUrl ? 'audio' : 'tts' });
|
|
141
|
+
const data = await mmPost(ctx, '/digital-human/video/generate', payload, { timeoutMs: 60_000 });
|
|
142
|
+
const generationId = data.generationId;
|
|
143
|
+
if (!generationId) {
|
|
144
|
+
throw new SkillError(`❌ could not parse generationId; API returned: ${JSON.stringify(data)}`);
|
|
145
|
+
}
|
|
146
|
+
emitProgress({ phase: 'gen-digital-human:async', generationId });
|
|
147
|
+
const videoUrl = await pollVideoStatus(ctx, generationId);
|
|
148
|
+
if (!videoUrl) {
|
|
149
|
+
throw new SkillError(`❌ video generated but no URL was returned; rerun --check-status --generation-id ${generationId} later`);
|
|
150
|
+
}
|
|
151
|
+
if (isTrue(input.json_output)) {
|
|
152
|
+
process.stdout.write(JSON.stringify({ url: videoUrl, generationId }) + '\n');
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
process.stdout.write(`\n🔗 Video URL (renders directly, no download needed):\n${videoUrl}\n`);
|
|
156
|
+
process.stdout.write('\n🎉 Digital-human video generation done!\n');
|
|
157
|
+
}
|