@oh-story/dsh 0.1.7 → 0.1.9

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 (272) hide show
  1. package/README.md +30 -15
  2. package/lib/client.js +93 -16
  3. package/lib/client.js.map +2 -2
  4. package/lib/drama/manifest.json +165 -69
  5. package/lib/drama/skills/short-drama/SKILL.md +5 -1
  6. package/lib/drama/skills/short-drama/scripts/creator_markdown_check.py +126 -9
  7. package/lib/drama/skills/short-drama/scripts/project_tool.py +11 -0
  8. package/lib/drama/skills/short-drama-assets/references/character-and-look.md +18 -0
  9. package/lib/drama/skills/short-drama-assets/references/continuity-lock.md +10 -1
  10. package/lib/drama/skills/short-drama-edit/SKILL.md +148 -0
  11. package/lib/drama/skills/short-drama-edit/agents/openai.yaml +4 -0
  12. package/lib/drama/skills/short-drama-edit/assets/remotion/README.md +60 -0
  13. package/lib/drama/skills/short-drama-edit/assets/remotion/package.json +15 -0
  14. package/lib/drama/skills/short-drama-edit/assets/remotion/remotion.config.ts +6 -0
  15. package/lib/drama/skills/short-drama-edit/assets/remotion/src/Root.tsx +26 -0
  16. package/lib/drama/skills/short-drama-edit/assets/remotion/src/Subtitles.tsx +88 -0
  17. package/lib/drama/skills/short-drama-edit/assets/remotion/src/font.ts +46 -0
  18. package/lib/drama/skills/short-drama-edit/assets/remotion/src/index.ts +4 -0
  19. package/lib/drama/skills/short-drama-edit/assets/remotion/src/schema.ts +32 -0
  20. package/lib/drama/skills/short-drama-edit/assets/remotion/tsconfig.json +13 -0
  21. package/lib/drama/skills/short-drama-edit/assets//345/211/252/350/276/221/345/215/225.md +43 -0
  22. package/lib/drama/skills/short-drama-edit/references/cut-craft.md +81 -0
  23. package/lib/drama/skills/short-drama-edit/references/delivery-verify.md +58 -0
  24. package/lib/drama/skills/short-drama-edit/references/generated-footage.md +96 -0
  25. package/lib/drama/skills/short-drama-edit/references/sound-and-subtitles.md +113 -0
  26. package/lib/drama/skills/short-drama-edit/references/stage-contract.md +55 -0
  27. package/lib/drama/skills/short-drama-edit/scripts/edit_tool.py +1041 -0
  28. package/lib/drama/skills/short-drama-edit/scripts/selftest.py +456 -0
  29. package/lib/drama/skills/short-drama-image-prompts/assets/image-prompts.md +5 -0
  30. package/lib/drama/skills/short-drama-image-prompts/assets/lookdev-prompts.md +3 -0
  31. package/lib/drama/skills/short-drama-novel-analyze/references/aggregation-and-entities.md +2 -2
  32. package/lib/drama/skills/short-drama-novel-analyze/references/chapter-extraction.md +4 -1
  33. package/lib/drama/skills/short-drama-produce/SKILL.md +24 -3
  34. package/lib/drama/skills/short-drama-produce/references/adapter-contract.md +28 -0
  35. package/lib/drama/skills/short-drama-produce/references/providers/minimax-h3-video.md +24 -5
  36. package/lib/drama/skills/short-drama-produce/references/providers/seedance.md +7 -0
  37. package/lib/drama/skills/short-drama-produce/scripts/fixture_adapter.py +18 -1
  38. package/lib/drama/skills/short-drama-produce/scripts/production_tool.py +181 -2
  39. package/lib/drama/skills/short-drama-produce/scripts/provider_adapters.py +57 -2
  40. package/lib/drama/skills/short-drama-review/SKILL.md +3 -0
  41. package/lib/drama/skills/short-drama-review/references/production-quality-gates.md +20 -0
  42. package/lib/drama/skills/short-drama-review/references/rubric-visual-motion.md +36 -0
  43. package/lib/drama/skills/short-drama-storyboard/SKILL.md +7 -0
  44. package/lib/drama/skills/short-drama-storyboard/assets/keyframe-prompts.md +4 -0
  45. package/lib/drama/skills/short-drama-storyboard/references/shot-craft.md +39 -0
  46. package/lib/drama/skills/short-drama-storyboard/references/stage-contract.md +2 -0
  47. package/lib/drama/skills/short-drama-video-prompts/SKILL.md +7 -1
  48. package/lib/drama/skills/short-drama-video-prompts/references/minimax-h3.md +101 -6
  49. package/lib/drama/skills/short-drama-video-prompts/references/motion-recipe.md +7 -1
  50. package/lib/drama/skills/short-drama-video-prompts/references/performance-action-timing.md +35 -0
  51. package/lib/drama/skills/short-drama-video-prompts/references/production-prompt-grammar.md +3 -1
  52. package/lib/drama/skills/short-drama-video-prompts/references/seedance-2.0.md +13 -3
  53. package/lib/drama/skills/short-drama-video-prompts/references/seedance-2.5.md +35 -4
  54. package/lib/drama/skills/short-drama-video-prompts/references/stage-contract.md +1 -1
  55. package/lib/drama/skills/short-drama-write/SKILL.md +13 -2
  56. package/lib/drama/skills/short-drama-write/references/screenplay-format.md +6 -0
  57. package/lib/drama/skills/short-drama-write/references/stage-contract.md +1 -0
  58. package/lib/index.js +4 -2
  59. package/lib/index.js.map +2 -2
  60. package/lib/novel-to-game/VERSION +1 -1
  61. package/lib/novel-to-game/manifest.json +28 -28
  62. package/lib/novel-to-game/skills/game-art-direction/SKILL.md +6 -27
  63. package/lib/novel-to-game/skills/game-build/SKILL.md +12 -29
  64. package/lib/novel-to-game/skills/game-concept/SKILL.md +10 -26
  65. package/lib/novel-to-game/skills/game-qa/SKILL.md +5 -15
  66. package/lib/novel-to-game/skills/game-qa/references/test-design-method.md +1 -1
  67. package/lib/novel-to-game/skills/game-world-design/SKILL.md +19 -28
  68. package/lib/novel-to-game/skills/game-world-design/references/dialogue-design-method.md +1 -2
  69. package/lib/novel-to-game/skills/novel-game-analyze/SKILL.md +6 -9
  70. package/lib/novel-to-game/skills/novel-to-game/SKILL.md +6 -11
  71. package/lib/novel-to-game/skills/novel-to-game/references/intake-benchmark-reference.md +0 -4
  72. package/lib/novel-to-game/skills/novel-to-game/references/intake-method.md +1 -3
  73. package/lib/novel-to-game/skills/novel-to-game/references/pipeline-contract.md +3 -5
  74. package/lib/oh-story/manifest.json +292 -172
  75. package/lib/oh-story/roles/character-designer.md +1 -1
  76. package/lib/oh-story/roles/narrative-writer.md +56 -242
  77. package/lib/oh-story/roles/story-architect.md +1 -1
  78. package/lib/oh-story/skills/story/SKILL.md +1 -1
  79. package/lib/oh-story/skills/story/VERSION +1 -1
  80. package/lib/oh-story/skills/story/references/author-memory.md +10 -8
  81. package/lib/oh-story/skills/story-deslop/SKILL.md +20 -182
  82. package/lib/oh-story/skills/story-deslop/references/anti-ai-writing.md +19 -47
  83. package/lib/oh-story/skills/story-deslop/references/author-memory.md +10 -8
  84. package/lib/oh-story/skills/story-deslop/references/banned-words.md +3 -1
  85. package/lib/oh-story/skills/story-deslop/references/deslop-gates.md +154 -0
  86. package/lib/oh-story/skills/story-deslop/references/style-resolution.md +27 -0
  87. package/lib/oh-story/skills/story-deslop/scripts/check-ai-patterns.js +6 -1
  88. package/lib/oh-story/skills/story-deslop/scripts/check-degeneration.js +39 -20
  89. package/lib/oh-story/skills/story-deslop/scripts/normalize-punctuation.js +10 -5
  90. package/lib/oh-story/skills/story-deslop/scripts/style-whitelist.js +39 -0
  91. package/lib/oh-story/skills/story-import/SKILL.md +2 -2
  92. package/lib/oh-story/skills/story-import/references/format-and-structure.md +2 -2
  93. package/lib/oh-story/skills/story-import/references/tracking-initialization.md +31 -0
  94. package/lib/oh-story/skills/story-import/references/tracking-transaction.md +2 -29
  95. package/lib/oh-story/skills/story-import/scripts/check-outline-contract.js +176 -12
  96. package/lib/oh-story/skills/story-long-analyze/SKILL.md +1 -1
  97. package/lib/oh-story/skills/story-long-write/SKILL.md +20 -193
  98. package/lib/oh-story/skills/story-long-write/references/anti-ai-writing.md +19 -47
  99. package/lib/oh-story/skills/story-long-write/references/artifact-protocols.md +62 -7
  100. package/lib/oh-story/skills/story-long-write/references/author-memory.md +10 -8
  101. package/lib/oh-story/skills/story-long-write/references/banned-words.md +3 -1
  102. package/lib/oh-story/skills/story-long-write/references/benchmark-recall.md +20 -0
  103. package/lib/oh-story/skills/story-long-write/references/dialogue-mastery.md +6 -4
  104. package/lib/oh-story/skills/story-long-write/references/long-chapter-quality.md +2 -2
  105. package/lib/oh-story/skills/story-long-write/references/long-format.md +2 -0
  106. package/lib/oh-story/skills/story-long-write/references/outline-structure-theory.md +2 -0
  107. package/lib/oh-story/skills/story-long-write/references/project-files.md +108 -0
  108. package/lib/oh-story/skills/story-long-write/references/reference-index.md +87 -0
  109. package/lib/oh-story/skills/story-long-write/references/style-combat-face.md +1 -1
  110. package/lib/oh-story/skills/story-long-write/references/style-craft.md +9 -29
  111. package/lib/oh-story/skills/story-long-write/references/style-genre-modules.md +1 -1
  112. package/lib/oh-story/skills/story-long-write/references/style-resolution.md +27 -0
  113. package/lib/oh-story/skills/story-long-write/references/tracking-initialization.md +31 -0
  114. package/lib/oh-story/skills/story-long-write/references/tracking-transaction.md +2 -29
  115. package/lib/oh-story/skills/story-long-write/references/workflow-chapter.md +51 -52
  116. package/lib/oh-story/skills/story-long-write/references/workflow-daily.md +8 -7
  117. package/lib/oh-story/skills/story-long-write/references/workflow-setup.md +36 -15
  118. package/lib/oh-story/skills/story-long-write/references/writer-prompt-fallback.md +22 -0
  119. package/lib/oh-story/skills/story-long-write/references/writing-craft.md +69 -2
  120. package/lib/oh-story/skills/story-long-write/scripts/build_writer_prompt.py +410 -0
  121. package/lib/oh-story/skills/story-long-write/scripts/check-ai-patterns.js +6 -1
  122. package/lib/oh-story/skills/story-long-write/scripts/check-degeneration.js +39 -20
  123. package/lib/oh-story/skills/story-long-write/scripts/check-outline-contract.js +176 -12
  124. package/lib/oh-story/skills/story-long-write/scripts/check-outline-copy.js +90 -23
  125. package/lib/oh-story/skills/story-long-write/scripts/normalize-punctuation.js +10 -5
  126. package/lib/oh-story/skills/story-long-write/scripts/outline_view.py +283 -0
  127. package/lib/oh-story/skills/story-long-write/scripts/style-whitelist.js +39 -0
  128. package/lib/oh-story/skills/story-review/SKILL.md +13 -3
  129. package/lib/oh-story/skills/story-review/references/anti-ai-writing.md +19 -47
  130. package/lib/oh-story/skills/story-review/references/author-memory.md +10 -8
  131. package/lib/oh-story/skills/story-review/references/banned-words.md +3 -1
  132. package/lib/oh-story/skills/story-review/references/dialogue-mastery.md +6 -4
  133. package/lib/oh-story/skills/story-review/references/review-quality.md +2 -2
  134. package/lib/oh-story/skills/story-review/references/style-resolution.md +27 -0
  135. package/lib/oh-story/skills/story-review/references/tracking-initialization.md +31 -0
  136. package/lib/oh-story/skills/story-review/references/tracking-transaction.md +2 -29
  137. package/lib/oh-story/skills/story-review/scripts/check-ai-patterns.js +6 -1
  138. package/lib/oh-story/skills/story-review/scripts/check-degeneration.js +39 -20
  139. package/lib/oh-story/skills/story-review/scripts/normalize-punctuation.js +10 -5
  140. package/lib/oh-story/skills/story-review/scripts/style-whitelist.js +39 -0
  141. package/lib/oh-story/skills/story-setup/SKILL.md +17 -10
  142. package/lib/oh-story/skills/story-setup/references/agent-references/anti-ai-writing.md +19 -47
  143. package/lib/oh-story/skills/story-setup/references/agent-references/banned-words.md +3 -1
  144. package/lib/oh-story/skills/story-setup/references/agent-references/deslop-gates.md +154 -0
  145. package/lib/oh-story/skills/story-setup/references/agent-references/dialogue-mastery.md +6 -4
  146. package/lib/oh-story/skills/story-setup/references/agent-references/format-and-structure.md +2 -2
  147. package/lib/oh-story/skills/story-setup/references/agent-references/short-genre-formulas.md +1 -1
  148. package/lib/oh-story/skills/story-setup/references/agent-references/style-combat-face.md +1 -1
  149. package/lib/oh-story/skills/story-setup/references/agent-references/style-genre-modules.md +1 -1
  150. package/lib/oh-story/skills/story-setup/references/agent-references/style-resolution.md +27 -0
  151. package/lib/oh-story/skills/story-setup/references/agent-references/writing-craft.md +69 -2
  152. package/lib/oh-story/skills/story-setup/references/diagnostics.md +38 -0
  153. package/lib/oh-story/skills/story-short-analyze/references/analysis-writing-techniques.md +1 -1
  154. package/lib/oh-story/skills/story-short-analyze/references/source-story-quality.md +2 -2
  155. package/lib/oh-story/skills/story-short-write/SKILL.md +18 -186
  156. package/lib/oh-story/skills/story-short-write/references/author-memory.md +10 -8
  157. package/lib/oh-story/skills/story-short-write/references/banned-words.md +3 -1
  158. package/lib/oh-story/skills/story-short-write/references/benchmark-recall.md +60 -0
  159. package/lib/oh-story/skills/story-short-write/references/dialogue-mastery.md +6 -4
  160. package/lib/oh-story/skills/story-short-write/references/genre-styles//344/270/226/346/203/205/346/211/223/350/204/270.md +2 -2
  161. package/lib/oh-story/skills/story-short-write/references/genre-styles//345/217/214/347/224/267/344/270/273.md +1 -1
  162. package/lib/oh-story/skills/story-short-write/references/genre-styles//345/244/215/344/273/207/346/211/223/350/204/270.md +3 -3
  163. package/lib/oh-story/skills/story-short-write/references/genre-styles//345/256/205/346/226/227/345/256/253/346/226/227.md +3 -3
  164. package/lib/oh-story/skills/story-short-write/references/genre-styles//346/200/273/350/243/201/350/261/252/351/227/250.md +3 -3
  165. package/lib/oh-story/skills/story-short-write/references/genre-styles//346/202/254/347/226/221.md +5 -5
  166. package/lib/oh-story/skills/story-short-write/references/genre-styles//346/260/221/344/277/227/346/200/252/350/260/210.md +4 -4
  167. package/lib/oh-story/skills/story-short-write/references/genre-styles//347/224/234/345/256/240.md +5 -5
  168. package/lib/oh-story/skills/story-short-write/references/genre-styles//350/277/275/345/246/273/347/201/253/350/221/254/345/234/272.md +3 -3
  169. package/lib/oh-story/skills/story-short-write/references/genre-writing-formulas.md +1 -1
  170. package/lib/oh-story/skills/story-short-write/references/genre-writing-techniques.md +1 -1
  171. package/lib/oh-story/skills/story-short-write/references/reference-index.md +53 -0
  172. package/lib/oh-story/skills/story-short-write/references/short-craft.md +12 -14
  173. package/lib/oh-story/skills/story-short-write/references/short-deslop.md +10 -10
  174. package/lib/oh-story/skills/story-short-write/references/short-format.md +3 -1
  175. package/lib/oh-story/skills/story-short-write/references/short-prose-quality.md +2 -2
  176. package/lib/oh-story/skills/story-short-write/references/style-resolution.md +27 -0
  177. package/lib/oh-story/skills/story-short-write/references/workflow-design.md +77 -0
  178. package/lib/oh-story/skills/story-short-write/references/writing-workflow.md +5 -5
  179. package/lib/oh-story/skills/story-short-write/scripts/check-ai-patterns.js +6 -1
  180. package/lib/oh-story/skills/story-short-write/scripts/check-degeneration.js +39 -20
  181. package/lib/oh-story/skills/story-short-write/scripts/check-delivery-contract.js +34 -5
  182. package/lib/oh-story/skills/story-short-write/scripts/check-outline-copy.js +90 -23
  183. package/lib/oh-story/skills/story-short-write/scripts/normalize-punctuation.js +10 -5
  184. package/lib/oh-story/skills/story-short-write/scripts/style-whitelist.js +39 -0
  185. package/lib/types/client/drama-production-view.d.ts.map +1 -1
  186. package/lib/types/client/index.d.ts.map +1 -1
  187. package/lib/types/client/production-prompts.d.ts.map +1 -1
  188. package/lib/types/client/production-runtime.d.ts +17 -0
  189. package/lib/types/client/production-runtime.d.ts.map +1 -1
  190. package/lib/types/skill-provider.d.ts.map +1 -1
  191. package/lib/video-recap/CHANGELOG.md +31 -1
  192. package/lib/video-recap/manifest.json +162 -162
  193. package/lib/video-recap/skills/video-assemble/scripts/artifacts.py +4 -9
  194. package/lib/video-recap/skills/video-assemble/scripts/assemble.py +47 -60
  195. package/lib/video-recap/skills/video-assemble/scripts/assembly_contract.py +77 -115
  196. package/lib/video-recap/skills/video-assemble/scripts/assembly_settings.py +41 -40
  197. package/lib/video-recap/skills/video-assemble/scripts/audio_automation.py +22 -32
  198. package/lib/video-recap/skills/video-assemble/scripts/audio_mix.py +102 -192
  199. package/lib/video-recap/skills/video-assemble/scripts/jianying_builders.py +14 -35
  200. package/lib/video-recap/skills/video-assemble/scripts/jianying_model.py +14 -12
  201. package/lib/video-recap/skills/video-assemble/scripts/jianying_optional.py +8 -9
  202. package/lib/video-recap/skills/video-assemble/scripts/jianying_schema.py +3 -14
  203. package/lib/video-recap/skills/video-assemble/scripts/jianying_timeline_contract.py +23 -12
  204. package/lib/video-recap/skills/video-assemble/scripts/jianying_tracks.py +1 -4
  205. package/lib/video-recap/skills/video-assemble/scripts/jianying_writer.py +24 -25
  206. package/lib/video-recap/skills/video-assemble/scripts/lib.py +31 -85
  207. package/lib/video-recap/skills/video-assemble/scripts/media.py +100 -144
  208. package/lib/video-recap/skills/video-assemble/scripts/narration_audio.py +101 -195
  209. package/lib/video-recap/skills/video-assemble/scripts/render_preflight.py +10 -21
  210. package/lib/video-recap/skills/video-assemble/scripts/source_subtitles.py +90 -169
  211. package/lib/video-recap/skills/video-assemble/scripts/subtitle_core.py +69 -122
  212. package/lib/video-recap/skills/video-assemble/scripts/subtitle_render.py +10 -17
  213. package/lib/video-recap/skills/video-assemble/scripts/timeline.py +33 -48
  214. package/lib/video-recap/skills/video-assemble/scripts/timeline_emit.py +14 -20
  215. package/lib/video-recap/skills/video-assemble/scripts/visual_render.py +135 -213
  216. package/lib/video-recap/skills/video-cut/scripts/cut_cli.py +49 -57
  217. package/lib/video-recap/skills/video-cut/scripts/cut_contract.py +38 -74
  218. package/lib/video-recap/skills/video-cut/scripts/cut_render.py +46 -95
  219. package/lib/video-recap/skills/video-cut/scripts/lib.py +23 -25
  220. package/lib/video-recap/skills/video-cut/scripts/media_geometry.py +70 -173
  221. package/lib/video-recap/skills/video-cut/scripts/narration_mapping.py +28 -53
  222. package/lib/video-recap/skills/video-cut/scripts/sentence_boundaries.py +162 -322
  223. package/lib/video-recap/skills/video-recap/references/timeline-and-jianying.md +6 -7
  224. package/lib/video-recap/skills/video-recap/scripts/doctor.py +104 -124
  225. package/lib/video-recap/skills/video-recap/scripts/final_qc.py +255 -412
  226. package/lib/video-recap/skills/video-recap/scripts/lib.py +30 -52
  227. package/lib/video-recap/skills/video-recap/scripts/materials.py +97 -120
  228. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_client.py +7 -12
  229. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_evidence.py +46 -97
  230. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_observations.py +18 -36
  231. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_payload.py +29 -47
  232. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_report.py +43 -76
  233. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_runner.py +2 -1
  234. package/lib/video-recap/skills/video-recap/scripts/qc_contract.py +2 -1
  235. package/lib/video-recap/skills/video-recap/scripts/recap_cli.py +2 -2
  236. package/lib/video-recap/skills/video-recap/scripts/recap_inspect.py +129 -145
  237. package/lib/video-recap/skills/video-recap/scripts/recap_review.py +27 -20
  238. package/lib/video-recap/skills/video-recap/scripts/recap_runner.py +20 -31
  239. package/lib/video-recap/skills/video-recap/scripts/recap_runtime.py +31 -106
  240. package/lib/video-recap/skills/video-recap/scripts/recap_stage_qc.py +43 -113
  241. package/lib/video-recap/skills/video-recap/scripts/recap_timeline.py +196 -377
  242. package/lib/video-recap/skills/video-script/scripts/agent_brief.py +1 -1
  243. package/lib/video-recap/skills/video-script/scripts/agent_text.py +63 -172
  244. package/lib/video-recap/skills/video-script/scripts/brief_context.py +80 -135
  245. package/lib/video-recap/skills/video-script/scripts/brief_inputs.py +125 -167
  246. package/lib/video-recap/skills/video-script/scripts/brief_timeline.py +93 -208
  247. package/lib/video-recap/skills/video-script/scripts/deslop_qc.py +20 -46
  248. package/lib/video-recap/skills/video-script/scripts/lib.py +30 -26
  249. package/lib/video-recap/skills/video-script/scripts/narration_lint.py +145 -140
  250. package/lib/video-recap/skills/video-script/scripts/review_response.py +12 -42
  251. package/lib/video-recap/skills/video-script/scripts/review_runner.py +0 -6
  252. package/lib/video-recap/skills/video-script/scripts/speech_ownership.py +39 -100
  253. package/lib/video-recap/skills/video-script/scripts/timeline_fusion.py +33 -124
  254. package/lib/video-recap/skills/video-understanding/scripts/agent_brief.py +1 -1
  255. package/lib/video-recap/skills/video-understanding/scripts/agent_text.py +63 -172
  256. package/lib/video-recap/skills/video-understanding/scripts/asr.py +1 -2
  257. package/lib/video-recap/skills/video-understanding/scripts/brief_context.py +80 -135
  258. package/lib/video-recap/skills/video-understanding/scripts/brief_inputs.py +125 -167
  259. package/lib/video-recap/skills/video-understanding/scripts/brief_timeline.py +93 -208
  260. package/lib/video-recap/skills/video-understanding/scripts/consolidate.py +1 -1
  261. package/lib/video-recap/skills/video-understanding/scripts/deslop_qc.py +20 -46
  262. package/lib/video-recap/skills/video-understanding/scripts/lib.py +31 -26
  263. package/lib/video-recap/skills/video-understanding/scripts/narration_lint.py +145 -140
  264. package/lib/video-recap/skills/video-understanding/scripts/speech_ownership.py +39 -100
  265. package/lib/video-recap/skills/video-understanding/scripts/timeline_fusion.py +33 -124
  266. package/lib/video-recap/skills/video-understanding/scripts/vlm.py +1 -4
  267. package/lib/video-recap/skills/video-voiceover/scripts/dub.py +7 -13
  268. package/lib/video-recap/skills/video-voiceover/scripts/fish_audio.py +12 -30
  269. package/lib/video-recap/skills/video-voiceover/scripts/lib.py +98 -131
  270. package/lib/video-recap/skills/video-voiceover/scripts/tts_audio.py +17 -28
  271. package/lib/video-recap/skills/video-voiceover/scripts/voiceover.py +151 -240
  272. package/package.json +41 -41
@@ -220,7 +220,7 @@ def build_agent_brief(
220
220
  lines.extend(_format_timeline_fusion_for_brief(timeline_fusion))
221
221
  lines.extend(_format_sentence_entry_anchors_for_brief(work_dir, edit_mode))
222
222
 
223
- overview_text = (mimo_overview.get("content") or "").strip()
223
+ overview_text = (mimo_overview or {}).get("content", "").strip()
224
224
  if overview_text:
225
225
  lines.extend(
226
226
  [
@@ -1,30 +1,10 @@
1
1
  """Normalize, split, budget, and de-duplicate narration text."""
2
2
 
3
- import importlib.util
4
-
5
3
  import copy
6
-
7
-
8
4
  import re
9
5
 
10
- from pathlib import Path
11
-
12
- from lib import CONFIG
13
-
14
- from lib import log
15
-
16
- try:
17
- from deslop_qc import analyze_deslop_qc
18
- except ModuleNotFoundError:
19
- _deslop_qc_path = Path(__file__).with_name("deslop_qc.py")
20
- _deslop_qc_spec = importlib.util.spec_from_file_location(
21
- "deslop_qc", _deslop_qc_path
22
- )
23
- if _deslop_qc_spec is None or _deslop_qc_spec.loader is None:
24
- raise
25
- _deslop_qc_module = importlib.util.module_from_spec(_deslop_qc_spec)
26
- _deslop_qc_spec.loader.exec_module(_deslop_qc_module)
27
- analyze_deslop_qc = _deslop_qc_module.analyze_deslop_qc
6
+ from lib import CONFIG, log
7
+ from deslop_qc import _sentence_pieces, _text_units
28
8
 
29
9
 
30
10
  def _format_frame_facts(scene):
@@ -32,10 +12,7 @@ def _format_frame_facts(scene):
32
12
  facts = scene.get("frame_facts", {})
33
13
  if not facts:
34
14
  return ""
35
- lines = []
36
- for ts in sorted(facts.keys(), key=lambda x: float(x)):
37
- actions = facts[ts]
38
- lines.append(f" {ts}s: {'; '.join(actions)}")
15
+ lines = [f" {ts}s: {'; '.join(facts[ts])}" for ts in sorted(facts, key=float)]
39
16
  return "\n 帧动作:\n" + "\n".join(lines)
40
17
 
41
18
 
@@ -45,50 +22,23 @@ def _text_char_count(text):
45
22
  re.sub(
46
23
  r'[,。!?、;:…“”‘’《》〈〉\s"\'「」『』()()【】\[\]—~·,.!?;:\\-]',
47
24
  "",
48
- text or "",
25
+ text,
49
26
  )
50
27
  )
51
28
 
52
29
 
53
- def _contains_cjk(text):
54
- return bool(re.search(r"[\u3400-\u4dbf\u4e00-\u9fff\uf900-\ufaff]", text or ""))
55
-
56
-
57
- def _writing_text_units(text):
58
- """Length unit used for ASR writing chunks: CJK chars, otherwise words."""
59
- text = str(text or "")
60
- if _contains_cjk(text):
61
- return len(re.sub(r"\s+", "", text))
62
- return len(re.findall(r"\b\w+\b", text))
63
-
64
-
65
- def _sentence_pieces(text):
66
- """Split text into sentence-like pieces while keeping terminal punctuation."""
67
- text = re.sub(r"\s+", " ", str(text or "")).strip()
68
- if not text:
69
- return []
70
- parts = re.split(r"([。!?!?;;.])", text)
71
- cleaned = []
72
- for idx in range(0, len(parts), 2):
73
- body = parts[idx].strip()
74
- punct = parts[idx + 1] if idx + 1 < len(parts) else ""
75
- if body or punct:
76
- cleaned.append((body + punct).strip())
77
- return cleaned or [text]
78
-
79
-
80
30
  def _split_text_by_sentence_windows(text, min_chars=500, max_chars=800):
81
31
  """Clipto-style three-tier sentence boundary splitting for long ASR text."""
82
- text = re.sub(r"\s+", " ", str(text or "")).strip()
32
+ text = re.sub(r"\s+", " ", text).strip()
83
33
  if not text:
84
34
  return []
85
- if _writing_text_units(text) <= max_chars:
35
+ if _text_units(text) <= max_chars:
86
36
  return _sentence_pieces(text)
87
37
 
88
38
  result = []
89
39
  rest = text
90
40
  sentence_marks = "。!?!?;;."
91
- while _writing_text_units(rest) > max_chars:
41
+ while _text_units(rest) > max_chars:
92
42
  # The min/max thresholds are unit-based but punctuation is char-indexed.
93
43
  # For Chinese (the dominant recap target) these are nearly identical; for
94
44
  # non-CJK this remains a safe sentence-boundary heuristic around words.
@@ -102,10 +52,7 @@ def _split_text_by_sentence_windows(text, min_chars=500, max_chars=800):
102
52
  if ch in sentence_marks:
103
53
  outside = i
104
54
  break
105
- if outside >= 0 and outside + 1 <= len(rest):
106
- cut = outside
107
- else:
108
- cut = char_max - 1
55
+ cut = outside if outside >= 0 and outside + 1 <= len(rest) else char_max - 1
109
56
  piece = rest[: cut + 1].strip()
110
57
  if not piece:
111
58
  piece = rest[:char_max].strip()
@@ -119,19 +66,13 @@ def _split_text_by_sentence_windows(text, min_chars=500, max_chars=800):
119
66
 
120
67
  def _timed_sentence_pieces(seg, min_chars, max_chars):
121
68
  """Split one ASR segment into timed sentence pieces with approximate spans."""
122
- text = str(seg.get("text", "")).strip()
69
+ text = seg["text"].strip()
123
70
  if not text:
124
71
  return []
125
- try:
126
- start = float(seg.get("start", 0))
127
- end = float(seg.get("end", start))
128
- except (TypeError, ValueError):
129
- start = end = 0.0
130
- if end < start:
131
- end = start
72
+ start, end = seg["start"], seg["end"]
132
73
  pieces = []
133
74
  for sentence in _sentence_pieces(text):
134
- if _writing_text_units(sentence) > max_chars:
75
+ if _text_units(sentence) > max_chars:
135
76
  pieces.extend(
136
77
  _split_text_by_sentence_windows(
137
78
  sentence, min_chars=min_chars, max_chars=max_chars
@@ -139,20 +80,19 @@ def _timed_sentence_pieces(seg, min_chars, max_chars):
139
80
  )
140
81
  else:
141
82
  pieces.append(sentence)
142
- total_units = sum(max(1, _writing_text_units(piece)) for piece in pieces) or 1
143
- duration = max(0.0, end - start)
83
+ total_units = sum(max(1, _text_units(piece)) for piece in pieces)
84
+ duration = end - start
144
85
  cursor = start
145
86
  timed = []
146
87
  for idx, piece in enumerate(pieces):
147
- units = max(1, _writing_text_units(piece))
148
- piece_duration = duration * units / total_units if duration else 0.0
149
- piece_end = end if idx == len(pieces) - 1 else cursor + piece_duration
88
+ units = max(1, _text_units(piece))
89
+ piece_end = end if idx == len(pieces) - 1 else cursor + duration * units / total_units
150
90
  timed.append(
151
91
  {
152
92
  "start": round(cursor, 2),
153
93
  "end": round(piece_end, 2),
154
94
  "text": piece,
155
- "char_count": _writing_text_units(piece),
95
+ "char_count": _text_units(piece),
156
96
  }
157
97
  )
158
98
  cursor = piece_end
@@ -160,25 +100,18 @@ def _timed_sentence_pieces(seg, min_chars, max_chars):
160
100
 
161
101
 
162
102
  def _scene_ids_for_range(scenes, start, end):
103
+ duration = max(0.001, end - start)
163
104
  scene_ids = []
164
- duration = max(0.001, float(end) - float(start))
165
- for scene in scenes or []:
166
- try:
167
- s_start = float(scene.get("start", 0))
168
- s_end = float(scene.get("end", s_start))
169
- except (TypeError, ValueError):
170
- continue
171
- overlap = _overlap_seconds(start, end, s_start, s_end)
105
+ for scene in scenes:
106
+ overlap = _overlap_seconds(start, end, scene["start"], scene["end"])
172
107
  # Ignore tiny boundary tails from approximate ASR sentence timing. A
173
108
  # scene id should mean the chunk materially belongs to that scene.
174
109
  if overlap and (overlap >= duration * 0.2 or overlap >= 3.0):
175
- scene_ids.append(scene.get("scene_id"))
176
- return [sid for sid in scene_ids if sid is not None]
110
+ scene_ids.append(scene["scene_id"])
111
+ return scene_ids
177
112
 
178
113
 
179
- def _chunk_asr_for_writing(
180
- segments, scenes_analysis=None, min_chars=None, max_chars=None
181
- ):
114
+ def _chunk_asr_for_writing(segments, scenes_analysis, min_chars=None, max_chars=None):
182
115
  """Chunk ASR into semantic windows before an agent writes long-dialogue recaps.
183
116
 
184
117
  The strategy mirrors Clipto's segment splitter: accumulate a window, prefer
@@ -186,15 +119,13 @@ def _chunk_asr_for_writing(
186
119
  boundary outside the window, and fall back to the remaining text. CJK text is
187
120
  measured by characters; non-CJK text is measured by words.
188
121
  """
189
- min_chars = int(min_chars or CONFIG.get("asr_chunk_min_chars", 500))
190
- max_chars = int(max_chars or CONFIG.get("asr_chunk_max_chars", 800))
191
- min_chars = max(1, min(min_chars, max_chars))
192
- max_chars = max(min_chars, max_chars)
193
-
194
- pieces = []
195
- for seg in segments or []:
196
- if isinstance(seg, dict):
197
- pieces.extend(_timed_sentence_pieces(seg, min_chars, max_chars))
122
+ min_chars = CONFIG["asr_chunk_min_chars"] if min_chars is None else min_chars
123
+ max_chars = CONFIG["asr_chunk_max_chars"] if max_chars is None else max_chars
124
+ pieces = [
125
+ piece
126
+ for seg in segments
127
+ for piece in _timed_sentence_pieces(seg, min_chars, max_chars)
128
+ ]
198
129
 
199
130
  chunks = []
200
131
  current = []
@@ -208,8 +139,8 @@ def _chunk_asr_for_writing(
208
139
  chunks.append(
209
140
  {
210
141
  "chunk_id": len(chunks),
211
- "start": round(float(current[0]["start"]), 2),
212
- "end": round(float(current[-1]["end"]), 2),
142
+ "start": current[0]["start"],
143
+ "end": current[-1]["end"],
213
144
  "scene_ids": sorted(
214
145
  current_scene_ids, key=lambda sid: (isinstance(sid, str), sid)
215
146
  ),
@@ -223,9 +154,7 @@ def _chunk_asr_for_writing(
223
154
  current_scene_ids = set()
224
155
 
225
156
  for piece in pieces:
226
- units = max(
227
- 1, int(piece.get("char_count", _writing_text_units(piece.get("text", ""))))
228
- )
157
+ units = max(1, piece["char_count"])
229
158
  piece_scene_ids = set(
230
159
  _scene_ids_for_range(scenes_analysis, piece["start"], piece["end"])
231
160
  )
@@ -279,12 +208,6 @@ def _post_dedup_narration(narration):
279
208
  result = [narration[0]]
280
209
  for seg in narration[1:]:
281
210
  prev = result[-1]
282
- if (
283
- not prev.get("narration", "").strip()
284
- or not seg.get("narration", "").strip()
285
- ):
286
- result.append(seg)
287
- continue
288
211
  set_a, set_b = _char_bigrams(prev["narration"]), _char_bigrams(seg["narration"])
289
212
  if not set_a or not set_b:
290
213
  result.append(seg)
@@ -294,20 +217,15 @@ def _post_dedup_narration(narration):
294
217
  # bigrams by chance, so a low threshold collapses intentional parallel beats
295
218
  # ("他不再试探" / "他直接赌上全力") and silently drops density below target.
296
219
  if overlap > 0.6:
297
- # Validation is also the handoff boundary for renderer metadata. When two
298
- # near-identical narration beats are merged, retain overlays authored on either
299
- # beat instead of silently discarding the second beat's visual instructions.
300
- merged_overlays = []
301
- for candidate in (prev, seg):
302
- raw_overlays = candidate.get("visual_overlays")
303
- if isinstance(raw_overlays, list):
304
- merged_overlays.extend(copy.deepcopy(raw_overlays))
220
+ # Validation is also the handoff boundary for renderer metadata: keep the
221
+ # overlays authored on either merged beat.
222
+ merged_overlays = copy.deepcopy(
223
+ prev.get("visual_overlays", []) + seg.get("visual_overlays", [])
224
+ )
305
225
  if len(seg["narration"]) > len(prev["narration"]):
306
226
  prev["narration"] = seg["narration"]
307
227
  prev["end"] = seg["end"]
308
- prev["pause_after_ms"] = seg.get(
309
- "pause_after_ms", prev.get("pause_after_ms", 600)
310
- )
228
+ prev["pause_after_ms"] = seg["pause_after_ms"]
311
229
  if merged_overlays:
312
230
  prev["visual_overlays"] = merged_overlays
313
231
  log(f" 去重合并: {prev['start']:.0f}-{prev['end']:.0f}s")
@@ -319,56 +237,39 @@ def _post_dedup_narration(narration):
319
237
  return result
320
238
 
321
239
 
322
- def _scene_available_seconds(start, end, pause_after_ms=None):
323
- del pause_after_ms # pause_after_ms affects the next segment gap during assembly, not current speech capacity.
324
- tail_pad = max(0.0, float(CONFIG.get("narration_tail_pad_seconds", 0.1) or 0.0))
325
- return max(0.0, float(end) - float(start) - tail_pad)
240
+ def _scene_available_seconds(start, end):
241
+ return max(0.0, end - start - CONFIG["narration_tail_pad_seconds"])
326
242
 
327
243
 
328
- def _recommended_char_budget(start, end, pause_after_ms=None):
244
+ def _recommended_char_budget(start, end):
329
245
  # account for the global narration atempo (CONFIG['narration_speed']) so a beat's text
330
246
  # is budgeted against the FINAL sped-up audio, not the raw TTS rate — otherwise windows
331
247
  # are over-sized and the bed shows long silent gaps between sentences.
332
248
  effective_rate = (
333
- CONFIG["speech_rate"]
334
- * CONFIG["speech_safety_margin"]
335
- * float(CONFIG.get("narration_speed", 1.0) or 1.0)
249
+ CONFIG["speech_rate"] * CONFIG["speech_safety_margin"] * CONFIG["narration_speed"]
336
250
  )
337
- available = _scene_available_seconds(start, end, pause_after_ms)
338
- return max(0, int(available * effective_rate))
251
+ return int(_scene_available_seconds(start, end) * effective_rate)
339
252
 
340
253
 
341
254
  def _find_scene_for_midpoint(scenes_analysis, start, end):
342
- mid = (float(start) + float(end)) / 2
255
+ mid = (start + end) / 2
343
256
  for scene in scenes_analysis:
344
257
  if scene["start"] <= mid <= scene["end"]:
345
258
  return scene
346
259
  return None
347
260
 
348
261
 
349
- def _normalise_narration_segment(seg, scenes_analysis=None):
350
- if not isinstance(seg, dict):
351
- return None
352
- try:
353
- start = float(seg.get("start"))
354
- end = float(seg.get("end"))
355
- except (TypeError, ValueError):
356
- return None
357
- if end <= start:
358
- return None
359
- text = str(seg.get("narration", "")).strip()
360
- if not text:
361
- return None
362
- pause = seg.get("pause_after_ms", CONFIG.get("breath_ms", 250))
363
- try:
364
- pause = int(pause)
365
- except (TypeError, ValueError):
366
- pause = CONFIG.get("breath_ms", 250)
262
+ def _normalise_narration_segment(seg):
263
+ """Normalize one lint-validated narration segment for the full-mode rewrite.
264
+
265
+ Authored timing is a delivery contract: scene boundaries are approximate
266
+ visual-analysis buckets, so start/end are never clamped here.
267
+ """
367
268
  item = {
368
- "start": round(start, 2),
369
- "end": round(end, 2),
370
- "narration": text,
371
- "pause_after_ms": pause,
269
+ "start": round(seg["start"], 2),
270
+ "end": round(seg["end"], 2),
271
+ "narration": str(seg["narration"]).strip(),
272
+ "pause_after_ms": int(seg.get("pause_after_ms", CONFIG["breath_ms"])),
372
273
  "overlaps_speech": bool(seg.get("overlaps_speech", True)),
373
274
  }
374
275
  for optional_key in (
@@ -381,30 +282,20 @@ def _normalise_narration_segment(seg, scenes_analysis=None):
381
282
  if optional_key in seg:
382
283
  item[optional_key] = seg[optional_key]
383
284
  # carry the per-beat emotion/tone tag (MiMo TTS instruct) through lint untouched
384
- emotion = seg.get("emotion")
385
- if isinstance(emotion, str) and emotion.strip():
386
- item["emotion"] = emotion.strip()
387
- # Preserve renderer-owned metadata across the full-mode validation rewrite. The recap
388
- # orchestrator filters supported overlay kinds later; validation must not erase authored
389
- # overlays merely because it normalizes narration timing/text fields.
390
- visual_overlays = seg.get("visual_overlays")
391
- if isinstance(visual_overlays, list):
392
- item["visual_overlays"] = copy.deepcopy(visual_overlays)
393
- # Authored timing is a delivery contract. Scene boundaries are approximate
394
- # visual-analysis buckets, while source sentence anchors are measured audio
395
- # handoff points. Lint may warn when a block crosses a scene, but validation
396
- # must never silently clamp start/end and invalidate an audio-safe handoff.
285
+ if seg.get("emotion"):
286
+ item["emotion"] = seg["emotion"].strip()
287
+ # Renderer-owned metadata survives the validation rewrite; the recap orchestrator
288
+ # filters supported overlay kinds later.
289
+ if "visual_overlays" in seg:
290
+ item["visual_overlays"] = copy.deepcopy(seg["visual_overlays"])
397
291
  return item
398
292
 
399
293
 
400
294
  def _clean_narration_punctuation(text):
401
- text = re.sub(r"\s+", " ", text or "").strip()
295
+ text = re.sub(r"\s+", " ", text).strip()
402
296
  text = re.sub(r'[,:、;,]["\']?[。!?]', "。", text)
403
- text = re.sub(r'["\']。$', "。", text)
404
- return text
297
+ return re.sub(r'["\']。$', "。", text)
405
298
 
406
299
 
407
300
  def _overlap_seconds(start, end, other_start, other_end):
408
- return max(
409
- 0.0, min(float(end), float(other_end)) - max(float(start), float(other_start))
410
- )
301
+ return max(0.0, min(end, other_end) - max(start, other_start))