@remixmate/cli 0.1.0

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.
Files changed (91) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +166 -0
  3. package/dist/argv.d.ts +15 -0
  4. package/dist/argv.js +51 -0
  5. package/dist/cli.d.ts +13 -0
  6. package/dist/cli.js +83 -0
  7. package/dist/handlers/gen-image.d.ts +13 -0
  8. package/dist/handlers/gen-image.js +161 -0
  9. package/dist/handlers/gen-voice.d.ts +16 -0
  10. package/dist/handlers/gen-voice.js +110 -0
  11. package/dist/handlers/index.d.ts +13 -0
  12. package/dist/handlers/index.js +14 -0
  13. package/dist/handlers/template-bind.d.ts +15 -0
  14. package/dist/handlers/template-bind.js +149 -0
  15. package/dist/http.d.ts +52 -0
  16. package/dist/http.js +110 -0
  17. package/dist/index.d.ts +9 -0
  18. package/dist/index.js +8 -0
  19. package/dist/manifest.json +260 -0
  20. package/dist/progress.d.ts +23 -0
  21. package/dist/progress.js +15 -0
  22. package/dist/registry.d.ts +33 -0
  23. package/dist/registry.js +72 -0
  24. package/dist/runner.d.ts +17 -0
  25. package/dist/runner.js +63 -0
  26. package/package.json +54 -0
  27. package/skills/export-jianying/SKILL.md +251 -0
  28. package/skills/export-jianying/scripts/gen_jianying_draft.py +1099 -0
  29. package/skills/export-jianying/skill.json +24 -0
  30. package/skills/export-jianying/version.json +7 -0
  31. package/skills/gen-digital-human/SKILL.md +211 -0
  32. package/skills/gen-digital-human/scripts/gen_digital_human_video.py +574 -0
  33. package/skills/gen-digital-human/skill.json +23 -0
  34. package/skills/gen-digital-human/version.json +7 -0
  35. package/skills/gen-image/SKILL.md +130 -0
  36. package/skills/gen-image/skill.json +22 -0
  37. package/skills/gen-image/version.json +7 -0
  38. package/skills/gen-script/SKILL.md +283 -0
  39. package/skills/gen-script/scripts/gen_script.py +1208 -0
  40. package/skills/gen-script/skill.json +41 -0
  41. package/skills/gen-script/version.json +7 -0
  42. package/skills/gen-video/SKILL.md +170 -0
  43. package/skills/gen-video/scripts/gen_video.py +591 -0
  44. package/skills/gen-video/skill.json +24 -0
  45. package/skills/gen-video/version.json +7 -0
  46. package/skills/gen-voice/SKILL.md +104 -0
  47. package/skills/gen-voice/skill.json +21 -0
  48. package/skills/gen-voice/version.json +7 -0
  49. package/skills/prepare-video-assets/SKILL.md +192 -0
  50. package/skills/prepare-video-assets/scripts/prepare_video_assets.py +57 -0
  51. package/skills/prepare-video-assets/skill.json +23 -0
  52. package/skills/prepare-video-assets/version.json +7 -0
  53. package/skills/render-video/SKILL.md +187 -0
  54. package/skills/render-video/scripts/_chrome_vendor.py +305 -0
  55. package/skills/render-video/scripts/_video_probe.py +174 -0
  56. package/skills/render-video/scripts/_vod_polling.py +86 -0
  57. package/skills/render-video/scripts/remote_renderer_client.py +236 -0
  58. package/skills/render-video/scripts/render_video.py +2288 -0
  59. package/skills/render-video/scripts/upload_video.py +193 -0
  60. package/skills/render-video/skill.json +21 -0
  61. package/skills/render-video/version.json +7 -0
  62. package/skills/template-bind/README.md +63 -0
  63. package/skills/template-bind/SKILL.md +234 -0
  64. package/skills/template-bind/scripts/check_contracts.py +196 -0
  65. package/skills/template-bind/scripts/match_template.py +215 -0
  66. package/skills/template-bind/scripts/registry_loader.py +379 -0
  67. package/skills/template-bind/scripts/render_job_client.py +162 -0
  68. package/skills/template-bind/scripts/sync_registry.py +44 -0
  69. package/skills/template-bind/scripts/template_paths.py +72 -0
  70. package/skills/template-bind/skill.json +20 -0
  71. package/skills/template-bind/version.json +7 -0
  72. package/skills/template-bind/video_dsl/README.md +97 -0
  73. package/skills/template-bind/video_dsl/runtime/__init__.py +11 -0
  74. package/skills/template-bind/video_dsl/runtime/dsl_validator.py +495 -0
  75. package/skills/template-bind/video_dsl/runtime/prompt_enhancer.py +274 -0
  76. package/skills/template-bind/video_dsl/runtime/template_binder.py +87 -0
  77. package/skills/template-bind/video_dsl/runtime/timeline_compiler.py +312 -0
  78. package/skills/template-bind/video_dsl/schema/render-plan-v1alpha1.json +193 -0
  79. package/skills/template-bind/video_dsl/schema/template-binding-v1alpha1.json +122 -0
  80. package/skills/template-bind/video_dsl/schema/template-definition-v1alpha1.json +247 -0
  81. package/skills/template-bind/video_dsl/schema/video-dsl-v1alpha1.json +378 -0
  82. package/skills/video-parser/SKILL.md +238 -0
  83. package/skills/video-parser/scripts/analyze_video.py +1038 -0
  84. package/skills/video-parser/scripts/deconstruct_video.py +602 -0
  85. package/skills/video-parser/scripts/parse_via_render.py +148 -0
  86. package/skills/video-parser/skill.json +21 -0
  87. package/skills/video-parser/version.json +7 -0
  88. package/skills/web-capture/SKILL.md +183 -0
  89. package/skills/web-capture/scripts/web_capture.py +461 -0
  90. package/skills/web-capture/skill.json +29 -0
  91. package/skills/web-capture/version.json +7 -0
@@ -0,0 +1,378 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "video.dsl/v1alpha1",
4
+ "title": "Video DSL v1alpha1",
5
+ "description": "声明式视频描述语言,描述最终视频应该长什么样,而非如何调用每个技能。",
6
+ "type": "object",
7
+ "required": ["version", "meta", "global", "scenes"],
8
+ "properties": {
9
+ "version": {
10
+ "type": "string",
11
+ "const": "v1alpha1"
12
+ },
13
+ "meta": {
14
+ "$ref": "#/definitions/Meta"
15
+ },
16
+ "global": {
17
+ "$ref": "#/definitions/GlobalConfig"
18
+ },
19
+ "assets": {
20
+ "type": "array",
21
+ "items": { "$ref": "#/definitions/AssetRef" },
22
+ "description": "顶层素材声明池,可被 scene 级 assetBindings 引用"
23
+ },
24
+ "scenes": {
25
+ "type": "array",
26
+ "items": { "$ref": "#/definitions/Scene" },
27
+ "minItems": 1,
28
+ "maxItems": 20
29
+ },
30
+ "transitions": {
31
+ "$ref": "#/definitions/TransitionConfig"
32
+ },
33
+ "renderHints": {
34
+ "$ref": "#/definitions/RenderHints"
35
+ },
36
+ "outputs": {
37
+ "$ref": "#/definitions/OutputConfig"
38
+ }
39
+ },
40
+ "definitions": {
41
+ "Meta": {
42
+ "type": "object",
43
+ "required": ["title"],
44
+ "properties": {
45
+ "title": { "type": "string" },
46
+ "topic": { "type": "string" },
47
+ "platform": {
48
+ "type": "string",
49
+ "enum": ["douyin", "xiaohongshu", "bilibili", "wechat", "youtube", "generic"],
50
+ "default": "generic"
51
+ },
52
+ "targetDuration": {
53
+ "type": "number",
54
+ "description": "目标总时长(秒)"
55
+ },
56
+ "language": { "type": "string", "default": "zh-CN" },
57
+ "style": {
58
+ "type": "string",
59
+ "description": "整体风格标签,如 科技感、清新、商务、卡通"
60
+ },
61
+ "audience": { "type": "string" },
62
+ "description": { "type": "string" }
63
+ }
64
+ },
65
+ "GlobalConfig": {
66
+ "type": "object",
67
+ "properties": {
68
+ "aspectRatio": {
69
+ "type": "string",
70
+ "enum": ["16:9", "9:16", "1:1", "4:3", "3:4", "21:9"],
71
+ "default": "16:9"
72
+ },
73
+ "resolution": {
74
+ "type": "string",
75
+ "enum": ["480p", "720p", "1080p", "4k"],
76
+ "default": "1080p"
77
+ },
78
+ "fps": {
79
+ "type": "integer",
80
+ "enum": [24, 25, 30, 60],
81
+ "default": 30
82
+ },
83
+ "subtitle": {
84
+ "type": "object",
85
+ "properties": {
86
+ "enabled": { "type": "boolean", "default": true },
87
+ "style": { "type": "string", "enum": ["bottom", "center", "karaoke"], "default": "bottom" },
88
+ "fontSize": { "type": "integer" },
89
+ "fontFamily": { "type": "string" },
90
+ "color": { "type": "string" }
91
+ }
92
+ },
93
+ "narration": {
94
+ "type": "object",
95
+ "properties": {
96
+ "voiceId": { "type": "string", "description": "Minimax 音色 ID" },
97
+ "voiceName": { "type": "string" },
98
+ "speed": { "type": "number", "default": 1.0 }
99
+ }
100
+ },
101
+ "bgm": {
102
+ "type": "object",
103
+ "properties": {
104
+ "enabled": { "type": "boolean", "default": false },
105
+ "url": { "type": "string" },
106
+ "volume": { "type": "number", "default": 0.15, "minimum": 0, "maximum": 1 }
107
+ }
108
+ }
109
+ }
110
+ },
111
+ "AssetRef": {
112
+ "type": "object",
113
+ "required": ["assetId", "type"],
114
+ "properties": {
115
+ "assetId": {
116
+ "type": "string",
117
+ "description": "全局唯一素材 ID,scene 可通过此 ID 引用"
118
+ },
119
+ "type": {
120
+ "type": "string",
121
+ "enum": ["image", "video", "audio", "avatar", "subtitle", "bgm"]
122
+ },
123
+ "source": {
124
+ "type": "string",
125
+ "enum": ["existing", "gen-image", "gen-video", "gen-voice", "gen-digital-human"],
126
+ "description": "素材来源"
127
+ },
128
+ "status": {
129
+ "type": "string",
130
+ "enum": ["missing", "planned", "generating", "generated", "approved", "failed"],
131
+ "default": "planned"
132
+ },
133
+ "url": {
134
+ "type": "string",
135
+ "description": "已生成/已有素材的 URL"
136
+ },
137
+ "localPath": {
138
+ "type": "string",
139
+ "description": "本地缓存路径"
140
+ },
141
+ "payload": {
142
+ "type": "object",
143
+ "description": "素材生成参数,根据 source 不同内容不同",
144
+ "properties": {
145
+ "prompt": { "type": "string" },
146
+ "negativePrompt": {
147
+ "type": "string",
148
+ "description": "负向提示词,描述不希望出现的元素"
149
+ },
150
+ "guidanceScale": {
151
+ "type": "number",
152
+ "description": "引导系数,控制生成图片对 prompt 的遵循程度"
153
+ },
154
+ "model": { "type": "string" },
155
+ "text": { "type": "string" },
156
+ "voiceId": { "type": "string" },
157
+ "avatarId": { "type": ["string", "integer"] },
158
+ "duration": { "type": "number" },
159
+ "ratio": { "type": "string" },
160
+ "resolution": { "type": "string" },
161
+ "style": { "type": "string" }
162
+ },
163
+ "additionalProperties": true
164
+ },
165
+ "metadata": {
166
+ "type": "object",
167
+ "description": "生成后的元数据",
168
+ "properties": {
169
+ "duration": { "type": "number" },
170
+ "width": { "type": "integer" },
171
+ "height": { "type": "integer" },
172
+ "fileSize": { "type": "integer" },
173
+ "mimeType": { "type": "string" }
174
+ },
175
+ "additionalProperties": true
176
+ }
177
+ }
178
+ },
179
+ "Scene": {
180
+ "type": "object",
181
+ "required": ["id", "purpose"],
182
+ "properties": {
183
+ "id": { "type": "string" },
184
+ "purpose": {
185
+ "type": "string",
186
+ "enum": ["opening", "intro", "point", "example", "explanation", "transition", "highlight", "cta", "ending"],
187
+ "description": "场景在叙事中的角色"
188
+ },
189
+ "duration": {
190
+ "type": "number",
191
+ "description": "该场景时长(秒),可省略由模板决定"
192
+ },
193
+ "layout": {
194
+ "type": "string",
195
+ "enum": ["full-visual", "split-left-right", "split-top-bottom", "picture-in-picture", "text-overlay", "avatar-with-bg", "kenburns"],
196
+ "default": "full-visual"
197
+ },
198
+ "visuals": {
199
+ "type": "object",
200
+ "properties": {
201
+ "background": {
202
+ "type": "object",
203
+ "properties": {
204
+ "assetRef": { "type": "string", "description": "引用 assets 池中的 assetId" },
205
+ "color": { "type": "string" },
206
+ "gradient": { "type": "string" }
207
+ }
208
+ },
209
+ "foreground": {
210
+ "type": "object",
211
+ "properties": {
212
+ "assetRef": { "type": "string" },
213
+ "position": { "type": "string", "enum": ["center", "left", "right", "top", "bottom"] },
214
+ "scale": { "type": "number", "default": 1.0 }
215
+ }
216
+ },
217
+ "avatar": {
218
+ "type": "object",
219
+ "properties": {
220
+ "assetRef": { "type": "string" },
221
+ "position": { "type": "string", "enum": ["center", "left", "right", "bottom-left", "bottom-right"] },
222
+ "scale": { "type": "number", "default": 1.0 }
223
+ }
224
+ }
225
+ }
226
+ },
227
+ "audio": {
228
+ "type": "object",
229
+ "properties": {
230
+ "narration": {
231
+ "type": "object",
232
+ "description": "场景旁白。两种写法:(A) 传统:提供 text;(B) 多项卡片场景推荐:提供 intro/items/outro,每个 item 对应一张卡片,编译期会按 \\n 切行送 TTS 并自动生成 highlightMap,避免高亮与音频错位。",
233
+ "properties": {
234
+ "text": { "type": "string", "description": "该场景的完整旁白文案(传统写法)" },
235
+ "intro": { "type": "string", "description": "可选:开场铺垫句(此期间无卡片高亮)" },
236
+ "items": {
237
+ "type": "array",
238
+ "items": { "type": "string" },
239
+ "description": "每行一个卡片的叙述句。长度必须等于 templateData 里对应数组(concepts/pillars/eras/items)的长度"
240
+ },
241
+ "outro": { "type": "string", "description": "可选:收尾句(此期间无卡片高亮)" },
242
+ "assetRef": { "type": "string", "description": "旁白音频 assetId" }
243
+ }
244
+ },
245
+ "bgm": {
246
+ "type": "object",
247
+ "properties": {
248
+ "assetRef": { "type": "string" },
249
+ "volume": { "type": "number" }
250
+ }
251
+ },
252
+ "soundEffect": {
253
+ "type": "object",
254
+ "properties": {
255
+ "assetRef": { "type": "string" },
256
+ "trigger": { "type": "string", "enum": ["scene-start", "scene-end", "midpoint"] }
257
+ }
258
+ }
259
+ }
260
+ },
261
+ "textLayers": {
262
+ "type": "array",
263
+ "items": {
264
+ "type": "object",
265
+ "required": ["role", "content"],
266
+ "properties": {
267
+ "role": {
268
+ "type": "string",
269
+ "enum": ["headline", "subheadline", "caption", "badge", "callout"],
270
+ "description": "文本图层的语义角色:headline=画面主标题(大字),subheadline=画面副标题/项目名/标语(小字,与 headline 配对),caption=注释/角标,badge=徽标,callout=指示/标注。注意 subtitle(CC 字幕,逐句出字)由 global.subtitle 与 render-plan.subtitleSegments 单独承载,不在 textLayers.role 这里。"
271
+ },
272
+ "content": { "type": "string" },
273
+ "position": { "type": "string" },
274
+ "animation": { "type": "string", "enum": ["fade-in", "slide-up", "typewriter", "none"], "default": "fade-in" }
275
+ }
276
+ }
277
+ },
278
+ "animationHints": {
279
+ "type": "object",
280
+ "properties": {
281
+ "entrance": { "type": "string", "enum": ["fade", "slide-left", "slide-right", "slide-up", "zoom-in", "none"], "default": "fade" },
282
+ "exit": { "type": "string", "enum": ["fade", "slide-left", "slide-right", "slide-down", "zoom-out", "none"], "default": "fade" },
283
+ "motion": { "type": "string", "enum": ["static", "kenburns-in", "kenburns-out", "pan-left", "pan-right", "float"], "default": "static" }
284
+ }
285
+ },
286
+ "assetBindings": {
287
+ "type": "array",
288
+ "items": { "type": "string" },
289
+ "description": "该场景引用的 assetId 列表"
290
+ },
291
+ "templateData": {
292
+ "type": "object",
293
+ "additionalProperties": true,
294
+ "description": "模板专属数据,结构由所绑定模板的 slotMapping 定义。如 knowledge-slides 的 slides 等"
295
+ }
296
+ }
297
+ },
298
+ "TransitionConfig": {
299
+ "type": "object",
300
+ "properties": {
301
+ "default": {
302
+ "type": "string",
303
+ "enum": ["cut", "fade", "dissolve", "slide-left", "slide-right", "wipe", "zoom", "flip", "flip-corner"],
304
+ "default": "fade"
305
+ },
306
+ "duration": {
307
+ "type": "number",
308
+ "default": 0.5,
309
+ "description": "转场时长(秒)"
310
+ },
311
+ "overrides": {
312
+ "type": "array",
313
+ "items": {
314
+ "type": "object",
315
+ "properties": {
316
+ "from": { "type": "string" },
317
+ "to": { "type": "string" },
318
+ "type": { "type": "string" },
319
+ "duration": { "type": "number" }
320
+ }
321
+ }
322
+ }
323
+ }
324
+ },
325
+ "RenderHints": {
326
+ "type": "object",
327
+ "properties": {
328
+ "templatePreference": {
329
+ "type": "array",
330
+ "items": { "type": "string" },
331
+ "description": "偏好的模板 ID 列表"
332
+ },
333
+ "brandColors": {
334
+ "type": "array",
335
+ "items": { "type": "string" },
336
+ "description": "品牌色 HEX 值"
337
+ },
338
+ "pace": {
339
+ "type": "string",
340
+ "enum": ["slow", "normal", "fast"],
341
+ "default": "normal"
342
+ },
343
+ "shotDensity": {
344
+ "type": "string",
345
+ "enum": ["sparse", "normal", "dense"],
346
+ "default": "normal"
347
+ }
348
+ }
349
+ },
350
+ "OutputConfig": {
351
+ "type": "object",
352
+ "properties": {
353
+ "remotion": {
354
+ "type": "object",
355
+ "properties": {
356
+ "enabled": { "type": "boolean", "default": true },
357
+ "codec": { "type": "string", "enum": ["h264", "h265", "vp8", "vp9"], "default": "h264" },
358
+ "crf": { "type": "integer", "default": 18 }
359
+ }
360
+ },
361
+ "jianyingDraft": {
362
+ "type": "object",
363
+ "description": "剪映草稿导出(预留)",
364
+ "properties": {
365
+ "enabled": { "type": "boolean", "default": false }
366
+ }
367
+ },
368
+ "cloudEditingProject": {
369
+ "type": "object",
370
+ "description": "云剪辑工程导出(预留)",
371
+ "properties": {
372
+ "enabled": { "type": "boolean", "default": false }
373
+ }
374
+ }
375
+ }
376
+ }
377
+ }
378
+ }
@@ -0,0 +1,238 @@
1
+ ---
2
+ name: video-parser
3
+ description: |
4
+ Video deconstruction skill. Splits a video into reusable content assets — audio, ASR transcript, scene segments, keyframe images.
5
+
6
+ Use this skill as soon as the user mentions any of these intents:
7
+ - Deconstruct / split / analyze / parse a video
8
+ - Extract keyframes, extract the script from a video, transcribe a video
9
+ - Pull content assets or raw material out of a video
10
+
11
+ Even when the user does not say "deconstruct" explicitly, use this skill whenever they want to extract script text, keyframes, or structured information from a video.
12
+ triggers:
13
+ - Deconstruct / split / analyze / parse a video
14
+ - Extract keyframes, extract the script from a video, transcribe a video
15
+ - Pull content assets or raw material out of a video
16
+ ---
17
+
18
+ # Video Deconstruction Skill
19
+
20
+ Splits a video into reusable content assets — audio, ASR transcript (with per-word timestamps), scene cuts, keyframe images. Emits a structured asset-manifest JSON.
21
+
22
+ ## Dependencies
23
+
24
+ - **ffmpeg**: must be installed locally (`brew install ffmpeg`) — used for audio extraction, scene-change detection, and keyframe capture.
25
+ - **Python 3**: standard library only, no third-party dependencies.
26
+
27
+ ## Authentication & environment
28
+
29
+ There is no dedicated env file; the **process environment** variables of the caller apply. Examples use `python`; on macOS without that alias use `python3`.
30
+
31
+ - **Enterprise OpenClaw**: auth is injected, **no** `PRIV_TOKEN` / `--priv-token` needed.
32
+ - **Other environments**: configure a token — obtain a `PRIV_TOKEN` from your ab-api service. Non-interactive invocations may fail when the token is missing; interactive runs prompt for one.
33
+
34
+ | Env var | Description | Default |
35
+ |---------|-------------|---------|
36
+ | `PRIV_TOKEN` | Tianyan token; overridable with `--priv-token`. | (none) |
37
+ | `MM_API_BASE_URL` | API root URL. | `http://localhost:3001/api` |
38
+ | `AGENT_NAME` | Optional, sent as the `x-invoke-agent` request header. | (none) |
39
+
40
+ ## Steps
41
+
42
+ 1. **Script path**: read the system-injected `Base directory for this skill: <path>` as `<SkillDir>`. Every command below uses `<SkillDir>/scripts/deconstruct_video.py`; never hard-code paths.
43
+ 2. **Confirm ffmpeg**: run `ffmpeg -version` to confirm it is installed locally.
44
+ 3. **Pick the input**: either a direct video URL or a local file path (one of the two).
45
+ 4. **Run the deconstruction**: invoke the script and wait for it to finish.
46
+ 5. **Inspect the result**: check `deconstruction.json`, `audio.mp3`, and the `keyframes/` directory in the output folder.
47
+
48
+ ### Deconstruct from a URL
49
+
50
+ ```bash
51
+ python3 <SkillDir>/scripts/deconstruct_video.py \
52
+ --url "https://example.com/video.mp4" \
53
+ --output-dir ./output_deconstructed
54
+ ```
55
+
56
+ ### Deconstruct from a local file
57
+
58
+ ```bash
59
+ python3 <SkillDir>/scripts/deconstruct_video.py \
60
+ --local /path/to/video.mp4 \
61
+ --output-dir ./output_deconstructed
62
+ ```
63
+
64
+ ### Custom scene-change threshold
65
+
66
+ ```bash
67
+ python3 <SkillDir>/scripts/deconstruct_video.py \
68
+ --url "https://example.com/video.mp4" \
69
+ --scene-threshold 0.4
70
+ ```
71
+
72
+ Lower values are more sensitive (more cuts), higher values are looser (fewer cuts). Default is 0.3.
73
+
74
+ ### Audio + ASR only (skip keyframes)
75
+
76
+ ```bash
77
+ python3 <SkillDir>/scripts/deconstruct_video.py \
78
+ --url "https://example.com/video.mp4" \
79
+ --skip-keyframes
80
+ ```
81
+
82
+ ### Keyframes only (skip ASR)
83
+
84
+ ```bash
85
+ python3 <SkillDir>/scripts/deconstruct_video.py \
86
+ --url "https://example.com/video.mp4" \
87
+ --skip-asr
88
+ ```
89
+
90
+ ### JSON pipeline mode
91
+
92
+ ```bash
93
+ python3 <SkillDir>/scripts/deconstruct_video.py \
94
+ --url "https://example.com/video.mp4" \
95
+ --json-output
96
+ ```
97
+
98
+ Prints only the final `deconstruction.json` payload to stdout — no progress lines — suitable for piping.
99
+
100
+ See every option: `python3 <SkillDir>/scripts/deconstruct_video.py --help`.
101
+
102
+ ## Common CLI flags
103
+
104
+ | Flag | Description | Default |
105
+ |------|-------------|---------|
106
+ | `--url` | Remote video URL (mutually exclusive with `--local`). | — |
107
+ | `--local` | Local video path (mutually exclusive with `--url`). | — |
108
+ | `-o` / `--output-dir` | Output directory. | `./deconstructed_<timestamp>` |
109
+ | `--scene-threshold` | Scene-change threshold 0.0–1.0. | `0.3` |
110
+ | `--private-token` | Override `privateToken`. | env var |
111
+ | `--json-output` | Pipeline mode — JSON-only stdout. | off |
112
+ | `--skip-asr` | Skip the ASR step. | off |
113
+ | `--skip-keyframes` | Skip the keyframe-extraction step. | off |
114
+
115
+ ## Output layout
116
+
117
+ The script writes the following under `--output-dir`:
118
+
119
+ ```
120
+ <output-dir>/
121
+ ├── audio.mp3 # Extracted audio file
122
+ ├── deconstruction.json # Asset-manifest JSON
123
+ └── keyframes/ # Keyframe images
124
+ ├── frame_001.jpg
125
+ ├── frame_002.jpg
126
+ └── ...
127
+ ```
128
+
129
+ ### `deconstruction.json` shape
130
+
131
+ ```json
132
+ {
133
+ "source": {
134
+ "url": "original URL (if any)",
135
+ "localPath": "local video path",
136
+ "durationMs": 12345
137
+ },
138
+ "audio": {
139
+ "localPath": "output_dir/audio.mp3",
140
+ "url": "uploaded OSS URL"
141
+ },
142
+ "asr": {
143
+ "text": "full recognized transcript",
144
+ "duration": 3816,
145
+ "utterances": [
146
+ {
147
+ "text": "per-utterance text",
148
+ "startTime": 0,
149
+ "endTime": 1000,
150
+ "words": [...]
151
+ }
152
+ ]
153
+ },
154
+ "scenes": [
155
+ {
156
+ "index": 0,
157
+ "startTimeSec": 0.0,
158
+ "endTimeSec": 3.5,
159
+ "keyframe": "keyframes/frame_001.jpg"
160
+ }
161
+ ],
162
+ "keyframes": [
163
+ {
164
+ "index": 0,
165
+ "timestampSec": 0.0,
166
+ "localPath": "keyframes/frame_001.jpg"
167
+ }
168
+ ]
169
+ }
170
+ ```
171
+
172
+ ## Deconstruction → structural analysis
173
+
174
+ Reads the deconstruction artifacts and extracts hook, narrative structure, copy pacing, scene composition, etc., emitting a structured analysis report. Script path: `<SkillDir>/scripts/analyze_video.py`.
175
+
176
+ The analysis report can be used to:
177
+ - Feed an AI to write a new script with the same structure.
178
+ - Guide a human re-creator to replicate the video's rhythm.
179
+ - Inform downstream template development.
180
+
181
+ ### Basic usage
182
+
183
+ ```bash
184
+ python3 <SkillDir>/scripts/analyze_video.py \
185
+ -i ./output/deconstructed_xxx/deconstruction.json
186
+ ```
187
+
188
+ ### Specify the output directory
189
+
190
+ ```bash
191
+ python3 <SkillDir>/scripts/analyze_video.py \
192
+ -i ./output/deconstructed_xxx/ \
193
+ -o ./reports/
194
+ ```
195
+
196
+ ### Pipeline mode
197
+
198
+ ```bash
199
+ python3 <SkillDir>/scripts/analyze_video.py \
200
+ -i ./output/deconstructed_xxx/ \
201
+ --json-output
202
+ ```
203
+
204
+ Prints only the `analysis.json` payload to stdout, no progress text, suitable for piping.
205
+
206
+ ### analyze_video.py flags
207
+
208
+ | Flag | Description | Default |
209
+ |------|-------------|---------|
210
+ | `-i` / `--input` | Path to `deconstruction.json` or its parent directory. | required |
211
+ | `-o` / `--output-dir` | Output directory. | same as input |
212
+ | `--json-output` | Pipeline mode (JSON-only stdout). | off |
213
+
214
+ ### Output files
215
+
216
+ `analysis.json`: the structured analysis report. Covers the following dimensions:
217
+
218
+ | Field | Description |
219
+ |-------|-------------|
220
+ | `source` | Original video information (URL, duration). |
221
+ | `overview` | Language, scene count, copy length, asset types. |
222
+ | `hook` | Opening-hook text, duration, strategy classification. |
223
+ | `narrativeStructure` | Per-scene narrative structure (role, duration share, text density). |
224
+ | `pacing` | Pacing analysis (scene-duration distribution, rhythm type). |
225
+ | `textStructure` | Copy structure (speaking rate, silent gaps, keywords). |
226
+ | `audioPattern` | Audio pattern (speech vs. silence ratio). |
227
+ | `cta` | CTA detection (closing copy, whether an explicit CTA exists). |
228
+ | `assets` | Asset manifest (audio, keyframes). |
229
+
230
+ ## Error handling
231
+
232
+ - **ffmpeg not installed**: the script detects this and prompts `brew install ffmpeg`.
233
+ - **401 / token missing** (non-OpenClaw): check or set the token via the Feishu link above.
234
+ - **Business `code != 0`**: read the `msg` printed on stderr.
235
+ - **Video download failed**: confirm the URL is a direct video link (page links such as Douyin / Xiaohongshu are not supported).
236
+ - **ASR timeout**: large files can take a while; the script defaults to a 300-second timeout.
237
+ - **No scene change detected**: too-high threshold can yield zero cuts — try lowering `--scene-threshold`.
238
+ - **Network**: check connectivity and `MM_API_BASE_URL`.