@oh-story/dsh 0.1.13 → 0.1.15

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 (58) hide show
  1. package/README.md +7 -7
  2. package/lib/client.js +932 -631
  3. package/lib/client.js.map +4 -4
  4. package/lib/index.js +1 -0
  5. package/lib/index.js.map +2 -2
  6. package/lib/oh-story/manifest.json +72 -72
  7. package/lib/oh-story/skills/story/SKILL.md +1 -1
  8. package/lib/oh-story/skills/story/VERSION +1 -1
  9. package/lib/oh-story/skills/story/references/author-memory-maintenance.md +2 -2
  10. package/lib/oh-story/skills/story/references/author-memory.md +4 -4
  11. package/lib/oh-story/skills/story/scripts/author_memory_commit.py +61 -58
  12. package/lib/oh-story/skills/story-deslop/SKILL.md +1 -1
  13. package/lib/oh-story/skills/story-deslop/references/agent-calls.md +1 -1
  14. package/lib/oh-story/skills/story-deslop/references/author-memory-maintenance.md +2 -2
  15. package/lib/oh-story/skills/story-deslop/references/author-memory.md +4 -4
  16. package/lib/oh-story/skills/story-deslop/scripts/author_memory_commit.py +61 -58
  17. package/lib/oh-story/skills/story-long-scan/SKILL.md +2 -7
  18. package/lib/oh-story/skills/story-long-scan/references/platform-qidian.md +11 -1
  19. package/lib/oh-story/skills/story-long-scan/references/platform-qimao.md +12 -2
  20. package/lib/oh-story/skills/story-long-write/SKILL.md +1 -1
  21. package/lib/oh-story/skills/story-long-write/references/agent-calls.md +2 -2
  22. package/lib/oh-story/skills/story-long-write/references/author-memory-maintenance.md +2 -2
  23. package/lib/oh-story/skills/story-long-write/references/author-memory.md +4 -4
  24. package/lib/oh-story/skills/story-long-write/references/workflow-chapter.md +1 -0
  25. package/lib/oh-story/skills/story-long-write/references/workflow-daily.md +2 -1
  26. package/lib/oh-story/skills/story-long-write/references/writer-prompt-fallback.md +1 -1
  27. package/lib/oh-story/skills/story-long-write/scripts/author_memory_commit.py +61 -58
  28. package/lib/oh-story/skills/story-long-write/scripts/build_writer_prompt.py +87 -20
  29. package/lib/oh-story/skills/story-review/SKILL.md +1 -1
  30. package/lib/oh-story/skills/story-review/references/author-memory-maintenance.md +2 -2
  31. package/lib/oh-story/skills/story-review/references/author-memory.md +4 -4
  32. package/lib/oh-story/skills/story-review/scripts/author_memory_commit.py +61 -58
  33. package/lib/oh-story/skills/story-short-write/SKILL.md +1 -1
  34. package/lib/oh-story/skills/story-short-write/references/agent-calls.md +2 -2
  35. package/lib/oh-story/skills/story-short-write/references/author-memory-maintenance.md +2 -2
  36. package/lib/oh-story/skills/story-short-write/references/author-memory.md +4 -4
  37. package/lib/oh-story/skills/story-short-write/scripts/author_memory_commit.py +61 -58
  38. package/lib/types/client/canvas-viewport.d.ts +31 -0
  39. package/lib/types/client/canvas-viewport.d.ts.map +1 -0
  40. package/lib/types/client/drama-production-view.d.ts +3 -2
  41. package/lib/types/client/drama-production-view.d.ts.map +1 -1
  42. package/lib/types/client/index.d.ts.map +1 -1
  43. package/lib/types/client/production-canvas.d.ts +24 -0
  44. package/lib/types/client/production-canvas.d.ts.map +1 -0
  45. package/lib/types/skill-provider.d.ts.map +1 -1
  46. package/lib/video-recap/CHANGELOG.md +16 -1
  47. package/lib/video-recap/README.en.md +2 -2
  48. package/lib/video-recap/README.md +2 -2
  49. package/lib/video-recap/manifest.json +25 -25
  50. package/lib/video-recap/skills/video-recap/SKILL.md +4 -2
  51. package/lib/video-recap/skills/video-recap/references/config-playbook.md +2 -2
  52. package/lib/video-recap/skills/video-recap/scripts/recap_cli.py +6 -0
  53. package/lib/video-recap/skills/video-recap/scripts/recap_runner.py +10 -1
  54. package/lib/video-recap/skills/video-script/SKILL.md +45 -18
  55. package/lib/video-recap/skills/video-script/scripts/review_runner.py +11 -2
  56. package/lib/video-recap/skills/video-voiceover/SKILL.md +34 -8
  57. package/lib/video-recap/skills/video-voiceover/scripts/dub.py +21 -0
  58. package/package.json +1 -1
@@ -5,6 +5,9 @@ description: >
5
5
  宣发标题、花字修订和外部文案回填。普通策划输入 work_dir 的 agent_narration_brief.md 与
6
6
  vlm_analysis.json;文案返修输入当前成片的工程与内容证据。策划输出 recap_story_plan.json、visual_audio_board.json、
7
7
  可选 style_card.json、cut 模式需要的 clip_plan.json,以及通过校验的 narration.json;仅宣发文案任务交付提案或回填既有包装计划。
8
+ 外发说明:只有建议型评审 review.py 联网,它把旁白稿全文与理解证据、策划文件的文字摘录发到 MiMo chat 接口
9
+ (MIMO_API_KEY / MIMO_API_URL,不发视频、图片或音频);单独使用时只在显式执行时运行,端到端编排默认在 TTS 前运行一次,
10
+ 可用 --no-review-narration / REVIEW_NARRATION=0 关闭(严格评审开启时除外);validate.py 与 lint 仅在本地运行。
8
11
  触发词:解说词、写解说、视频旁白、宣发标题、花字修订、文案回填、
9
12
  narration script、写稿、解说文案、剪辑思路、导演思路。
10
13
  ---
@@ -37,7 +40,30 @@ Agent 先记录简洁决定,再写时间线产物。`validate.py` 负责对理
37
40
 
38
41
  REVISION 先明确本轮修改项与冻结项,再编辑对应层:表达、口语节奏、字幕反馈更新 `style_card.json`;镜头、入出点、表演和声音分工更新 `visual_audio_board.json`;只有观众承诺、POV、主线或 beat 改变时才更新 `recap_story_plan.json`。被删除的镜头、原声或文案也要从相关计划中删除,不能保留过期锚点。不要把看片修改重新做成一次 CREATE。
39
42
 
40
- ## 2. 读取素材并确认状态
43
+ ## 2. 远程调用与数据外发
44
+
45
+ 本技能只有一个脚本会联网:`review.py`(§7.1 的建议型语义评审)。其余全部在本地运行:`validate.py` 及其 lint、去 AI 味检查只读写 `work_dir` 里的文件,不发任何网络请求;策划与写稿由 Agent 直接读写 `work_dir` 完成。
46
+
47
+ `review.py` 发给谁:
48
+
49
+ - MiMo 的 OpenAI 兼容 chat 接口 `<MIMO_API_URL>/chat/completions`,用 `MIMO_API_KEY` 认证,模型为 `MIMO_MODEL`(默认 `mimo-v2.5`)。未设 `MIMO_API_URL` 时,按量计费 key 发往 `https://api.xiaomimimo.com/v1`,`tp-` 开头的 Token Plan key 发往 `MIMO_TOKEN_PLAN_CLUSTER` 选定的 token-plan 集群(默认 `https://token-plan-cn.xiaomimimo.com/v1`)。
50
+ - 未设 `MIMO_API_KEY` 时直接退出,不构造也不发送任何请求。
51
+
52
+ `review.py` 发送什么(只有文字,不发视频、帧图片或音频文件):
53
+
54
+ - `narration.json` 全部旁白段的时间与文字。
55
+ - 理解索引的文字证据:开头、中段、结尾、长空档与各旁白时间窗附近选出的时间段内,`vlm_analysis.json` 的场景描述与 `frame_facts` 文字,以及对白转写(有 `asr_clean.json` 时用它,否则 `asr_result.json`)。
56
+ - `background_research.json` 的梗概、角色、关系与剧情线摘录。
57
+ - 存在时附上 `packaging_plan.json`、`recap_story_plan.json`、`visual_audio_board.json`、`style_card.json`(各截前 3000 字)和 `original_subtitles.json` 的原声字幕文字。
58
+ - 内容较长时分块,每块一次请求;返回的意见只写回本地 `narration_review.json` / `narration_review.md`。
59
+
60
+ 何时运行、怎样关闭:
61
+
62
+ - 单独使用本技能时,`review.py` 只在用户或 Agent 显式执行它时运行;不执行它就没有任何外发。用户不希望稿件离开本机时,不要运行它,改为人工复核后直接跑 `validate.py`。
63
+ - 端到端编排默认在 TTS 前自动调用一次这项评审,失败不阻断;关闭方式是编排入口的 `--no-review-narration` 或环境变量 `REVIEW_NARRATION=0`。若同时开启了严格评审(`--require-narration-review` 或 `REQUIRE_NARRATION_REVIEW=1`),评审必须运行,关闭开关不生效。
64
+ - 评审是建议型的:只写报告,从不改写 `narration.json`;是否修改由 Agent 或用户决定。
65
+
66
+ ## 3. 读取素材并确认状态
41
67
 
42
68
  首先阅读:
43
69
 
@@ -46,7 +72,7 @@ REVISION 先明确本轮修改项与冻结项,再编辑对应层:表达、
46
72
  - `timeline_fusion.json`:判断某段是否有对白或静音槽。
47
73
  - `vlm_analysis.json` / `asr_result.json`:核对具体画面与原声证据;有 `asr_clean.json` 时以它的文本为准(lint、评审、剪辑和合成都读它)。
48
74
  - brief 顶部列出的 contact sheet:不要只依赖场景摘要;反应、走位、静止和台词前后的具体时刻常常更重要。
49
- - `production_reference.json`(仅当 `work_dir` 里有):另一部成片拆出的可迁移方法与节奏数值,用法见 §3。
75
+ - `production_reference.json`(仅当 `work_dir` 里有):另一部成片拆出的可迁移方法与节奏数值,用法见 §4。
50
76
 
51
77
  full 模式使用原片时间。cut 模式第一阶段只写 `clip_plan.json`;`edited_source.mp4` 产生后,第二阶段才按输出时间写 `narration.json`。
52
78
 
@@ -59,7 +85,7 @@ full 模式使用原片时间。cut 模式第一阶段只写 `clip_plan.json`;
59
85
 
60
86
  必须确认旁白没有跨越错误剪辑边界,也没有落进已删除区间。整个判断只依赖 `work_dir` 产物。
61
87
 
62
- ## 3. 制定创作方案
88
+ ## 4. 制定创作方案
63
89
 
64
90
  先阅读 `references/creative-editing-playbook.md`,再按创作控制模式写或更新工作产物:
65
91
 
@@ -71,7 +97,7 @@ full 模式使用原片时间。cut 模式第一阶段只写 `clip_plan.json`;
71
97
 
72
98
  若 `work_dir` 有 `production_reference.json`,制定方案前先读它。它来自另一部成片,只含可迁移的方法和测得的节奏,不含本片事实,不能作为本片画面、剧情或台词的证据。优先级:用户指令 > 本片证据 > 参考。CREATE 可把它的 `structure` 当作一个候选假设,与素材自生的假设比较;DIRECTED / REVISION 默认不套用。`targets` 是参考值,不是配额:与本片的 `audio_owner`、完整台词或表演冲突时以素材为准。可在 `recap_story_plan.json` 写可选字段 `reference_methods: [{"id": "m1", "decision": "adopt|adapt|skip", "note": "…"}]`。没有这个文件就跳过本段。
73
99
 
74
- ### 3.1 导演判断
100
+ ### 4.1 导演判断
75
101
 
76
102
  锁定:
77
103
 
@@ -82,11 +108,11 @@ full 模式使用原片时间。cut 模式第一阶段只写 `clip_plan.json`;
82
108
  - 隐瞒与揭示
83
109
  - 结尾余味
84
110
 
85
- ### 3.2 故事编辑
111
+ ### 4.2 故事编辑
86
112
 
87
113
  CREATE 比较两个真正可行的结构后选择一个;DIRECTED / REVISION 沿用用户指定或已确认的结构,除非最新反馈明确改变故事方向。每个 beat 至少改变一项:知识、权力、目标、关系、情绪或风险。若删除后因果、人物和情绪都没有损失,该 beat 通常不应保留。
88
114
 
89
- ### 3.3 画面剪辑
115
+ ### 4.3 画面剪辑
90
116
 
91
117
  选择具体时刻,而不是只选择事件。比较:
92
118
 
@@ -97,7 +123,7 @@ CREATE 比较两个真正可行的结构后选择一个;DIRECTED / REVISION
97
123
 
98
124
  在不破坏理解的前提下晚进早出,同时保留不可替代的表演、停顿、失误、动作声和完整台词。
99
125
 
100
- ### 3.4 声音与旁白分工
126
+ ### 4.4 声音与旁白分工
101
127
 
102
128
  先指定 `audio_owner`,再写字。旁白只允许承担以下 `narration_job`:
103
129
 
@@ -110,7 +136,7 @@ CREATE 比较两个真正可行的结构后选择一个;DIRECTED / REVISION
110
136
 
111
137
  画面、原声或沉默已经足够时使用 `none`,不要默认铺旁白。
112
138
 
113
- ### 3.5 cut 模式第一阶段
139
+ ### 4.5 cut 模式第一阶段
114
140
 
115
141
  cut 模式先根据 `recap_story_plan.json` 与 `visual_audio_board.json` 写原片时间的 `clip_plan.json`,此时不要写 `narration.json`:
116
142
 
@@ -135,7 +161,7 @@ beat_id | function | change | POV | preferred moment | 入点 | 出点
135
161
 
136
162
  片段顺序必须构成一条完整故事线,而不是无序高光。可使用 0–1 个 cold open,随后回到因果清楚的 setup → turn → escalation → payoff。片段长度服从具体时刻,不使用统一秒数模板;片尾必须保留完整台词或动作。密集 scene-change 候选的来源判断与处理规则由剪辑阶段定义,写计划时遵循同一规则,不制造人工闪切。
137
163
 
138
- ## 4. 撰写旁白
164
+ ## 5. 撰写旁白
139
165
 
140
166
  full 模式直接按原片时间写;cut 第二阶段先查看 `edited_source.mp4` 与剪后故事板,补充 `visual_audio_board.json` 的输出时间并重新确认 `audio_owner` / `narration_job`,再按输出时间写:
141
167
 
@@ -162,7 +188,7 @@ full 模式直接按原片时间写;cut 第二阶段先查看 `edited_source.m
162
188
  | `overlaps_speech` | 是否与原对白重叠;连续铺底窗口通常为 `true`,真正静音槽才为 `false` |
163
189
  | `emotion` | 整个解说块的 MiMo TTS 情绪/语气标签 |
164
190
 
165
- ### 4.1 写作规则
191
+ ### 5.1 写作规则
166
192
 
167
193
  1. **先有 `narration_job`,后有句子**:没有明确任务就不写;旁白不是默认音轨。
168
194
  2. **按连续思路写**:旁白拥有一个 beat 时,用一个或少量完整句子完成“前提 → 触发动作 → 变化/意义”,并在一次 TTS 中合成。句号服从口语思路和呼吸,不服从字幕换行;不要固定句数,也不要“一句一停”。
@@ -174,7 +200,7 @@ full 模式直接按原片时间写;cut 第二阶段先查看 `edited_source.m
174
200
  8. **写给耳朵听**:使用具体名词和动词,句子完整、口语可听;避免字幕腔、半句、空泛拔高和破折号。TTS 文本先保证听感连续,字幕再按阅读宽度拆分,不能反过来把朗读稿切碎。
175
201
  9. **避免模板化纠偏**:“不是 A,而是 B”只在确实存在一个观众可能相信、而素材又要纠正的判断时使用。它不是禁句,但不能靠先否定再肯定制造假洞察;优先直接写人物的动作、因果和后果。
176
202
 
177
- ### 4.2 解说结构
203
+ ### 5.2 解说结构
178
204
 
179
205
  - **钩子**:提出正文会真实兑现的问题或利害,不用无关留存话术。
180
206
  - **主线**:围绕选定 POV 与主线推进,不在每个场景重新开篇。
@@ -183,7 +209,7 @@ full 模式直接按原片时间写;cut 第二阶段先查看 `edited_source.m
183
209
  - **收尾**:回答或有意转化开头问题,留下明确余味。
184
210
  - **衔接**:旁白块与相邻原声属于同一个 beat,前者铺垫、后者呈现、下一块承接。
185
211
 
186
- ### 4.3 原声留白字幕
212
+ ### 5.3 原声留白字幕
187
213
 
188
214
  可选写 `original_subtitles.json`,使用成片输出时间:
189
215
 
@@ -193,7 +219,7 @@ full 模式直接按原片时间写;cut 第二阶段先查看 `edited_source.m
193
219
 
194
220
  只写留白中实际听得到的台词,订正 ASR 错字与人名,每条尽量控制在一行;被旁白盖住或已经剪掉的句子不要写。省略时,合成阶段会使用保守的 ASR 映射兜底,并在成片中用 `「」` 区分原声对白与旁白。
195
221
 
196
- ## 5. 创作自审
222
+ ## 6. 创作自审
197
223
 
198
224
  在调用 LLM 评审前做以下**反事实检查**:
199
225
 
@@ -209,12 +235,12 @@ full 模式直接按原片时间写;cut 第二阶段先查看 `edited_source.m
209
235
 
210
236
  REVISION 还要逐项确认:用户点名的问题已经改变,未点名的冻结项没有意外变化,相关 `style_card.json` / `visual_audio_board.json` 中不存在旧镜头或旧表达。除非用户要求备选版本,不额外扩展新方向。
211
237
 
212
- ## 6. 评审与校验
238
+ ## 7. 评审与校验
213
239
 
214
- ### 6.1 建议型语义评审
240
+ ### 7.1 建议型语义评审
215
241
 
216
242
  ```bash
217
- python3 scripts/review.py --work-dir <work_dir>
243
+ python3 scripts/review.py --work-dir <work_dir> # 会联网:外发内容与关闭方式见 §2
218
244
  ```
219
245
 
220
246
  评审会自动识别 cut 模式,并在存在已校验剪辑计划时按输出时间线核对;`--timeline source` 可强制使用原片时间。打开 `narration_review.md`,逐项处理 `error`,尤其是 `category=hallucination`。
@@ -236,7 +262,7 @@ python3 scripts/review.py --work-dir <work_dir>
236
262
 
237
263
  `review.py` 本身只写报告,默认调用策略为建议型、失败开放;若调用方显式开启严格评审,事实矛盾、残句、解析失败或评审不可用可在 TTS 前阻断。覆盖记录只用于审计,`review.py` / `validate.py` 不读取它。
238
264
 
239
- ### 6.2 确定性硬校验
265
+ ### 7.2 确定性硬校验
240
266
 
241
267
  ```bash
242
268
  python3 scripts/validate.py --work-dir <work_dir> --mode full
@@ -247,7 +273,8 @@ python3 scripts/validate.py --work-dir <work_dir> --mode full
247
273
 
248
274
  片名或题材明确但缺少剧情上下文时,先按本技能的 `references/research-guide.md` 写 `background_research.json`。若理解素材偏薄,brief 中的数量只能当上限:宁可少写、写实,也不要为凑数复述画面。
249
275
 
250
- ## 7. 能力边界
276
+ ## 8. 能力边界
251
277
 
252
278
  - 不运行 ASR / VLM,不合成 TTS,不渲染视频;只消费视频理解索引。
279
+ - 唯一的远程调用是 `review.py` 的 MiMo 文字评审(见 §2);`validate.py` 与 lint 只在本地运行。
253
280
  - 平台研究仅用于明确的宣发任务;不替代当前片内事实,也不默认改变解说和剪辑。
@@ -1,4 +1,8 @@
1
- """Run narration review and write narration_review.json / .md."""
1
+ """Run narration review and write narration_review.json / .md.
2
+
3
+ Network: sends the narration text plus text excerpts of VLM/ASR evidence, research and planning
4
+ files to the MiMo chat endpoint (MIMO_API_KEY / MIMO_API_URL); no video, frames or audio.
5
+ """
2
6
 
3
7
  import argparse
4
8
 
@@ -142,7 +146,8 @@ def _auto_timeline(work_dir):
142
146
 
143
147
  def main():
144
148
  ap = argparse.ArgumentParser(
145
- description="Review an agent-written narration.json for quality (LLM-as-judge)."
149
+ description=("Review an agent-written narration.json for quality (LLM-as-judge). Sends the narration "
150
+ "text and text excerpts of the evidence to the MiMo chat endpoint (MIMO_API_KEY); no media.")
146
151
  )
147
152
  ap.add_argument("--work-dir", required=True)
148
153
  ap.add_argument(
@@ -159,6 +164,10 @@ def main():
159
164
  help="block instead of advisory fail-open when required cut-output evidence mapping is missing/stale",
160
165
  )
161
166
  args = ap.parse_args()
167
+ if not CONFIG["api_key"]:
168
+ # The review sends narration and evidence text to MiMo; without a key it would only
169
+ # post that text to be rejected, so stop before building any request.
170
+ raise SystemExit("review.py 需要 MIMO_API_KEY:未设置,评审未运行,也未发送任何内容")
162
171
  timeline = args.timeline or _auto_timeline(args.work_dir)
163
172
  if args.timeline is None and timeline != "source":
164
173
  log(f"评审 grounding 时间轴自动判定为 {timeline}(检测到已校验的剪辑产物)")
@@ -5,6 +5,9 @@ description: >
5
5
  把带时间戳的 narration.json 合成为中文解说音频。使用 MiMo TTS(mimo-v2.5-tts)或
6
6
  Fish Audio(s2.1-pro-free)或显式配置的通用 IndexTTS HTTP 服务逐段生成语音,
7
7
  按时间窗动态适配语速并处理响度;输入输出时间线上的旁白,产出 tts_segments 与 tts_meta.json。
8
+ 外发说明:每段旁白文字会发给所选 TTS 服务(MiMo / Fish Audio / 用户自托管的 IndexTTS),--voice-ref 的参考音频会发给 MiMo。
9
+ 另含实验性的英译中 dub 路径:只在显式选择 dub 模式并传 --confirm-voice-rights 时运行,会把源视频音频发到 MiMo ASR,
10
+ 并以原说话人的声音为参考经 MiMo voiceclone 克隆配音;只可用于用户有权使用、且说话人同意被克隆声音的内容。
8
11
  触发词:配音、语音合成、TTS、解说配音、
9
12
  voiceover、text to speech、旁白配音。
10
13
  ---
@@ -14,7 +17,21 @@ description: >
14
17
  本技能读取带时间戳的旁白稿,为每一段生成独立音频,并把语音适配到对应时间窗,随后记录下游合成所需的放置元数据。
15
18
  默认引擎是 MiMo TTS(`mimo-v2.5-tts`);也可显式选择 Fish Audio(默认模型 `s2.1-pro-free`)。
16
19
 
17
- ## 2. 环境要求
20
+ ## 2. 远程服务与数据外发
21
+
22
+ 本技能只在下表中被选中的路径上联网,每条路径发出的内容如下。实读文本指 `narration.json` 段落去掉格式与舞台提示后的文字。
23
+
24
+ | 路径 | 服务与凭据 | 发送内容 |
25
+ |------|------------|----------|
26
+ | 默认解说 TTS(`--tts-provider auto\|mimo-tts`) | MiMo chat 接口 `<MIMO_TTS_API_URL 或 MIMO_API_URL>/chat/completions`,`MIMO_TTS_API_KEY` 或 `MIMO_API_KEY`,模型 `mimo-v2.5-tts` | 每段实读文本、一句自然语言语气/语速指令(由段的 `emotion` 与时间窗算出)、内置音色名(默认 `冰糖`);`auto` 下 MiMo key 缺失且设置了 `FISH_API_KEY` 时改走 Fish Audio 行 |
27
+ | 解说声音克隆(`--voice-ref <audio>` / `VOICE_REF`) | 同上,模型 `mimo-v2.5-tts-voiceclone` | 上述内容,外加参考音频(转成 24 kHz 单声道 WAV,最长 30 秒)的 base64,每段请求都带 |
28
+ | Fish Audio(`--tts-provider fish-audio`) | `FISH_TTS_API_URL`(默认 `https://api.fish.audio/v1/tts`),`FISH_API_KEY` | 每段实读文本、数值语速、音色 ID `FISH_TTS_REFERENCE_ID`;不发送本地音频 |
29
+ | 自托管 IndexTTS(`--tts-provider index-tts`) | 用户自己部署、由 `INDEX_TTS_ENDPOINT` 指定的 HTTP(S) 服务 | `{"voice": INDEX_TTS_VOICE, "text": 实读文本}`;不发送本地音频 |
30
+ | 实验性 dub(见 §8) | MiMo ASR(`MIMO_API_URL`,`MIMO_API_KEY`,`mimo-v2.5-asr`)与 MiMo voiceclone(TTS 接口与凭据,`mimo-v2.5-tts-voiceclone`) | 源视频整条音轨按 6 秒分窗送 ASR;每句中文译文连同从源音频截取的约 10 秒原说话人声音(克隆参考)送 voiceclone |
31
+
32
+ `--voice-ref` 与 dub 都会把一个真实人物的声音发给 MiMo 用于克隆:只在用户有权使用该音频、且声音主人同意被克隆时使用。
33
+
34
+ ## 3. 环境要求
18
35
 
19
36
  ```bash
20
37
  export MIMO_API_KEY=*** # 也可使用仅供 TTS 的 MIMO_TTS_API_KEY
@@ -30,14 +47,14 @@ export TTS_PROVIDER=index-tts
30
47
 
31
48
  下面的 `scripts/...` 均相对于本技能目录。若执行器从仓库根目录启动,请给脚本路径加上本技能的绝对目录。
32
49
 
33
- ## 3. 输入契约
50
+ ## 4. 输入契约
34
51
 
35
52
  默认输入为 `work_dir/narration.json`。每段必须包含 `start`、`end` 与 `narration`,可选字段包括
36
53
  `pause_after_ms` 和 `overlaps_speech`。时间统一表示音频最终放置的**输出时间线秒数**。
37
54
 
38
55
  cut 流程先剪后配:`narration.json` 本身就是按剪后成片的输出时间写的,不存在另一份映射稿。
39
56
 
40
- ## 4. 运行命令
57
+ ## 5. 运行命令
41
58
 
42
59
  ```bash
43
60
  python3 scripts/voiceover.py --work-dir <work_dir> --narration <narration.json> \
@@ -48,7 +65,7 @@ python3 scripts/voiceover.py --work-dir <work_dir> --narration <narration.json>
48
65
 
49
66
  单独运行且省略 `--narration` 时,默认读取 `work_dir/narration.json`;`--narration` 只用于指定其他路径的同格式稿件。
50
67
 
51
- ## 5. 输出契约
68
+ ## 6. 输出契约
52
69
 
53
70
  - `tts_segments/*.wav`:每段旁白对应一个音频文件。
54
71
  - `tts_meta.json`:包含 `segments`、`engine`、`voice`(实际使用的 provider、模型、音色或参考音频)与 `narration`。每段记录 `audio_path`、时间、
@@ -63,7 +80,7 @@ python3 scripts/voiceover.py --work-dir <work_dir> --narration <narration.json>
63
80
  `--allow-partial-tts` 降级为可交付的部分成功;异常记录标为 `required: true` 并带策略 ID。
64
81
  仅含 `[停顿]` 等清理标记、清理后无实读文本的作者段也属于必需段错误。
65
82
 
66
- ## 6. 运行规则
83
+ ## 7. 运行规则
67
84
 
68
85
  - 分段音频按内容缓存在 `tts_segments/cache/`:键是实读文本、实际发给供应商的语气请求(MiMo 是那句自然语言指令,语速只在 ≥+6% 或 ≤-3% 时改变措辞;Fish Audio 是数值 speed;index-tts 没有段级控制)与 TTS 设置,不含段序号和时间窗;因此段位变化让名义语速从 +5% 变成 -2% 时,MiMo 不重新合成;
69
86
  缓存 WAV 的 `size`/`mtime_ns` 变了即失效。`narr_NNN.wav` 是指向缓存的硬链接(不支持时为副本),`tts_meta.json`
@@ -87,10 +104,19 @@ python3 scripts/voiceover.py --work-dir <work_dir> --narration <narration.json>
87
104
  dub 的 voiceclone 台词走同一道检查与重试(被拒的留在 `dub_tts/line_NNN_raw.rejected.wav`)。
88
105
  旧版本缓存下的这类 WAV 在重跑时不再复用,会重新合成。设为 `0` 关闭这道检查。
89
106
  - `TTS_WORKERS`、`TTS_TIMEOUT`、`TTS_RETRIES`、`ALLOW_PARTIAL_TTS` 用于调整并发、超时、重试与部分成功策略。
90
- - dub 模式有独立的确定性门禁:`dub.py --stage render` 在语音克隆前写 `dub_lint.json`,
91
- 空行、重叠或越界译文即中止。`dub.py` 只由编排入口的 `--edit-mode dub` 调用,没有单独的手动阶段。
92
107
 
93
- ## 7. 能力边界
108
+ ## 8. 实验性 dub 配音(英译中、克隆原声)
109
+
110
+ `dub.py` 是实验功能:把英文原声翻译成中文,并用原说话人的克隆音色整轨替换人声。它与上面的解说配音是两条独立路径,`voiceover.py` 从不调用它。
111
+
112
+ - **触发**:只在用户明确要求英译中配音时,由编排入口以 `--edit-mode dub --confirm-voice-rights` 运行;编排入口把确认参数原样转给 `dub.py` 的准备和渲染两个阶段。没有单独的手动阶段。
113
+ - **确认门禁**:`dub.py` 的两个阶段都必须带 `--confirm-voice-rights`,缺少时在抽取音频和发出任何请求之前退出,并说明会外发什么。编排入口在 dub 模式下同样拒绝缺少该参数的运行,在其他模式下拒绝该参数。
114
+ - **外发内容**:准备阶段把源视频音轨按 6 秒分窗发给 MiMo ASR(`mimo-v2.5-asr`)转写英文;渲染阶段把每句中文译文和从源音频第 2 秒起截取的约 10 秒原说话人声音(`dub_reference.wav`)一起发给 MiMo voiceclone(`mimo-v2.5-tts-voiceclone`)。
115
+ - **权利与同意**:只能用于用户有权使用的视频与音频,且被克隆声音的说话人已同意。Agent 必须先向用户确认这两点,用户确认后才能加 `--confirm-voice-rights`;无法确认时不要运行 dub,也不要用它冒充他人发言。
116
+ - **确定性门禁**:渲染阶段在语音克隆前写 `dub_lint.json`,空行、重叠或越界译文即中止,不发 voiceclone 请求。
117
+ - **本地产物**:`dub_source.wav`、`dub_transcript.json`、`dub_brief.md`、`dub_reference.wav`、Agent 写的 `dub_script.json`、`dub_lint.json`、`dub_tts/`、`dub_manifest.json` 与 `dub_<name>.mp4`,都只写在 `work_dir`。
118
+
119
+ ## 9. 能力边界
94
120
 
95
121
  - 超窗时保留原稿并记录日志;assemble 有界提速放不下则在渲染前以 `no_safe_fit` 阻断。批准稿加 `--preserve-approved-text`,超窗即在 TTS 阶段失败。
96
122
  - 不混流、不压低原声、不渲染字幕。
@@ -5,6 +5,12 @@ faithful Chinese translation spoken in the ORIGINAL speaker's cloned voice
5
5
  (`mimo-v2.5-tts-voiceclone`, same MIMO_API_KEY, pure stdlib + ffmpeg, no GPU). This differs
6
6
  from recap/解说, which overlays Chinese commentary on ducked original audio.
7
7
 
8
+ Experimental, and only on explicit request: both stages refuse to run without
9
+ `--confirm-voice-rights`, the user's confirmation that they may use this video's audio and clone
10
+ its speaker's voice. Remote calls: `prepare` sends 6-second windows of the source audio to MiMo
11
+ ASR; `render` sends each Chinese line plus the ~10-second source-voice reference clip to MiMo
12
+ voiceclone.
13
+
8
14
  Division of labour (the same as recap): CODE does only the mechanical parts; the AGENT does all
9
15
  the judgment. So there are NO text heuristics here (no sentence-splitting, hook-dedup, or junk
10
16
  filters) — those are exactly the things an LLM does better, and trying to do them in code is
@@ -50,6 +56,14 @@ DUB_MIN_ASR_WINDOW_SECONDS = 0.5
50
56
  DUB_TTS_STYLE_PROMPT = "自然、清晰,保持原说话人的音色与节奏,语气平稳。"
51
57
 
52
58
  DUB_SCHEMA_VERSION = 1
59
+ # dub sends the source speech to MiMo ASR and uses the source speaker's own voice as the
60
+ # voiceclone reference, so neither stage runs until the caller passes the user's explicit
61
+ # confirmation. Checked before any audio extraction or network request (fail closed).
62
+ VOICE_RIGHTS_REQUIRED = (
63
+ "[dub] 需要显式确认 --confirm-voice-rights:dub 会把源视频的音频分窗发送到 MiMo ASR 转写,"
64
+ "并截取原说话人约 10 秒声音作为 MiMo voiceclone 的参考音频来克隆其音色。"
65
+ "只有当用户有权使用这段视频与音频、且说话人同意被克隆声音时才可确认;否则不要运行 dub。"
66
+ )
53
67
  # Tolerate ~1-frame rounding in agent-estimated timings so the gate blocks only GENUINE
54
68
  # errors, not rounding noise (the old render clamped such cases via min(slot_end, nxt)).
55
69
  DUB_TIMING_EPS = 0.05
@@ -536,7 +550,14 @@ def main():
536
550
  help="prepare writes the ASR brief; render lints dub_script.json before voiceclone")
537
551
  ap.add_argument("--video", required=True, help="source video")
538
552
  ap.add_argument("--work-dir", required=True, help="work directory containing dub artifacts")
553
+ ap.add_argument(
554
+ "--confirm-voice-rights",
555
+ action="store_true",
556
+ help="required: the user confirms they may use this video's audio and clone its speaker's voice",
557
+ )
539
558
  args = ap.parse_args()
559
+ if not args.confirm_voice_rights:
560
+ raise SystemExit(VOICE_RIGHTS_REQUIRED)
540
561
  video, work = Path(args.video), Path(args.work_dir)
541
562
  if args.stage == "prepare":
542
563
  stage_prepare(video, work)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oh-story/dsh",
3
- "version": "0.1.13",
3
+ "version": "0.1.15",
4
4
  "description": "A DSH plugin for fiction, short-drama, interactive-game, and video-recap production",
5
5
  "keywords": [
6
6
  "deepseek-harness",