yuanflow-cli 0.1.39 → 0.1.40

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 (24) hide show
  1. package/README.md +30 -4
  2. package/package.json +1 -1
  3. package/skills/yuanflow-skill/IP/350/277/220/350/220/245/SKILL.md +1 -0
  4. package/skills/yuanflow-skill/README.md +8 -4
  5. package/skills/yuanflow-skill/SKILL.md +9 -4
  6. package/skills/yuanflow-skill//345/205/254/344/274/227/345/217/267/347/224/237/346/210/220/344/270/216/345/217/221/345/270/203/SKILL.md +1 -1
  7. package/skills/yuanflow-skill//345/210/233/344/275/234/346/200/273/347/233/221/SKILL.md +94 -0
  8. package/skills/yuanflow-skill//345/260/217/347/272/242/344/271/246/350/277/220/350/220/245/{344/270/216/345/217/221/345/270/203/SKILL.md → SKILL.md} +10 -5
  9. package/skills/yuanflow-skill//345/270/220/345/217/267/345/256/232/344/275/215/SKILL.md +1 -0
  10. package/skills/yuanflow-skill//346/226/207/346/241/210/345/210/233/344/275/234/SKILL.md +1 -0
  11. package/skills/yuanflow-skill//347/203/255/351/227/250/345/206/205/345/256/271/346/225/264/347/220/206/SKILL.md +83 -0
  12. package/skills/yuanflow-skill//347/233/264/346/222/255/346/212/225/346/265/201/347/255/226/347/225/245/SKILL.md +1 -0
  13. package/skills/yuanflow-skill//347/233/264/346/222/255/350/257/235/346/234/257/347/224/237/346/210/220/SKILL.md +1 -0
  14. package/skills/yuanflow-skill//350/207/252/345/252/222/344/275/223/347/237/245/350/257/206/345/272/223/SKILL.md +2 -0
  15. package/skills/yuanflow-skill//350/247/206/351/242/221/346/212/225/346/265/201/347/255/226/347/225/245/SKILL.md +1 -0
  16. package/skills/yuanflow-skill//350/247/206/351/242/221/346/231/272/350/203/275/345/211/252/350/276/221/SKILL.md +60 -12
  17. package/skills/yuanflow-skill//351/200/211/351/242/230/347/255/226/345/210/222/SKILL.md +1 -0
  18. package/src/agent-protocol.js +3 -0
  19. package/src/cli.js +23 -1
  20. package/src/trending-tools.js +117 -0
  21. package/src/video-tools.js +182 -3
  22. /package/skills/yuanflow-skill//345/260/217/347/272/242/344/271/246/350/277/220/350/220/245/{344/270/216/345/217/221/345/270/203/references → references}/commands.md" +0 -0
  23. /package/skills/yuanflow-skill//345/260/217/347/272/242/344/271/246/350/277/220/350/220/245/{344/270/216/345/217/221/345/270/203/references → references}/interaction-policy.md" +0 -0
  24. /package/skills/yuanflow-skill//345/260/217/347/272/242/344/271/246/350/277/220/350/220/245/{344/270/216/345/217/221/345/270/203/references → references}/publishing-policy.md" +0 -0
package/README.md CHANGED
@@ -36,6 +36,7 @@ yuanflow-cli works detail --platform douyin --target "https://v.douyin.com/xxx/"
36
36
  yuanflow-cli works download --platform douyin --target "https://v.douyin.com/xxx/" --region CN --format agent-json
37
37
  yuanflow-cli search content --platform xiaohongshu --keyword "美妆" --format agent-json
38
38
  yuanflow-cli search users --platform instagram --keyword "nasa" --format agent-json
39
+ yuanflow-cli trending video-hot-list --format agent-json
39
40
  yuanflow-cli douyin web get-sec-user-id --url "https://v.douyin.com/xxx/" --format agent-json
40
41
  yuanflow-cli douyin user-profile <sec_user_id> --format agent-json
41
42
  yuanflow-cli douyin user-posts <sec_user_id> --count 20 --cursor 0 --format agent-json
@@ -99,6 +100,26 @@ yuanflow-cli search users --platform instagram --keyword "nasa" --format agent-j
99
100
 
100
101
  Agent 不确定参数时先执行 `commands list`,再用 `schema works.douyin.detail` 或 `schema search.xiaohongshu.content` 查看参数。`--dry-run` 可预览请求映射,不发起真实接口请求,也不要求 token。
101
102
 
103
+ ### 热门内容整理
104
+
105
+ 热门内容整理统一走 `trending video-hot-list`,一次性查询抖音视频热榜五类榜单,并输出统一 JSON:
106
+
107
+ ```bash
108
+ yuanflow-cli trending video-hot-list --format agent-json
109
+ yuanflow-cli trending video-hot-list --page 1 --page-size 10 --date-window 2 --format agent-json
110
+ yuanflow-cli trending video-hot-list --tags-json "[{\"value\":\"顶级垂类标签id\",\"children\":[{\"value\":\"子级垂类标签id\"}]}]" --format agent-json
111
+ ```
112
+
113
+ 默认查询:
114
+
115
+ - `1001` 视频总榜
116
+ - `1002` 低粉爆款
117
+ - `1003` 高完播率
118
+ - `1004` 高涨粉率
119
+ - `1005` 高点赞率
120
+
121
+ Agent 使用该命令后,应结合用户个人创作信息、个人创作库历史记录、历史任务或对话记录,输出可执行的选题、内容结构和发布建议,而不是只罗列榜单。
122
+
102
123
  ### 帐号监控
103
124
 
104
125
  `帐号监控` 随 npm 包安装到 Skill bundle,用于把帐号搜索、主页资料、主页作品、历史快照和变化对比串成稳定流程。
@@ -133,26 +154,29 @@ yuanflow-cli knowledge rule-detail --rule-code some_rule_code --output-format sh
133
154
 
134
155
  Agent 应先查看 `knowledge docs`,再把用户需求整理成 `task_frame`,按接口返回的 `next_actions` 渐进查询。v2 接口不要求 `task_intent`;旧字段仅保留兼容。`packs`、`rules` 的 `data` 可能直接是数组,Agent 不应假设固定对象结构,应优先读取 `next_actions` 继续下钻。
135
156
 
157
+ 查询前必须先明确用户具体需求,再从知识库的一级能力开始,依次使用渐进式查询。不能跳级、跨越式查询。
158
+
136
159
  `knowledge entry`、`knowledge packs`、`knowledge evaluate` 会按规则库实际校验要求本地检查 `task_frame.domain` 和 `task_frame.content_goal`,CLI 调用时必须传 `--domain` 和 `--content-goal`,或通过 `--task-frame-file` 提供同名字段。`rules` 和 `rule-detail` 用于按已选包/规则继续下钻,仍必须分别传 `--pack-code` 或 `--rule-code`。
137
160
 
138
161
  ### 视频智能剪辑
139
162
 
140
- 视频剪辑链路先查自媒体知识库中的 `video_editing_strategy`,再进入 `video` 命令执行。第一版人工测试流程是:查询规则包、保存策略快照、扫描素材、导入文案、1 秒 1 帧抽帧、Agent 生成 `timeline_plan.agent.json`、CLI 校验并渲染。
163
+ 视频剪辑链路先查自媒体知识库中的 `video_editing_strategy`,再进入 `video` 命令执行。第一版人工测试流程是:查询规则包、保存策略快照、扫描素材、通过“音视频在线转文字”取得 ASR/时间戳结果、导入音频对齐、1 秒 1 帧抽帧、导入视觉理解结果、Agent 生成 `timeline_plan.agent.json`、CLI 校验并渲染。
141
164
 
142
165
  ```bash
143
166
  yuanflow-cli knowledge rules --pack-code video_edit_logic_layer_pack --domain 视频剪辑 --content-goal "口播视频剪辑,需要主音频匹配 B-roll 并生成 EDL" --output-format video_edit_plan --format agent-json --output "D:\规则\logic.json"
144
167
  yuanflow-cli video init --input "D:\素材" --primary-audio "D:\素材\口播.mp3" --broll "D:\素材\画面.mp4" --format agent-json
145
168
  yuanflow-cli video strategy --project "D:\素材\yuanflow-video-edit" --template-type talking_head --rules-file "D:\规则\logic.json,D:\规则\talking_head.json,D:\规则\cli.json" --format agent-json
146
169
  yuanflow-cli video inspect --project "D:\素材\yuanflow-video-edit" --format agent-json
147
- yuanflow-cli video transcribe --project "D:\素材\yuanflow-video-edit" --script-file "D:\素材\口播文案.txt" --format agent-json
170
+ yuanflow-cli video align --project "D:\素材\yuanflow-video-edit" --asr-file "D:\素材\asr.json" --format agent-json
148
171
  yuanflow-cli video timeline --project "D:\素材\yuanflow-video-edit" --fps 1 --format agent-json
172
+ yuanflow-cli video visual-review --project "D:\素材\yuanflow-video-edit" --review-file "D:\素材\yuanflow-video-edit\visual_review.agent.json" --format agent-json
149
173
  yuanflow-cli video plan --project "D:\素材\yuanflow-video-edit" --timeline-plan "D:\素材\yuanflow-video-edit\timeline_plan.agent.json" --format agent-json
150
174
  yuanflow-cli video render-preview --project "D:\素材\yuanflow-video-edit" --format agent-json
151
175
  yuanflow-cli video evaluate --project "D:\素材\yuanflow-video-edit" --file "D:\素材\yuanflow-video-edit\preview.mp4" --format agent-json
152
176
  yuanflow-cli video render-final --project "D:\素材\yuanflow-video-edit" --format agent-json
153
177
  ```
154
178
 
155
- `--template-type` 当前支持 `talking_head` 和 `short_video_sales`。知识库查询由 `knowledge` 命令负责,`video` 命令只导入规则结果并保存 `strategy_snapshot.json`,不直接远程查规则库。
179
+ `video align` 只导入已有 ASR 或 forced alignment 结果,不直接调用在线 ASR;在线 ASR 由“音视频在线转文字”Skill 执行。`video visual-review` 只导入 Agent/人工对抽帧图片的视觉理解,不替 Agent 做语义判断。`--template-type` 当前支持 `talking_head` 和 `short_video_sales`。知识库查询由 `knowledge` 命令负责,`video` 命令只导入规则结果并保存 `strategy_snapshot.json`,不直接远程查规则库。
156
180
 
157
181
  ### 业务创作类 Skill
158
182
 
@@ -163,6 +187,8 @@ yuanflow-cli video render-final --project "D:\素材\yuanflow-video-edit" --form
163
187
  - `帐号定位`:账号人设、内容边界、目标受众、价值主张和栏目方向。
164
188
  - `选题策划`:选题池、系列选题、热点选题和选题优先级排序。
165
189
  - `文案创作`:小红书、公众号、朋友圈、短视频口播、直播预告、海报和转化型文案。
190
+ - `热门内容整理`:一次性查询视频总榜、低粉爆款、高完播率、高涨粉率、高点赞率,并结合用户创作方向输出建议。
191
+ - `创作总监`:负责内容方向把控、创新判断、复查验收和跨技能创作统筹。
166
192
  - `视频投流策略`:短视频、图文视频或内容素材的投流目标、人群、预算、素材分层和测试节奏。
167
193
  - `直播投流策略`:直播预热、直播引流、直播成交和直播间投放的人群、预算、素材、节奏和复盘策略。
168
194
 
@@ -216,7 +242,7 @@ yuanflow-cli browser dry-run --platform xiaohongshu --task publish --account mai
216
242
  3. 使用 `yuanflow-cli oss temp-upload` 上传音频。
217
243
  4. ASR 请求必须优先使用 OSS 返回的 `data.signed_url` 作为 `metadata.audio_url`,不要优先使用裸域名 `data.url`。
218
244
  5. 调用 `POST https://open.yuanchuangai.com/v1/audio/transcriptions`,模型 `doubao-asr`,`response_format=json`。
219
- 6. 默认只返回响应里的 `text` 干净文本;用户要求时间戳时再检查响应结构,不编造时间戳。
245
+ 6. 默认只返回响应里的 `text` 干净文本;视频剪辑任务需要音频对齐时,要优先保留 ASR 响应中的 `segments` / `words` 时间戳结构,后续交给 `yuanflow-cli video align --asr-file` 导入。接口没有返回时间戳时,不编造时间戳,改用 forced alignment 或人工按文案切分时间。
220
246
 
221
247
  请求体示例:
222
248
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yuanflow-cli",
3
- "version": "0.1.39",
3
+ "version": "0.1.40",
4
4
  "description": "YuanFlow 自媒体 API CLI 与 Skill 安装器。",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -16,6 +16,7 @@ emoji: 🪪
16
16
  - 如果没有命中,说明“未发现可用历史参考记录”,继续下一步。
17
17
  2. 调用 `自媒体知识库`。
18
18
  - 先查 `knowledge docs` 或 `knowledge entry`,根据用户目标构造 `domain` 和 `content_goal`。
19
+ - 查询时,必须先明确用户具体需求,然后从知识库的一级能力开始,依次使用渐进式查询。不能跳级、跨越式查询。
19
20
  - 按返回的 `next_actions` 分层查看 `packs`、`rules` 或 `rule-detail`,直到拿到足够的方法摘要。
20
21
  - 不要自己拼 SQL,不要跳过知识库直接凭空输出。
21
22
  3. 结合用户要求、历史参考记录和知识库结果,完成 IP 运营方案。
@@ -10,8 +10,10 @@ YuanFlow Skill 是 `yuanflow-cli` 的 Agent Skill 仓库,用于把社媒平台
10
10
  - `OSS文件中转工具/`:OSS 临时上传、签名链接、对象复制 Skill,走 `yuanflow-cli oss`。
11
11
  - `生图技能/`:图片生成与编辑 Skill,优先走 YuanFlow 内置 `yuanflow_image_request`。
12
12
  - `公众号生成与发布/`:公众号文章创作、主题预览、微信内联 HTML 排版、正文图片上传和草稿箱推送 Skill。
13
- - `小红书运营与发布/`:小红书选题调研、笔记搜索、账号分析、评论采集、文案创作、图文/视频发布和互动操作 Skill
14
- - `视频智能剪辑/`:主音频 + B-roll 素材智能剪辑 Skill,先查自媒体知识库的视频剪辑策略,保存 `strategy_snapshot.json`,再按 1 秒 1 帧抽帧,由 Agent 生成 `timeline_plan` 和 EDL,交给 `yuanflow-cli video` 校验和渲染。
13
+ - `小红书运营/`:小红书选题调研、笔记搜索、账号分析、评论采集、文案创作、发布前检查和互动操作 Skill;不建议浏览器自动发布作品,除非用户明确无视平台识别和限流风险并强制要求。
14
+ - `热门内容整理/`:一次性查询视频总榜、低粉爆款、高完播率、高涨粉率、高点赞率,并结合用户创作方向输出建议。
15
+ - `创作总监/`:负责内容方向把控、创新判断、复查验收和跨技能创作统筹。
16
+ - `视频智能剪辑/`:主音频 + B-roll 素材智能剪辑 Skill,先查自媒体知识库的视频剪辑策略,保存 `strategy_snapshot.json`,再通过 ASR/forced alignment 导入 `audio_alignment.json`,按 1 秒 1 帧抽帧并回写视觉理解,由 Agent 生成 `timeline_plan` 和 EDL,交给 `yuanflow-cli video` 校验和渲染。
15
17
  - `HTML报告生成/`:单页 HTML 报告生成 Skill,内置 9 种米色留白报告模板。
16
18
  - `本地音视频转文字/`:本地 SenseVoice 音视频转文字 Skill,首次明确使用时按需下载模型。
17
19
  - `音视频在线转文字/`:通过 YuanFlow 在线 ASR 接口把音频或视频转成干净文本,视频会先抽取音频并通过 OSS 中转。
@@ -38,7 +40,7 @@ YuanFlow Skill 是 `yuanflow-cli` 的 Agent Skill 仓库,用于把社媒平台
38
40
  - 把自媒体分析、数据复盘、文案方案、账号监控、知识梳理和执行计划生成可直接打开的单页 HTML 报告。
39
41
  - 生成或改写公众号文章,进入主题选择预览后导出微信公众号编辑器可粘贴 HTML,或推送到公众号草稿箱。
40
42
  - 处理小红书单段或全流程任务,例如只写文案、只采集笔记、只分析账号、只采集评论、只做点赞收藏评论回复,或完整执行图文/视频发布流程;每轮开始前必须明确任务边界。
41
- - 在用户要求智能剪辑、自动剪辑、主音频匹配 B-roll、重排视频画面或生成剪辑预览时,使用视频智能剪辑 Skill;第一版先查规则库,导入策略快照,抽帧频率固定 1 秒 1 帧,由 Agent 看抽帧图生成 `timeline_plan` 和 EDL。
43
+ - 在用户要求智能剪辑、自动剪辑、主音频匹配 B-roll、重排视频画面或生成剪辑预览时,使用视频智能剪辑 Skill;第一版先查规则库,导入策略快照,用“音视频在线转文字”或 forced alignment 建立音频对齐,抽帧频率固定 1 秒 1 帧,再回写视觉理解结果,最后生成 `timeline_plan` 和 EDL。
42
44
  - 在用户明确要求本地转写时,把本地音频或视频转成文字;视频会先抽取音频,模型和缓存都保存在 Skill 自己目录下。
43
45
  - 在用户要求在线转写、云端转写或 doubao-asr 时,把本地音频或视频经 OSS 中转后提交在线 ASR,默认只返回干净文本。
44
46
  - 在用户要求自媒体平台浏览器自动化、账号登录态隔离、Cookie/profile 保存、平台页面采集或作品发布时,使用专用自媒体浏览器自动化 Skill;普通网页浏览和搜索仍使用通用浏览器能力。
@@ -113,7 +115,9 @@ yuanflow-skill list-skills
113
115
  │ ├─ themes/
114
116
  │ ├─ references/
115
117
  │ └─ tests/
116
- ├─ 小红书运营与发布
118
+ ├─ 小红书运营
119
+ ├─ 热门内容整理
120
+ ├─ 创作总监
117
121
  │ ├─ SKILL.md
118
122
  │ └─ references/
119
123
  ├─ 视频智能剪辑
@@ -22,7 +22,9 @@ description: 当用户需要处理自媒体平台接口工作流、平台数据
22
22
  - `OSS文件中转工具/`
23
23
  - `生图技能/`
24
24
  - `公众号生成与发布/`
25
- - `小红书运营与发布/`
25
+ - `小红书运营/`
26
+ - `热门内容整理/`
27
+ - `创作总监/`
26
28
  - `视频智能剪辑/`
27
29
  - `HTML报告生成/`
28
30
  - `本地音视频转文字/`
@@ -140,6 +142,7 @@ description: 当用户需要处理自媒体平台接口工作流、平台数据
140
142
  - 自媒体选题、开头钩子、脚本、文案、标题、改写、评分、剪辑建议、发布策略。
141
143
  - 用户明确要求查询 YuanFlow 自媒体知识库、方法包、规则方向。
142
144
  - 需要按照知识库 `next_actions` 渐进式查询方法论摘要。
145
+ - 查询前必须先明确用户具体需求,再从知识库的一级能力开始,依次使用渐进式查询。不能跳级、跨越式查询。
143
146
 
144
147
  子 Skill 名称:
145
148
 
@@ -156,6 +159,8 @@ description: 当用户需要处理自媒体平台接口工作流、平台数据
156
159
  | 账号人设、内容边界、目标受众、价值主张、栏目方向 | `帐号定位` |
157
160
  | 选题池、系列选题、热点选题、选题优先级排序 | `选题策划` |
158
161
  | 小红书、公众号、朋友圈、短视频口播、直播预告、海报或转化型文案 | `文案创作` |
162
+ | 热点内容、爆款案例、高完播、高涨粉、高点赞内容整理和创作建议 | `热门内容整理` |
163
+ | 内容方向把控、创新判断、复查验收、上线前质量门禁 | `创作总监` |
159
164
  | 主音频匹配 B-roll、自动剪辑、重排画面、生成剪辑预览或成片 | `视频智能剪辑` |
160
165
  | 短视频、图文视频或内容素材的投流目标、人群、预算、素材分层、测试节奏 | `视频投流策略` |
161
166
  | 直播间、直播预热、直播引流、直播成交的投放人群、预算、素材、节奏和复盘 | `直播投流策略` |
@@ -236,7 +241,7 @@ description: 当用户需要处理自媒体平台接口工作流、平台数据
236
241
 
237
242
  - `公众号生成与发布`
238
243
 
239
- ### 15. 走 `小红书运营与发布`
244
+ ### 15. 走 `小红书运营`
240
245
 
241
246
  遇到下面这些需求,优先进入这个子 Skill:
242
247
 
@@ -244,11 +249,11 @@ description: 当用户需要处理自媒体平台接口工作流、平台数据
244
249
  - 用户要求写小红书文案、准备发布笔记、检查素材、生成话题标签。
245
250
  - 用户要求对小红书笔记点赞、收藏、评论或回复。
246
251
 
247
- 它可以执行完整流程,也可以只做部分能力。每次开始前必须先明确用户本轮任务边界:只写文案、只采集、只分析、只互动、只发布,还是完整流程。
252
+ 它可以执行完整流程,也可以只做部分能力。每次开始前必须先明确用户本轮任务边界:只写文案、只采集、只分析、只互动、只发布,还是完整流程。默认不建议使用浏览器自动发布作品,因为平台识别算法可能检测并限流;只有用户明确无视此说明并强制要求时才允许自动发布。
248
253
 
249
254
  子 Skill 名称:
250
255
 
251
- - `小红书运营与发布`
256
+ - `小红书运营`
252
257
 
253
258
  ### 16. 走 `个人创作库`
254
259
 
@@ -36,7 +36,7 @@ emoji: 📰
36
36
  ### 完全重新创作
37
37
 
38
38
  1. 先明确主题、读者、账号定位、文章目的和期望风格。
39
- 2. 使用 `自媒体知识库` 查询公众号创作相关知识点,根据主题构造 `domain` 和 `content_goal`。
39
+ 2. 使用 `自媒体知识库` 查询公众号创作相关知识点,根据主题构造 `domain` 和 `content_goal`。查询时,必须先明确用户具体需求,然后从知识库的一级能力开始,依次使用渐进式查询。不能跳级、跨越式查询。
40
40
  3. 结合知识库结果输出文章结构、标题候选和正文。
41
41
  4. 用户确认正文后再进入排版、导出或草稿箱流程。
42
42
 
@@ -0,0 +1,94 @@
1
+ ---
2
+ name: 创作总监
3
+ description: 当用户需要对自媒体内容进行方向把控、创新判断、结构复查、质量验收、上线前审稿或跨技能创作统筹时使用。
4
+ emoji: 🎯
5
+ ---
6
+
7
+ # 创作总监
8
+
9
+ 本 Skill 是 YuanFlow 自媒体创作链路的把控与验收角色。它不替代具体执行技能,而是负责判断创作目标是否清楚、选题是否成立、内容是否有创新、结构是否完整、表达是否适合平台,以及最终产物是否达到可发布或可交付标准。
10
+
11
+ ## 分类
12
+
13
+ 自媒体技能区
14
+
15
+ ## 核心职责
16
+
17
+ 1. 把控:明确用户目标、平台、受众、账号定位和成败标准。
18
+ 2. 创新:判断内容是否有新角度、新表达、新结构或新组合,避免同质化。
19
+ 3. 复查:检查选题、标题、开头、脚本、文案、画面、封面、话题、节奏和转化逻辑。
20
+ 4. 验收:给出是否可发布、需要修改、需要重做或需要补数据的结论。
21
+ 5. 调度:根据任务需要调用个人创作库、自媒体知识库、热门内容整理、选题策划、文案创作、视频智能剪辑、HTML报告生成等技能。
22
+
23
+ ## 开始前必须确认
24
+
25
+ 1. 本次要把控的对象:选题、标题、脚本、文案、封面、视频结构、发布计划、复盘报告或完整项目。
26
+ 2. 目标平台:抖音、小红书、公众号、视频号、B站、快手、TikTok、YouTube 或其它。
27
+ 3. 目标用户和账号定位。
28
+ 4. 用户当前阶段:从 0 到 1 创作、半成品复查、发布前验收、发布后复盘。
29
+ 5. 是否允许调用其它技能补充数据或重新生成内容。
30
+
31
+ ## 调度逻辑
32
+
33
+ 根据用户任务选择能力:
34
+
35
+ - 需要历史资产:调用 `个人创作库`。
36
+ - 需要方法论:调用 `自媒体知识库`。查询时,必须先明确用户具体需求,然后从知识库的一级能力开始,依次使用渐进式查询。不能跳级、跨越式查询。
37
+ - 需要趋势和爆款参考:调用 `热门内容整理`。
38
+ - 需要选题池:调用 `选题策划`。
39
+ - 需要标题、脚本、正文、发布文案:调用 `文案创作`。
40
+ - 需要小红书专项流程:调用 `小红书运营`。
41
+ - 需要公众号草稿和排版:调用 `公众号生成与发布`。
42
+ - 需要视频剪辑结构:调用 `视频智能剪辑`。
43
+ - 需要可视化汇总:调用 `HTML报告生成`。
44
+
45
+ ## 把控维度
46
+
47
+ ### 1. 目标把控
48
+
49
+ - 用户真正想要的是增长、转化、建立信任、表达观点、带货、获客、沉淀品牌,还是完成一次内容交付。
50
+ - 内容是否服务于这个目标,而不是只追求热闹。
51
+
52
+ ### 2. 选题把控
53
+
54
+ - 是否有明确受众。
55
+ - 是否有足够痛点、爽点、利益点或新信息。
56
+ - 是否和账号长期定位一致。
57
+ - 是否有平台适配性。
58
+
59
+ ### 3. 创新把控
60
+
61
+ - 是否只是套模板。
62
+ - 是否有独特观点、案例、反常识、结构创新、表达创新或视觉创新。
63
+ - 是否能形成用户记忆点。
64
+
65
+ ### 4. 结构复查
66
+
67
+ - 开头是否足够快进入主题。
68
+ - 中段是否有递进,而不是平铺。
69
+ - 结尾是否有明确行动、观点或记忆点。
70
+ - 是否存在信息重复、逻辑跳跃或表达过载。
71
+
72
+ ### 5. 发布验收
73
+
74
+ - 标题、封面、正文、脚本、话题、素材是否完整。
75
+ - 是否存在平台风险、夸大承诺、敏感表达、侵权风险或自动化发布风险。
76
+ - 小红书等平台不建议使用浏览器自动化直接发布作品,除非用户明确无视平台识别和限流风险并强制要求。
77
+
78
+ ## 输出格式
79
+
80
+ 默认输出:
81
+
82
+ 1. 总体判断:可继续 / 需要修改 / 建议重做。
83
+ 2. 核心问题:按影响优先级列出。
84
+ 3. 创新建议:给出可执行的新角度或新结构。
85
+ 4. 修改清单:明确改哪里、怎么改。
86
+ 5. 验收标准:修改后用什么标准判断合格。
87
+ 6. 下一步建议:进入具体技能执行,或生成 HTML 汇总报告。
88
+
89
+ ## 注意事项
90
+
91
+ - 不要只夸内容,要敢于指出不可发布的问题。
92
+ - 不要替具体技能编造已经查询到的数据;需要数据就调用对应技能。
93
+ - 不要把所有内容都推倒重做,优先判断能否局部修复。
94
+ - 用户只要轻量建议时,输出短版;用户要求完整审稿时,再输出详细版。
@@ -1,13 +1,15 @@
1
1
  ---
2
- name: 小红书运营与发布
2
+ name: 小红书运营
3
3
  description: 当用户需要小红书选题调研、文案创作、笔记搜索、账号分析、评论采集、图文或视频发布、点赞收藏评论回复等小红书运营任务时使用。
4
4
  emoji: 📕
5
5
  ---
6
6
 
7
- # 小红书运营与发布
7
+ # 小红书运营
8
8
 
9
9
  本 Skill 用于把小红书任务从“明确边界、采集分析、内容创作、素材检查、登录确认、发布执行、互动操作、结果回报”串成一个可控流程。它拥有完整能力,但用户可以只使用其中一部分,例如只写文案、只采集笔记、只分析账号、只采集评论、只做发布前检查,或一次性完成完整运营流程。
10
10
 
11
+ 重要提醒:不建议让 Agent 使用浏览器自动化直接发布作品。小红书等平台的识别算法可能检测自动化发布行为,并对账号内容进行限流或风控。除非用户在看到此说明后仍明确要求“无视风险,强制使用浏览器自动发布”,否则本 Skill 默认只做发布前检查、素材整理、文案准备和人工发布指导。
12
+
11
13
  ## 分类
12
14
 
13
15
  自媒体技能区
@@ -20,7 +22,8 @@ emoji: 📕
20
22
  4. 是否涉及封面或正文插图:如果需要生成封面或插图,必须使用 `生图技能`,并先和用户确认图片比例是 4:3 还是 3:4;用户已明确给出比例时直接采用。
21
23
  5. 发布确认策略:
22
24
  - 默认每次真实发布前必须向用户确认。
23
- - 如果用户明确说“后续都自动直接发布”“这个账号以后不用重复问”,可以把该偏好作为用户记忆;后续同类任务不重复确认,但仍需在执行结果里说明已按用户记忆自动执行。
25
+ - 不建议自动发布作品,因为平台识别算法可能检测并限流。只有用户明确表示已了解风险且强制要求浏览器自动发布时,才允许继续执行自动化发布。
26
+ - 如果用户明确说“后续都自动直接发布”“这个账号以后不用重复问”,必须再次提示平台自动化发布限流风险;用户仍明确坚持后,才可以把该偏好作为用户记忆;后续同类任务不重复确认,但仍需在执行结果里说明已按用户记忆自动执行。
24
27
  6. 评论和回复默认必须确认;点赞、收藏可按用户明确指令直接执行。
25
28
  7. 登录态使用 YuanFlow 独立浏览器配置,不要求用户复用默认 Chrome,不读取用户默认浏览器 Cookie。
26
29
 
@@ -88,6 +91,7 @@ yuanflow-cli browser dry-run --platform xiaohongshu --task publish --title "标
88
91
  1. 先确认任务边界:只写标题、只写正文、标题+正文、发布版笔记、种草文案、测评文案、探店文案、合集文案或完整发布流程。
89
92
  2. 确认账号定位、目标用户、产品/主题、语气风格、禁忌表达、字数范围、是否需要话题标签。
90
93
  3. 写任何小红书文案前,必须先使用 `自媒体知识库` 查找并参考对应写作方法论,例如选题、标题、钩子、种草、测评、探店、合集、转化或发布策略;不要跳过知识库直接凭空输出文案。
94
+ - 查询时,必须先明确用户具体需求,然后从知识库的一级能力开始,依次使用渐进式查询。不能跳级、跨越式查询。
91
95
  4. 如果用户要求参考平台趋势,先采集或搜索同类笔记,再结合知识库方法论创作。
92
96
  5. 输出小红书文案时默认包含:
93
97
  - 标题候选
@@ -114,7 +118,7 @@ yuanflow-cli browser dry-run --platform xiaohongshu --task publish --title "标
114
118
  3. 标题应符合小红书长度限制,过长时先给用户改写建议。
115
119
  4. 话题必须通过平台官方候选插入,优先使用 `yuanflow_browser_automation` 的 `select-platform-topics`;成功标准是正文编辑器 DOM 中出现 `a.tiptap-topic`、`data-topic`、`contenteditable="false"`,不能只看见普通 `#关键词` 文本。
116
120
  5. 话题异常时先用 `inspect-platform-topic-dom` 写出 DOM 诊断文件,再根据诊断重试;不要只靠坐标、截图颜色或文本候选点击判断。
117
- 6. 默认发布前确认;用户已明确授权自动发布时,可直接执行。
121
+ 6. 默认发布前确认;不建议浏览器自动发布。只有用户明确表示无视平台识别和限流风险、强制要求自动发布时,才可执行自动化发布。
118
122
  7. 发布失败时报告失败阶段:未登录、素材不存在、上传失败、页面元素变化、提交失败或平台风控。
119
123
  8. 任务完整结束后可以询问用户对结果是否满意;如果用户明确表示满意,可将最终结果、素材路径、关键制作流程、发布前检查结论保存到 `个人创作库`,便于后续复用。
120
124
 
@@ -123,7 +127,7 @@ yuanflow-cli browser dry-run --platform xiaohongshu --task publish --title "标
123
127
  1. 检查标题、正文、视频路径、封面、话题、可见性、发布时间和目标账号。
124
128
  2. 视频路径必须是本地可访问文件。
125
129
  3. 不要把图文和视频混成一个发布任务。
126
- 4. 默认发布前确认;用户已明确授权自动发布时,可直接执行。
130
+ 4. 默认发布前确认;不建议浏览器自动发布。只有用户明确表示无视平台识别和限流风险、强制要求自动发布时,才可执行自动化发布。
127
131
 
128
132
  ## 互动流程
129
133
 
@@ -154,6 +158,7 @@ yuanflow-cli browser dry-run --platform xiaohongshu --task publish --title "标
154
158
  ## 常见错误
155
159
 
156
160
  - 用户只想写文案,却直接尝试发布。
161
+ - 用户没有明确无视平台风控风险,却使用浏览器自动发布作品。
157
162
  - 采集任务没有确认关键词、数量或目标笔记。
158
163
  - 发布前没有检查图片/视频路径是否存在。
159
164
  - 把小红书 MCP 当成独立安装项使用。
@@ -16,6 +16,7 @@ emoji: 🧭
16
16
  - 如果没有命中,说明“未发现可用历史参考记录”,继续下一步。
17
17
  2. 调用 `自媒体知识库`。
18
18
  - 先查 `knowledge docs` 或 `knowledge entry`,根据账号行业、目标人群、内容目标和商业目标构造 `domain` 和 `content_goal`。
19
+ - 查询时,必须先明确用户具体需求,然后从知识库的一级能力开始,依次使用渐进式查询。不能跳级、跨越式查询。
19
20
  - 按返回的 `next_actions` 分层查看 `packs`、`rules` 或 `rule-detail`,直到拿到足够的方法摘要。
20
21
  - 不要自己拼 SQL,不要跳过知识库直接凭空输出。
21
22
  3. 结合用户要求、历史参考记录和知识库结果,输出账号定位方案。
@@ -16,6 +16,7 @@ emoji: ✍️
16
16
  - 如果没有命中,说明“未发现可用历史参考记录”,继续下一步。
17
17
  2. 调用 `自媒体知识库`。
18
18
  - 先查 `knowledge docs` 或 `knowledge entry`,根据平台、受众、语气、内容目标和转化目标构造 `domain` 和 `content_goal`。
19
+ - 查询时,必须先明确用户具体需求,然后从知识库的一级能力开始,依次使用渐进式查询。不能跳级、跨越式查询。
19
20
  - 按返回的 `next_actions` 分层查看 `packs`、`rules` 或 `rule-detail`,直到拿到足够的方法摘要。
20
21
  - 不要自己拼 SQL,不要跳过知识库直接凭空输出。
21
22
  3. 结合用户要求、历史参考记录和知识库结果,输出文案。
@@ -0,0 +1,83 @@
1
+ ---
2
+ name: 热门内容整理
3
+ description: 当用户需要整理抖音视频热榜、爆款案例、高完播率、高涨粉率、高点赞率内容,并结合个人创作方向输出创作建议时使用。
4
+ emoji: 🔥
5
+ ---
6
+
7
+ # 热门内容整理
8
+
9
+ 本 Skill 用于把热门内容从“榜单采集、结构化整理、创作相关性判断、个人化建议”串成完整流程。它不是简单罗列链接,而是帮助用户判断哪些热点值得借鉴、如何借鉴、哪些不适合照搬。
10
+
11
+ ## 分类
12
+
13
+ 自媒体技能区
14
+
15
+ ## 开始前必须确认
16
+
17
+ 1. 用户的创作方向、账号定位、目标平台和目标受众。
18
+ 2. 用户希望参考的主题、行业、关键词或内容垂类;如果用户没有指定,先按全部垂类查询。
19
+ 3. 是否已有个人创作库历史记录、历史任务或历史对话可参考。
20
+ 4. 输出目标:选题建议、内容拆解、脚本方向、账号定位参考、投放素材参考或复盘报告。
21
+
22
+ ## 数据查询逻辑
23
+
24
+ 优先使用 YuanFlow 受控工具 `yuanflow_cli_call` 调用:
25
+
26
+ ```bash
27
+ yuanflow-cli trending video-hot-list --format agent-json
28
+ ```
29
+
30
+ 默认参数:
31
+
32
+ - `page=1`
33
+ - `page_size=10`
34
+ - `date_window=2`
35
+ - `tags` 为空时查询全部垂类
36
+
37
+ 必须一次性查询全部榜单分类:
38
+
39
+ - `1001` 视频总榜
40
+ - `1002` 低粉爆款
41
+ - `1003` 高完播率
42
+ - `1004` 高涨粉率
43
+ - `1005` 高点赞率
44
+
45
+ 如果用户明确指定垂类标签,使用 `--tags-json` 传入:
46
+
47
+ ```bash
48
+ yuanflow-cli trending video-hot-list --tags-json "[{\"value\":\"顶级垂类标签id\",\"children\":[{\"value\":\"子级垂类标签id\"}]}]" --format agent-json
49
+ ```
50
+
51
+ ## 分析流程
52
+
53
+ 1. 先读取个人创作库,查找用户过往的选题、文案、脚本、发布计划和数据复盘。
54
+ 2. 再执行五类榜单查询,按榜单分类保存结果。
55
+ 3. 对每个榜单提取标题、主题、表达形式、互动指标、账号特征和可复用方法。
56
+ 4. 交叉比较五个榜单,识别共同趋势和差异:
57
+ - 总榜代表平台当前广泛热度。
58
+ - 低粉爆款代表普通账号可借鉴机会。
59
+ - 高完播率代表内容结构和留存能力。
60
+ - 高涨粉率代表人设、系列化和关注理由。
61
+ - 高点赞率代表情绪价值、观点共鸣和表达记忆点。
62
+ 5. 结合用户个人创作信息、创作库历史记录、历史任务或对话记录,给出个人化建议。
63
+
64
+ ## 输出结构
65
+
66
+ 必须输出:
67
+
68
+ 1. 查询范围和参数说明。
69
+ 2. 五类榜单摘要。
70
+ 3. 热点共同规律。
71
+ 4. 与用户创作方向最相关的机会点。
72
+ 5. 不建议照搬的风险点。
73
+ 6. 可执行选题建议。
74
+ 7. 可执行内容结构建议。
75
+ 8. 下一步建议:是否进入选题策划、文案创作、创作总监复查或 HTML 报告生成。
76
+
77
+ ## 注意事项
78
+
79
+ - 不把“热门”简单等同于“适合用户做”。
80
+ - 不只看播放或点赞,要结合完播、涨粉、账号体量和内容形式判断。
81
+ - 不要把五个榜单混成一个列表,必须保留分类来源。
82
+ - 不要跳过用户个人创作信息;没有历史记录时要明确说明“未读取到可参考历史”。
83
+ - 如果用户需要沉淀结果,询问是否写入个人创作库。
@@ -16,6 +16,7 @@ emoji: 📡
16
16
  - 如果没有命中,说明“未发现可用历史参考记录”,继续下一步。
17
17
  2. 调用 `自媒体知识库`。
18
18
  - 先查 `knowledge docs` 或 `knowledge entry`,根据直播平台、直播目标、产品、预算和目标人群构造 `domain` 和 `content_goal`。
19
+ - 查询时,必须先明确用户具体需求,然后从知识库的一级能力开始,依次使用渐进式查询。不能跳级、跨越式查询。
19
20
  - 按返回的 `next_actions` 分层查看 `packs`、`rules` 或 `rule-detail`,直到拿到足够的方法摘要。
20
21
  - 不要自己拼 SQL,不要跳过知识库直接凭空输出。
21
22
  3. 结合用户要求、历史参考记录和知识库结果,输出直播投流策略。
@@ -16,6 +16,7 @@ emoji: 🎤
16
16
  - 如果没有命中,说明“未发现可用历史参考记录”,继续下一步。
17
17
  2. 调用 `自媒体知识库`。
18
18
  - 先查 `knowledge docs` 或 `knowledge entry`,根据直播主题、产品、目标人群和转化目标构造 `domain` 和 `content_goal`。
19
+ - 查询时,必须先明确用户具体需求,然后从知识库的一级能力开始,依次使用渐进式查询。不能跳级、跨越式查询。
19
20
  - 按返回的 `next_actions` 分层查看 `packs`、`rules` 或 `rule-detail`,直到拿到足够的方法摘要。
20
21
  - 不要自己拼 SQL,不要跳过知识库直接凭空输出。
21
22
  3. 结合用户要求、历史参考记录和知识库结果,输出直播话术。
@@ -8,6 +8,8 @@ emoji: 🧠
8
8
 
9
9
  这个 Skill 负责使用 YuanFlow 的自媒体知识库原子能力。它和社媒数据采集、作品评论、作品下载是独立能力,不要混用接口。
10
10
 
11
+ 查询时,必须先明确用户具体需求,然后从知识库的一级能力开始,依次使用渐进式查询。不能跳级、跨越式查询。
12
+
11
13
  ## 环境判断
12
14
 
13
15
  1. 在 YuanFlow 主程序内,优先调用内置工具 `yuanflow_cli_call`。
@@ -46,6 +46,7 @@ emoji: 📈
46
46
  - 如果没有命中,说明“未发现可用历史参考记录”,继续下一步。
47
47
  5. 调用 `自媒体知识库`。
48
48
  - 先查 `knowledge docs` 或 `knowledge entry`,根据平台、投放目标、预算、人群和素材类型构造 `domain` 和 `content_goal`。
49
+ - 查询时,必须先明确用户具体需求,然后从知识库的一级能力开始,依次使用渐进式查询。不能跳级、跨越式查询。
49
50
  - 按返回的 `next_actions` 分层查看 `packs`、`rules` 或 `rule-detail`,直到拿到足够的方法摘要。
50
51
  - 不要自己拼 SQL,不要跳过知识库直接凭空输出。
51
52
  6. 结合用户要求、账号最近 5 条视频数据、历史参考记录和知识库结果,输出视频投流策略。
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: 视频智能剪辑
3
- description: "用于把主音频、文案、B-roll 视频或图片素材组合成可解释的智能剪辑项目。执行前先查询自媒体知识库中的视频剪辑策略规则,再按 1秒1帧抽帧,由 Agent 判断画面语义并生成 EDL,交给 yuanflow-cli video 校验和渲染。"
3
+ description: "用于把主音频、文案、B-roll 视频或图片素材组合成可解释的智能剪辑项目。执行前先查询自媒体知识库中的视频剪辑策略规则,再用音视频在线转文字取得 ASR/时间戳,导入音频对齐,按 1秒1帧抽帧并回写视觉理解,由 Agent 生成 EDL,交给 yuanflow-cli video 校验和渲染。"
4
4
  metadata:
5
5
  builtin_skill_version: "1.0.1"
6
6
  yuanflow:
@@ -25,6 +25,7 @@ tags:
25
25
  推荐查询顺序:
26
26
 
27
27
  1. 先用 `knowledge entry` 建立任务入口,`domain` 填 `视频剪辑`,`content_goal` 写清本次剪辑目标。
28
+ - 查询时,必须先明确用户具体需求,然后从知识库的一级能力开始,依次使用渐进式查询。不能跳级、跨越式查询。
28
29
  2. 再用 `knowledge packs --capability-code video_editing_strategy` 查看视频剪辑策略下的方法包。
29
30
  3. 必查 `video_edit_logic_layer_pack`,获得通用剪辑逻辑层规则。
30
31
  4. 如果任务是口播、旁白、主音频驱动,查询 `talking_head_edit_template_pack`。
@@ -101,8 +102,9 @@ CLI 会生成 `strategy_snapshot.json`,后续 Agent 必须基于这个快照
101
102
 
102
103
  - 主音频可以是口播、旁白或已经录好的音频。
103
104
  - B-roll 可以是视频或图片素材。
104
- - 第一版视觉理解不调用视觉模型,使用 `yuanflow-cli video timeline --fps 1` 1秒1帧抽帧。
105
- - Agent 需要查看抽帧结果,自己判断每段画面的语义、主体、动作和适合承接的文案。
105
+ - 第一版音频对齐优先使用“音视频在线转文字”Skill ASR 时间戳结果;如果 ASR 没有时间戳,使用 forced alignment 或人工按文案切分,不编造时间戳。
106
+ - 第一版视觉理解不调用固定视觉模型,使用 `yuanflow-cli video timeline --fps 1` 按 1秒1帧抽帧。
107
+ - Agent 或人工需要查看抽帧结果,判断每段画面的语义、主体、动作和适合承接的文案,再用 `video visual-review` 回写结构化视觉描述。
106
108
  - 第一版 `video plan` 不自动生成 EDL,由 Agent 生成 EDL 后交给 CLI 校验和渲染。
107
109
 
108
110
  ## 执行原则
@@ -161,18 +163,27 @@ yuanflow-cli video inspect --project "D:\素材\yuanflow-video-edit" --format ag
161
163
 
162
164
  如果只是验证命令结构,可以加 `--dry-run`。
163
165
 
164
- ### 3. 导入文案或转写
166
+ ### 3. 音频对齐
165
167
 
166
- 如果用户已有文案:
168
+ 主音频驱动的剪辑必须先建立音频时间轴。优先使用“音视频在线转文字”Skill 调用在线 ASR;如果 ASR 返回 `segments` / `words` 时间戳,保存为 JSON 后导入:
167
169
 
168
170
  ```bash
169
- yuanflow-cli video transcribe \
171
+ yuanflow-cli video align \
170
172
  --project "D:\素材\yuanflow-video-edit" \
171
- --script-file "D:\素材\口播文案.txt" \
173
+ --asr-file "D:\素材\asr.json" \
172
174
  --format agent-json
173
175
  ```
174
176
 
175
- 如果用户只有音频,先使用“音视频在线转文字”或“本地音视频转文字”得到文本,再用 `--script-file` 导入。
177
+ 如果 ASR 只返回纯文本,没有时间戳:
178
+
179
+ - 先明确说明缺少时间戳。
180
+ - 可用 forced alignment 或人工按文案切分生成 `segments`。
181
+ - 只有在用户允许“粗对齐”时,才能按文本段落和音频总时长均分兜底。
182
+
183
+ `video align` 会生成:
184
+
185
+ - `audio_alignment.json`
186
+ - 带 `start_s / end_s` 的 `beats.json`
176
187
 
177
188
  ### 4. 生成 1秒1帧时间线
178
189
 
@@ -191,14 +202,51 @@ CLI 会生成:
191
202
 
192
203
  Agent 必须基于这些抽帧图片判断画面,不要凭文件名猜。
193
204
 
194
- ### 5. Agent 生成 timeline_plan 和 EDL
205
+ ### 5. 视觉理解回写
206
+
207
+ Agent 或人工查看抽帧图片后,先写出 `visual_review.agent.json`,不要直接跳到 EDL。基础格式:
208
+
209
+ ```json
210
+ {
211
+ "version": 1,
212
+ "reviews": [
213
+ {
214
+ "segment_id": "vis_0001",
215
+ "description": "产品近景,手持展示刷头。",
216
+ "subjects": ["产品", "手"],
217
+ "scene": "厨房水槽",
218
+ "motion": "展示",
219
+ "semantic_tags": ["product_closeup", "kitchen"],
220
+ "quality_score": 0.88
221
+ }
222
+ ]
223
+ }
224
+ ```
225
+
226
+ 导入视觉理解:
227
+
228
+ ```bash
229
+ yuanflow-cli video visual-review \
230
+ --project "D:\素材\yuanflow-video-edit" \
231
+ --review-file "D:\素材\yuanflow-video-edit\visual_review.agent.json" \
232
+ --format agent-json
233
+ ```
234
+
235
+ CLI 会生成或更新:
236
+
237
+ - `visual_understanding.json`
238
+ - 带结构化描述的 `visual_segments.json`
239
+
240
+ ### 6. Agent 生成 timeline_plan 和 EDL
195
241
 
196
242
  Agent 读取:
197
243
 
198
244
  - `project.json`
199
245
  - `assets.json`
246
+ - `audio_alignment.json`
200
247
  - `beats.json`
201
248
  - `visual_segments.json`
249
+ - `visual_understanding.json`
202
250
  - `strategy_snapshot.json`
203
251
  - 抽帧图片
204
252
 
@@ -268,7 +316,7 @@ Agent 读取:
268
316
  }
269
317
  ```
270
318
 
271
- ### 6. 校验 EDL
319
+ ### 7. 校验 EDL
272
320
 
273
321
  ```bash
274
322
  yuanflow-cli video plan \
@@ -289,14 +337,14 @@ yuanflow-cli video plan \
289
337
 
290
338
  只有校验通过后,才允许渲染。
291
339
 
292
- ### 7. 渲染预览和自检
340
+ ### 8. 渲染预览和自检
293
341
 
294
342
  ```bash
295
343
  yuanflow-cli video render-preview --project "D:\素材\yuanflow-video-edit" --format agent-json
296
344
  yuanflow-cli video evaluate --project "D:\素材\yuanflow-video-edit" --file "D:\素材\yuanflow-video-edit\preview.mp4" --format agent-json
297
345
  ```
298
346
 
299
- ### 8. 最终导出
347
+ ### 9. 最终导出
300
348
 
301
349
  ```bash
302
350
  yuanflow-cli video render-final --project "D:\素材\yuanflow-video-edit" --format agent-json
@@ -16,6 +16,7 @@ emoji: 💡
16
16
  - 如果没有命中,说明“未发现可用历史参考记录”,继续下一步。
17
17
  2. 调用 `自媒体知识库`。
18
18
  - 先查 `knowledge docs` 或 `knowledge entry`,根据平台、受众、内容目标和选题方向构造 `domain` 和 `content_goal`。
19
+ - 查询时,必须先明确用户具体需求,然后从知识库的一级能力开始,依次使用渐进式查询。不能跳级、跨越式查询。
19
20
  - 按返回的 `next_actions` 分层查看 `packs`、`rules` 或 `rule-detail`,直到拿到足够的方法摘要。
20
21
  - 不要自己拼 SQL,不要跳过知识库直接凭空输出。
21
22
  3. 结合用户要求、历史参考记录和知识库结果,输出选题策划结果。
@@ -6,6 +6,7 @@ import { listOssCommands } from './oss-tools.js';
6
6
  import { listBrowserCommands } from './browser-tools.js';
7
7
  import { listSearchCommands, listWorkCommands } from './work-tools.js';
8
8
  import { listVideoCommands } from './video-tools.js';
9
+ import { listTrendingCommands } from './trending-tools.js';
9
10
 
10
11
  const ERROR_MAP = [
11
12
  {
@@ -109,6 +110,7 @@ export function buildCommandRegistry() {
109
110
  const ossCommands = listOssCommands();
110
111
  const browserCommands = listBrowserCommands();
111
112
  const videoCommands = listVideoCommands();
113
+ const trendingCommands = listTrendingCommands();
112
114
  return [
113
115
  ...shortcuts,
114
116
  ...endpoints,
@@ -119,6 +121,7 @@ export function buildCommandRegistry() {
119
121
  ...ossCommands,
120
122
  ...browserCommands,
121
123
  ...videoCommands,
124
+ ...trendingCommands,
122
125
  ].sort((left, right) => left.key.localeCompare(right.key));
123
126
  }
124
127
 
package/src/cli.js CHANGED
@@ -27,6 +27,7 @@ import {
27
27
  searchContent,
28
28
  searchUsers,
29
29
  } from './work-tools.js';
30
+ import { fetchVideoHotList } from './trending-tools.js';
30
31
 
31
32
  export async function main(argv) {
32
33
  const args = argv.slice(2);
@@ -72,6 +73,11 @@ export async function main(argv) {
72
73
  return;
73
74
  }
74
75
 
76
+ if (command === 'trending') {
77
+ await handleTrending(rest);
78
+ return;
79
+ }
80
+
75
81
  if (command === 'knowledge') {
76
82
  await handleKnowledge(rest);
77
83
  return;
@@ -351,6 +357,19 @@ async function handleSearch(args) {
351
357
  });
352
358
  }
353
359
 
360
+ async function handleTrending(args) {
361
+ const { positionals, options } = parseOptions(args);
362
+ const [action = 'video-hot-list'] = positionals;
363
+ if (action !== 'video-hot-list') {
364
+ throw new Error('未知 trending 命令。用法:yuanflow-cli trending video-hot-list');
365
+ }
366
+ const result = await fetchVideoHotList({ options });
367
+ await outputResult(result, options, {
368
+ command: 'trending video-hot-list',
369
+ meta: { endpoint: result.endpoint.path, kind: result.endpoint.kind },
370
+ });
371
+ }
372
+
354
373
  async function handleKnowledge(args) {
355
374
  const { positionals, options } = parseOptions(args);
356
375
  const [action = 'docs'] = positionals;
@@ -578,6 +597,7 @@ function printHelp() {
578
597
  yuanflow-cli works download --platform youtube --target "dQw4w9WgXcQ" --dry-run
579
598
  yuanflow-cli search content --platform xiaohongshu --keyword "美妆" --dry-run
580
599
  yuanflow-cli search users --platform instagram --keyword "nasa" --dry-run
600
+ yuanflow-cli trending video-hot-list --dry-run --format agent-json
581
601
  yuanflow-cli knowledge docs --dry-run
582
602
  yuanflow-cli knowledge entry --output-format short_video_script --domain 自媒体运营 --content-goal "写一个创业者短视频选题" --target-audience 创业者 --format agent-json
583
603
  yuanflow-cli oss signed-url --key path/to/file.png --ttl-seconds 1800 --format agent-json
@@ -585,14 +605,16 @@ function printHelp() {
585
605
  yuanflow-cli browser task-plan --platform xiaohongshu --task publish --account main --format agent-json
586
606
  yuanflow-cli video init --input "D:\\素材" --primary-audio "D:\\素材\\口播.mp3" --broll "D:\\素材\\画面.mp4" --format agent-json
587
607
  yuanflow-cli video strategy --project "D:\\素材\\yuanflow-video-edit" --template-type talking_head --rules-file "D:\\规则\\logic.json,D:\\规则\\template.json,D:\\规则\\cli.json" --format agent-json
608
+ yuanflow-cli video align --project "D:\\素材\\yuanflow-video-edit" --asr-file "D:\\素材\\asr.json" --format agent-json
588
609
  yuanflow-cli video timeline --project "D:\\素材\\yuanflow-video-edit" --fps 1 --format agent-json
610
+ yuanflow-cli video visual-review --project "D:\\素材\\yuanflow-video-edit" --review-file "D:\\素材\\yuanflow-video-edit\\visual_review.agent.json" --format agent-json
589
611
  yuanflow-cli video plan --project "D:\\素材\\yuanflow-video-edit" --timeline-plan "D:\\素材\\yuanflow-video-edit\\timeline_plan.agent.json" --format agent-json
590
612
  yuanflow-cli list douyin
591
613
 
592
614
  说明:
593
615
  社媒请求调用 Yuan API 的 /social/*path;知识库和 OSS 原子能力调用 /api/* 或 /atomic/*。
594
616
  browser 命令是自媒体平台专用浏览器自动化协议,只返回受控 profile/cookie/任务路径与执行计划,不用于普通网页搜索。
595
- video 命令是视频智能剪辑基础链路:规则库策略快照、主音频+B-roll、1秒1帧抽帧、Agent 生成 timeline_plan/EDL、CLI 校验和渲染。
617
+ video 命令是视频智能剪辑基础链路:规则库策略快照、主音频+B-roll、ASR 时间戳对齐、1秒1帧抽帧、视觉理解回写、Agent 生成 timeline_plan/EDL、CLI 校验和渲染。
596
618
  需要鉴权的请求都会使用 Authorization: Bearer <token>。
597
619
  token 优先级:--token > YUANCHUANG_API_TOKEN > 本地 config.token。
598
620
  YuanFlow 主程序内使用时,token 由主程序认证系统注入,不需要手动配置。
@@ -0,0 +1,117 @@
1
+ import { callEndpoint } from './request.js';
2
+
3
+ const VIDEO_HOT_LIST_PATH = '/douyin/billboard/fetch_hot_total_video_list';
4
+
5
+ const VIDEO_HOT_BOARD_TYPES = [
6
+ { subType: 1001, label: '视频总榜' },
7
+ { subType: 1002, label: '低粉爆款' },
8
+ { subType: 1003, label: '高完播率' },
9
+ { subType: 1004, label: '高涨粉率' },
10
+ { subType: 1005, label: '高点赞率' },
11
+ ];
12
+
13
+ export function listTrendingCommands() {
14
+ return [
15
+ {
16
+ key: 'trending.video-hot-list',
17
+ command: 'trending video-hot-list',
18
+ kind: 'trending',
19
+ description: '一次性查询抖音视频热榜五类榜单:视频总榜、低粉爆款、高完播率、高涨粉率、高点赞率。',
20
+ method: 'POST',
21
+ socialPath: VIDEO_HOT_LIST_PATH,
22
+ positionals: [],
23
+ options: [
24
+ { flag: '--page', name: 'page', required: false, label: '页码,默认 1' },
25
+ { flag: '--page-size', name: 'page_size', required: false, label: '每页数量,默认 10' },
26
+ { flag: '--date-window', name: 'date_window', required: false, label: '时间窗口,默认 2 按天' },
27
+ { flag: '--tags-json', name: 'tags', required: false, label: '垂类标签 JSON 数组,空则全部垂类' },
28
+ ],
29
+ returns: '返回五类视频热榜的统一 JSON,字段结构以上游实际响应为准。',
30
+ },
31
+ ];
32
+ }
33
+
34
+ export async function fetchVideoHotList({ options }) {
35
+ const query = buildVideoHotListQuery(options);
36
+ const boards = [];
37
+
38
+ for (const board of VIDEO_HOT_BOARD_TYPES) {
39
+ const requestBody = {
40
+ page: query.page,
41
+ page_size: query.page_size,
42
+ date_window: query.date_window,
43
+ sub_type: board.subType,
44
+ ...(query.tags !== undefined ? { tags: query.tags } : {}),
45
+ };
46
+ const response = await callEndpoint(VIDEO_HOT_LIST_PATH, {
47
+ ...options,
48
+ method: 'POST',
49
+ body: requestBody,
50
+ });
51
+ boards.push({
52
+ sub_type: board.subType,
53
+ label: board.label,
54
+ request: requestBody,
55
+ response,
56
+ });
57
+ }
58
+
59
+ return {
60
+ platform: 'douyin',
61
+ endpoint: {
62
+ method: 'POST',
63
+ path: VIDEO_HOT_LIST_PATH,
64
+ kind: 'video-hot-list',
65
+ },
66
+ query,
67
+ boards,
68
+ guidance: [
69
+ '结合用户个人创作信息、个人创作库历史记录、历史任务或对话记录输出建议。',
70
+ '不要只罗列榜单,必须区分五类榜单来源并提炼可执行选题和内容结构。',
71
+ '重点比较总榜、低粉爆款、高完播率、高涨粉率、高点赞率之间的差异。',
72
+ ],
73
+ };
74
+ }
75
+
76
+ function buildVideoHotListQuery(options = {}) {
77
+ return {
78
+ page: parsePositiveInteger(options.named?.page, 1, 'page'),
79
+ page_size: parsePositiveInteger(options.named?.['page-size'] ?? options.named?.page_size, 10, 'page_size'),
80
+ date_window: parsePositiveInteger(options.named?.['date-window'] ?? options.named?.date_window, 2, 'date_window'),
81
+ ...(parseTags(options.named?.['tags-json'] ?? options.named?.tags) !== undefined
82
+ ? { tags: parseTags(options.named?.['tags-json'] ?? options.named?.tags) }
83
+ : {}),
84
+ };
85
+ }
86
+
87
+ function parsePositiveInteger(value, fallback, name) {
88
+ if (value === undefined || value === null || value === '') {
89
+ return fallback;
90
+ }
91
+ const number = Number(value);
92
+ if (!Number.isInteger(number) || number <= 0) {
93
+ throw new Error(`${name} 必须是正整数。`);
94
+ }
95
+ return number;
96
+ }
97
+
98
+ function parseTags(value) {
99
+ if (value === undefined || value === null || value === '') {
100
+ return undefined;
101
+ }
102
+ if (Array.isArray(value)) {
103
+ return value;
104
+ }
105
+ try {
106
+ const parsed = JSON.parse(String(value));
107
+ if (!Array.isArray(parsed)) {
108
+ throw new Error('tags-json 必须是数组。');
109
+ }
110
+ return parsed;
111
+ } catch (error) {
112
+ if (error?.message === 'tags-json 必须是数组。') {
113
+ throw error;
114
+ }
115
+ throw new Error('tags-json 必须是合法 JSON 数组。');
116
+ }
117
+ }
@@ -9,7 +9,9 @@ const IMAGE_EXTS = new Set(['.jpg', '.jpeg', '.png', '.webp']);
9
9
  const PROJECT_FILE = 'project.json';
10
10
  const ASSETS_FILE = 'assets.json';
11
11
  const BEATS_FILE = 'beats.json';
12
+ const AUDIO_ALIGNMENT_FILE = 'audio_alignment.json';
12
13
  const VISUAL_SEGMENTS_FILE = 'visual_segments.json';
14
+ const VISUAL_UNDERSTANDING_FILE = 'visual_understanding.json';
13
15
  const EDL_FILE = 'edl.json';
14
16
  const STRATEGY_FILE = 'strategy_snapshot.json';
15
17
  const TIMELINE_PLAN_FILE = 'timeline_plan.json';
@@ -39,11 +41,19 @@ export function listVideoCommands() {
39
41
  option('--script-file', 'scriptFile', false, '读取文案文件。'),
40
42
  option('--transcript-file', 'transcriptFile', false, '读取转写 JSON 或文本文件。'),
41
43
  ]),
44
+ videoCommand('align', '导入 ASR 或 forced alignment 时间戳结果,生成 audio_alignment.json 和带时间戳 beats.json。', [
45
+ option('--project', 'project', true, '剪辑项目目录。'),
46
+ option('--asr-file', 'asrFile', true, '音视频在线转文字或 forced alignment 输出的 JSON 文件。'),
47
+ ]),
42
48
  videoCommand('timeline', '按 1 秒 1 帧抽帧并生成 visual_segments.json。', [
43
49
  option('--project', 'project', true, '剪辑项目目录。'),
44
50
  option('--fps', 'fps', false, '抽帧频率,基础版建议 1。'),
45
51
  option('--dry-run', 'dryRun', false, '不调用 ffmpeg,只生成占位帧清单。'),
46
52
  ]),
53
+ videoCommand('visual-review', '导入 Agent/人工对抽帧结果的视觉理解,回写 visual_segments.json。', [
54
+ option('--project', 'project', true, '剪辑项目目录。'),
55
+ option('--review-file', 'reviewFile', true, 'Agent 或人工生成的 visual_review.agent.json。'),
56
+ ]),
47
57
  videoCommand('plan', '校验 Agent 生成的 timeline_plan 和 EDL,写入标准 timeline_plan.json / edl.json。', [
48
58
  option('--project', 'project', true, '剪辑项目目录。'),
49
59
  option('--timeline-plan', 'timelinePlan', false, 'Agent 生成的 timeline_plan.agent.json。'),
@@ -72,12 +82,14 @@ export async function runVideoCommand({ action, options }) {
72
82
  if (action === 'inspect') return inspectProject(options);
73
83
  if (action === 'strategy') return saveStrategySnapshot(options);
74
84
  if (action === 'transcribe') return importTranscript(options);
85
+ if (action === 'align') return importAudioAlignment(options);
75
86
  if (action === 'timeline') return buildTimeline(options);
87
+ if (action === 'visual-review') return importVisualReview(options);
76
88
  if (action === 'plan') return planFromAgentEdl(options);
77
89
  if (action === 'render-preview') return renderVideo(options, { final: false });
78
90
  if (action === 'render-final') return renderVideo(options, { final: true });
79
91
  if (action === 'evaluate') return evaluateVideo(options);
80
- throw new Error('未知 video 命令。用法:yuanflow-cli video init|inspect|strategy|transcribe|timeline|plan|render-preview|render-final|evaluate');
92
+ throw new Error('未知 video 命令。用法:yuanflow-cli video init|inspect|strategy|transcribe|align|timeline|visual-review|plan|render-preview|render-final|evaluate');
81
93
  }
82
94
 
83
95
  function videoCommand(action, description, options) {
@@ -208,6 +220,58 @@ async function importTranscript(options) {
208
220
  return { ok: true, action: 'video.transcribe', project: projectSummary(project), beats };
209
221
  }
210
222
 
223
+ async function importAudioAlignment(options) {
224
+ const project = await readProject(options);
225
+ const asrPath = requiredPath(options.named?.['asr-file'], '缺少 --asr-file。');
226
+ const parsed = JSON.parse(await fs.readFile(asrPath, 'utf8'));
227
+ const segments = extractAlignmentSegments(parsed);
228
+ if (segments.length === 0) {
229
+ return {
230
+ ok: false,
231
+ action: 'video.align',
232
+ project: projectSummary(project),
233
+ alignment_validation: {
234
+ ok: false,
235
+ errors: ['ASR/对齐结果里没有可用的 segments,无法生成带时间戳 beats。'],
236
+ },
237
+ };
238
+ }
239
+
240
+ const alignment = {
241
+ version: 1,
242
+ source: path.resolve(asrPath),
243
+ imported_at: new Date().toISOString(),
244
+ text: stringOrDefault(parsed.text || parsed.data?.text || parsed.result?.text, segments.map((item) => item.text).join('')),
245
+ segments,
246
+ };
247
+ const beats = segments.map((segment, index) => ({
248
+ beat_id: `beat_${String(index + 1).padStart(3, '0')}`,
249
+ text: segment.text,
250
+ start_s: segment.start_s,
251
+ end_s: segment.end_s,
252
+ intent: '',
253
+ visual_need: '',
254
+ emotion: '',
255
+ alignment_source: 'audio_alignment',
256
+ }));
257
+ await writeJson(path.join(project.work_dir, AUDIO_ALIGNMENT_FILE), alignment);
258
+ await writeJson(path.join(project.work_dir, BEATS_FILE), {
259
+ source: path.resolve(asrPath),
260
+ imported_at: new Date().toISOString(),
261
+ alignment_file: path.join(project.work_dir, AUDIO_ALIGNMENT_FILE),
262
+ beats,
263
+ });
264
+ return {
265
+ ok: true,
266
+ action: 'video.align',
267
+ project: projectSummary(project),
268
+ alignment,
269
+ beats,
270
+ alignment_path: path.join(project.work_dir, AUDIO_ALIGNMENT_FILE),
271
+ beats_path: path.join(project.work_dir, BEATS_FILE),
272
+ };
273
+ }
274
+
211
275
  async function saveStrategySnapshot(options) {
212
276
  const project = await readProject(options);
213
277
  const templateType = stringOrDefault(options.named?.['template-type'], 'custom');
@@ -310,6 +374,61 @@ async function buildTimeline(options) {
310
374
  };
311
375
  }
312
376
 
377
+ async function importVisualReview(options) {
378
+ const project = await readProject(options);
379
+ const reviewPath = requiredPath(options.named?.['review-file'], '缺少 --review-file。');
380
+ const review = JSON.parse(await fs.readFile(reviewPath, 'utf8'));
381
+ const visualSegments = await readOptionalJson(path.join(project.work_dir, VISUAL_SEGMENTS_FILE));
382
+ if (!Array.isArray(visualSegments) || visualSegments.length === 0) {
383
+ return {
384
+ ok: false,
385
+ action: 'video.visual-review',
386
+ project: projectSummary(project),
387
+ review_validation: { ok: false, errors: ['请先执行 video timeline 生成 visual_segments.json。'], warnings: [] },
388
+ };
389
+ }
390
+
391
+ const reviewValidation = validateVisualReview(review, visualSegments);
392
+ if (!reviewValidation.ok) {
393
+ return { ok: false, action: 'video.visual-review', project: projectSummary(project), review_validation: reviewValidation };
394
+ }
395
+
396
+ const byId = new Map((review.reviews || []).map((item) => [item.segment_id, item]));
397
+ const reviewedSegments = visualSegments.map((segment) => {
398
+ const item = byId.get(segment.segment_id);
399
+ if (!item) return segment;
400
+ return {
401
+ ...segment,
402
+ description: item.description,
403
+ subjects: Array.isArray(item.subjects) ? item.subjects : [],
404
+ scene: stringOrDefault(item.scene, ''),
405
+ motion: stringOrDefault(item.motion, ''),
406
+ semantic_tags: Array.isArray(item.semantic_tags) ? item.semantic_tags : [],
407
+ quality_score: isFiniteNumber(item.quality_score) ? Number(item.quality_score) : null,
408
+ visual_review_source: path.resolve(reviewPath),
409
+ agent_review_required: false,
410
+ };
411
+ });
412
+ const payload = {
413
+ version: 1,
414
+ source: path.resolve(reviewPath),
415
+ imported_at: new Date().toISOString(),
416
+ reviewed_count: review.reviews.length,
417
+ reviews: review.reviews,
418
+ };
419
+ await writeJson(path.join(project.work_dir, VISUAL_UNDERSTANDING_FILE), payload);
420
+ await writeJson(path.join(project.work_dir, VISUAL_SEGMENTS_FILE), reviewedSegments);
421
+ return {
422
+ ok: true,
423
+ action: 'video.visual-review',
424
+ project: projectSummary(project),
425
+ review_validation: reviewValidation,
426
+ visual_understanding_path: path.join(project.work_dir, VISUAL_UNDERSTANDING_FILE),
427
+ visual_segments_path: path.join(project.work_dir, VISUAL_SEGMENTS_FILE),
428
+ reviewed_segments: reviewedSegments.filter((segment) => byId.has(segment.segment_id)),
429
+ };
430
+ }
431
+
313
432
  async function planFromAgentEdl(options) {
314
433
  const project = await readProject(options);
315
434
  const timelinePlanSource = normalizeOptionalPath(options.named?.['timeline-plan']);
@@ -334,9 +453,9 @@ async function planFromAgentEdl(options) {
334
453
  action: 'video.plan',
335
454
  project: projectSummary(project),
336
455
  agent_action_required: true,
337
- required_inputs: ['strategy_snapshot.json', 'beats.json', 'visual_segments.json', '用户剪辑 brief'],
456
+ required_inputs: ['strategy_snapshot.json', 'audio_alignment.json 或 beats.json', 'visual_segments.json', 'visual_understanding.json', '用户剪辑 brief'],
338
457
  expected_outputs: [path.join(project.work_dir, TIMELINE_PLAN_FILE), path.join(project.work_dir, EDL_FILE)],
339
- message: '由 Agent 读取规则库策略快照、beats 1秒1帧抽帧结果后生成 timeline_plan;timeline_plan 内可包含 edl,或再用 --edl 单独传入。',
458
+ message: '由 Agent 读取规则库策略快照、音频对齐结果、beats、视觉理解结果和 1秒1帧抽帧结果后生成 timeline_plan;timeline_plan 内可包含 edl,或再用 --edl 单独传入。',
340
459
  };
341
460
  }
342
461
  const edl = edlSource ? JSON.parse(await fs.readFile(edlSource, 'utf8')) : timelinePlan.edl;
@@ -480,6 +599,36 @@ function validateTimelinePlan(plan, beatsPayload, visualSegments) {
480
599
  return { ok: errors.length === 0, errors, warnings };
481
600
  }
482
601
 
602
+ function validateVisualReview(review, visualSegments) {
603
+ const errors = [];
604
+ const warnings = [];
605
+ if (!review || typeof review !== 'object' || Array.isArray(review)) {
606
+ return { ok: false, errors: ['visual_review 必须是 JSON 对象。'], warnings };
607
+ }
608
+ if (!Array.isArray(review.reviews) || review.reviews.length === 0) {
609
+ errors.push('visual_review.reviews 必须是非空数组。');
610
+ }
611
+ const knownSegmentIds = new Set((visualSegments || []).map((segment) => segment.segment_id));
612
+ const seen = new Set();
613
+ for (const [index, item] of (review.reviews || []).entries()) {
614
+ const label = `reviews[${index}]`;
615
+ if (!item.segment_id) errors.push(`${label}.segment_id 不能为空。`);
616
+ if (item.segment_id && !knownSegmentIds.has(item.segment_id)) {
617
+ errors.push(`${label}.segment_id 未出现在 visual_segments.json 中: ${item.segment_id}`);
618
+ }
619
+ if (item.segment_id && seen.has(item.segment_id)) {
620
+ errors.push(`${label}.segment_id 重复: ${item.segment_id}`);
621
+ }
622
+ if (item.segment_id) seen.add(item.segment_id);
623
+ if (!item.description) errors.push(`${label}.description 不能为空,必须说明画面内容。`);
624
+ if (!Array.isArray(item.subjects)) warnings.push(`${label}.subjects 建议使用数组。`);
625
+ if (item.quality_score !== undefined && (!isFiniteNumber(item.quality_score) || Number(item.quality_score) < 0 || Number(item.quality_score) > 1)) {
626
+ errors.push(`${label}.quality_score 必须是 0 到 1 之间的数字。`);
627
+ }
628
+ }
629
+ return { ok: errors.length === 0, errors, warnings };
630
+ }
631
+
483
632
  function buildRenderCommands(project, edl, assets, output, { final }) {
484
633
  const byId = new Map(assets.map((asset) => [asset.asset_id, asset]));
485
634
  const renderDir = path.join(project.work_dir, 'renders');
@@ -594,6 +743,29 @@ async function readScriptInput(options) {
594
743
  return '';
595
744
  }
596
745
 
746
+ function extractAlignmentSegments(payload) {
747
+ const candidates = [
748
+ payload?.segments,
749
+ payload?.data?.segments,
750
+ payload?.result?.segments,
751
+ payload?.data?.result?.segments,
752
+ payload?.response?.segments,
753
+ payload?.words,
754
+ payload?.data?.words,
755
+ payload?.result?.words,
756
+ ];
757
+ const found = candidates.find((item) => Array.isArray(item));
758
+ return (found || [])
759
+ .map((item) => {
760
+ const text = stringOrDefault(item.text || item.word || item.sentence, '').trim();
761
+ const start = firstFiniteNumber(item.start_s, item.start, item.begin_s, item.begin, item.from);
762
+ const end = firstFiniteNumber(item.end_s, item.end, item.stop_s, item.stop, item.to);
763
+ return { text, start_s: start, end_s: end };
764
+ })
765
+ .filter((item) => item.text && isFiniteNumber(item.start_s) && isFiniteNumber(item.end_s) && Number(item.end_s) > Number(item.start_s))
766
+ .map((item) => ({ text: item.text, start_s: Number(item.start_s), end_s: Number(item.end_s) }));
767
+ }
768
+
597
769
  function splitScriptToBeats(script) {
598
770
  return String(script)
599
771
  .replace(/\r/g, '\n')
@@ -755,6 +927,13 @@ function isFiniteNumber(value) {
755
927
  return Number.isFinite(Number(value));
756
928
  }
757
929
 
930
+ function firstFiniteNumber(...values) {
931
+ for (const value of values) {
932
+ if (isFiniteNumber(value)) return Number(value);
933
+ }
934
+ return null;
935
+ }
936
+
758
937
  function projectSummary(project) {
759
938
  return {
760
939
  project_id: project.project_id,