@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
@@ -1,33 +1,14 @@
1
1
  """Validate optional ASR, MiMo, and stage-status inputs for the brief."""
2
2
 
3
3
  import hashlib
4
-
5
- import importlib.util
6
-
7
-
8
4
  import json
9
-
10
-
11
5
  from pathlib import Path
12
6
 
13
7
  from lib import CONFIG, file_fingerprint, stable_hash
14
-
15
-
16
8
  from brief_context import _clean_asr_prompt_fingerprint, _consolidation_model
17
9
 
18
- try:
19
- from deslop_qc import analyze_deslop_qc
20
- except ModuleNotFoundError:
21
- _deslop_qc_path = Path(__file__).with_name("deslop_qc.py")
22
- _deslop_qc_spec = importlib.util.spec_from_file_location(
23
- "deslop_qc", _deslop_qc_path
24
- )
25
- if _deslop_qc_spec is None or _deslop_qc_spec.loader is None:
26
- raise
27
- _deslop_qc_module = importlib.util.module_from_spec(_deslop_qc_spec)
28
- _deslop_qc_spec.loader.exec_module(_deslop_qc_module)
29
- analyze_deslop_qc = _deslop_qc_module.analyze_deslop_qc
30
-
10
+ # Shared with consolidate.py, which this byte-identical copy cannot import (the sibling
11
+ # skill ships no consolidate.py). Keep both literals in sync.
31
12
  _ASR_SPAN_TOL = 0.05
32
13
 
33
14
  _MIMO_REJECTION_MARKERS = (
@@ -42,67 +23,61 @@ _MIMO_REJECTION_MARKERS = (
42
23
  )
43
24
 
44
25
 
45
- def _clean_asr_fresh(out_path, source_path):
46
- # Local freshness check kept inline so the separately shipped byte-identical copy
47
- # stays self-contained and in lockstep.
48
- try:
49
- return (
50
- out_path.exists()
51
- and source_path.exists()
52
- and (out_path.stat().st_mtime >= source_path.stat().st_mtime)
53
- )
54
- except OSError:
55
- return False
56
-
57
-
58
26
  def _load_clean_asr(work_dir, asr_result):
59
27
  """Return consolidate.py's cleaned ASR segments, or None to fall back to raw asr_result.
60
- Gated on parse + non-empty + freshness + provenance(source_md5) + timing invariant
61
- (len== first, then per-segment spans within _ASR_SPAN_TOL)."""
62
- base = [s for s in (asr_result or []) if isinstance(s, dict)]
63
- if not base:
28
+
29
+ Accepted only when the file is at least as fresh as asr_result.json, its provenance
30
+ (source_md5 / model / prompt_md5) matches, and every segment keeps its original span
31
+ within _ASR_SPAN_TOL."""
32
+ if not asr_result:
64
33
  return None
65
34
  work_dir = Path(work_dir)
66
35
  clean_path = work_dir / "asr_clean.json"
67
36
  src_path = work_dir / "asr_result.json"
68
- if not clean_path.exists() or not _clean_asr_fresh(clean_path, src_path):
37
+ try:
38
+ fresh = (
39
+ clean_path.exists()
40
+ and clean_path.stat().st_mtime >= src_path.stat().st_mtime
41
+ )
42
+ except OSError:
43
+ return None
44
+ if not fresh:
69
45
  return None
70
46
  try:
71
47
  payload = json.loads(clean_path.read_text(encoding="utf-8"))
72
- except (json.JSONDecodeError, OSError):
48
+ source_md5 = hashlib.md5(src_path.read_bytes()).hexdigest()
49
+ except (OSError, json.JSONDecodeError):
73
50
  return None
74
51
  if not isinstance(payload, dict):
75
52
  return None
76
- segments = payload.get("segments")
77
- if not isinstance(segments, list) or len(segments) != len(base):
78
- return None
79
- try:
80
- if payload.get("source_md5") != hashlib.md5(src_path.read_bytes()).hexdigest():
81
- return None
82
- except OSError:
83
- return None
84
- if payload.get("model") != _consolidation_model():
53
+ provenance = {
54
+ "source_md5": source_md5,
55
+ "model": _consolidation_model(),
56
+ "prompt_md5": _clean_asr_prompt_fingerprint(),
57
+ }
58
+ if not payload.items() >= provenance.items():
85
59
  return None
86
- if payload.get("prompt_md5") != _clean_asr_prompt_fingerprint():
60
+ segments = payload.get("segments")
61
+ if not isinstance(segments, list) or len(segments) != len(asr_result):
87
62
  return None
88
- for orig, clean in zip(base, segments):
89
- if not isinstance(clean, dict):
63
+ for orig, clean in zip(asr_result, segments):
64
+ if (
65
+ not isinstance(clean, dict)
66
+ or not isinstance(clean.get("start"), (int, float))
67
+ or not isinstance(clean.get("end"), (int, float))
68
+ ):
90
69
  return None
91
- try:
92
- if (
93
- abs(float(clean.get("start")) - float(orig.get("start")))
94
- > _ASR_SPAN_TOL
95
- ):
96
- return None
97
- if abs(float(clean.get("end")) - float(orig.get("end"))) > _ASR_SPAN_TOL:
98
- return None
99
- except (TypeError, ValueError):
70
+ if (
71
+ abs(clean["start"] - orig["start"]) > _ASR_SPAN_TOL
72
+ or abs(clean["end"] - orig["end"]) > _ASR_SPAN_TOL
73
+ ):
100
74
  return None
101
75
  return segments
102
76
 
103
77
 
104
78
  def _is_mimo_chunk_usable(content):
105
- text = str(content or "").strip()
79
+ """A chunk is usable only if MiMo returned real analysis (not empty / a moderation refusal)."""
80
+ text = (content or "").strip()
106
81
  if not text:
107
82
  return False
108
83
  low = text.lower()
@@ -110,28 +85,23 @@ def _is_mimo_chunk_usable(content):
110
85
 
111
86
 
112
87
  def _mimo_video_settings_fingerprint():
88
+ """Non-secret MiMo video-overview settings that affect generated content."""
113
89
  return {
114
- "model": CONFIG.get("mimo_video_model")
115
- or CONFIG.get("mimo_model")
116
- or CONFIG.get("vlm_model"),
117
- "mimo_video_api_url": CONFIG.get("mimo_video_api_url"),
118
- "mimo_video_fps": CONFIG.get("mimo_video_fps", 2.0),
119
- "mimo_media_resolution": CONFIG.get("mimo_media_resolution", "default"),
120
- "mimo_video_chunk_max_seconds": CONFIG.get(
121
- "mimo_video_chunk_max_seconds", 20.0
122
- ),
123
- "mimo_video_chunk_min_seconds": CONFIG.get("mimo_video_chunk_min_seconds", 1.0),
124
- "mimo_video_base64_max_mb": CONFIG.get("mimo_video_base64_max_mb", 45.0),
125
- "mimo_video_prompt": CONFIG.get("mimo_video_prompt", ""),
126
- "mimo_disable_thinking": CONFIG.get("mimo_disable_thinking", True),
90
+ "model": CONFIG["mimo_video_model"],
91
+ "mimo_video_api_url": CONFIG["mimo_video_api_url"],
92
+ "mimo_video_fps": CONFIG["mimo_video_fps"],
93
+ "mimo_media_resolution": CONFIG["mimo_media_resolution"],
94
+ "mimo_video_chunk_max_seconds": CONFIG["mimo_video_chunk_max_seconds"],
95
+ "mimo_video_chunk_min_seconds": CONFIG["mimo_video_chunk_min_seconds"],
96
+ "mimo_video_base64_max_mb": CONFIG["mimo_video_base64_max_mb"],
97
+ "mimo_video_prompt": CONFIG["mimo_video_prompt"],
98
+ "mimo_disable_thinking": CONFIG["mimo_disable_thinking"],
127
99
  }
128
100
 
129
101
 
130
102
  def _mimo_chunk_cache_key(chunk):
131
- return (
132
- f"{chunk['chunk_id']}|{chunk['scene_id']}|"
133
- f"{float(chunk['start']):.3f}-{float(chunk['end']):.3f}"
134
- )
103
+ """Stable identifier for a MiMo chunk (index + scene span) for partial-cache reuse."""
104
+ return f"{chunk['chunk_id']}|{chunk['scene_id']}|{chunk['start']:.3f}-{chunk['end']:.3f}"
135
105
 
136
106
 
137
107
  def _mimo_cached_chunks_fingerprint(done):
@@ -144,48 +114,48 @@ def _mimo_overview_payload_fingerprint(overview):
144
114
  return stable_hash(payload)
145
115
 
146
116
 
147
- def _mimo_video_chunks_for_brief(scenes):
148
- max_seconds = float(CONFIG.get("mimo_video_chunk_max_seconds", 20.0) or 20.0)
149
- min_seconds = float(CONFIG.get("mimo_video_chunk_min_seconds", 1.0) or 1.0)
117
+ def _mimo_video_chunks(scenes):
118
+ """Split scene spans into MiMo video chunks. scenes.json rows carry no scene_id, so the
119
+ row index stands in for it there."""
120
+ max_seconds = CONFIG["mimo_video_chunk_max_seconds"]
121
+ min_seconds = CONFIG["mimo_video_chunk_min_seconds"]
150
122
  chunks = []
151
- for scene_index, scene in enumerate(scenes or []):
152
- if not isinstance(scene, dict):
153
- continue
154
- try:
155
- start = float(scene.get("start", 0.0))
156
- end = float(scene.get("end", start))
157
- except (TypeError, ValueError):
158
- continue
159
- if end <= start:
160
- continue
123
+ for scene_index, scene in enumerate(scenes):
124
+ start, end = scene["start"], scene["end"]
161
125
  scene_id = scene.get("scene_id", scene_index)
162
126
  cursor = start
163
127
  while cursor < end:
164
128
  chunk_end = min(end, cursor + max_seconds)
165
129
  if end - chunk_end < min_seconds and chunk_end < end:
166
130
  chunk_end = end
167
- if chunk_end > cursor:
168
- chunks.append(
169
- {
170
- "chunk_id": len(chunks),
171
- "scene_id": scene_id,
172
- "start": round(cursor, 3),
173
- "end": round(chunk_end, 3),
174
- }
175
- )
131
+ chunks.append(
132
+ {
133
+ "chunk_id": len(chunks),
134
+ "scene_id": scene_id,
135
+ "start": round(cursor, 3),
136
+ "end": round(chunk_end, 3),
137
+ }
138
+ )
176
139
  cursor = chunk_end
177
140
  return chunks
178
141
 
179
142
 
180
- def _mimo_overview_matches_current_inputs(overview, scenes_analysis, video_path=None):
143
+ def _mimo_overview_matches_current_inputs(overview, scenes, video_path=None):
144
+ """True only when mimo_video_overview.json was produced from the current settings,
145
+ source video and scene plan, and none of its chunks was moderation-rejected.
146
+ Provenance keys are read with .get: a stale or partial file simply does not match."""
181
147
  if not isinstance(overview, dict) or overview.get("input") != "scene_chunks":
182
148
  return False
183
- if overview.get("settings") != _mimo_video_settings_fingerprint():
184
- return False
185
- overview_fingerprint = overview.get("overview_fingerprint")
186
- if (
187
- not overview_fingerprint
188
- or overview_fingerprint != _mimo_overview_payload_fingerprint(overview)
149
+ settings = _mimo_video_settings_fingerprint()
150
+ expected_keys = [
151
+ _mimo_chunk_cache_key(chunk) for chunk in _mimo_video_chunks(scenes)
152
+ ]
153
+ chunks = overview.get("chunks")
154
+ if not isinstance(chunks, list) or not all(
155
+ isinstance(chunk, dict)
156
+ and isinstance(chunk.get("content"), str)
157
+ and _is_mimo_chunk_usable(chunk["content"])
158
+ for chunk in chunks
189
159
  ):
190
160
  return False
191
161
  if video_path is not None:
@@ -194,73 +164,60 @@ def _mimo_overview_matches_current_inputs(overview, scenes_analysis, video_path=
194
164
  return False
195
165
  except OSError:
196
166
  return False
197
- chunks = overview.get("chunks")
198
- if not isinstance(chunks, list) or not all(
199
- isinstance(chunk, dict) and _is_mimo_chunk_usable(chunk.get("content"))
200
- for chunk in chunks
201
- ):
202
- return False
203
- chunks_fingerprint = overview.get("chunks_fingerprint")
204
- if not chunks_fingerprint or chunks_fingerprint != _mimo_cached_chunks_fingerprint(
205
- chunks
206
- ):
207
- return False
208
- expected_chunks = _mimo_video_chunks_for_brief(scenes_analysis)
209
- if not expected_chunks or len(chunks) != len(expected_chunks):
210
- return False
211
167
  try:
212
168
  cached_keys = [_mimo_chunk_cache_key(chunk) for chunk in chunks]
213
- expected_keys = [_mimo_chunk_cache_key(chunk) for chunk in expected_chunks]
214
169
  except (KeyError, TypeError, ValueError):
215
170
  return False
216
- return cached_keys == expected_keys
171
+ return (
172
+ overview.get("settings") == settings
173
+ and overview.get("overview_fingerprint")
174
+ == _mimo_overview_payload_fingerprint(overview)
175
+ and overview.get("chunks_fingerprint")
176
+ == _mimo_cached_chunks_fingerprint(chunks)
177
+ and cached_keys == expected_keys
178
+ )
217
179
 
218
180
 
219
- def _load_mimo_overview_for_brief(
220
- work_dir, scenes_analysis, enabled=None, video_path=None
221
- ):
222
- overview_enabled = (
223
- CONFIG.get("mimo_video_overview", False) if enabled is None else enabled
224
- )
225
- if not overview_enabled:
226
- return {}
181
+ def _load_mimo_overview_for_brief(work_dir, scenes, enabled=None, video_path=None):
182
+ """The current MiMo overview for the brief, or None when disabled, absent or stale."""
183
+ if not (CONFIG["mimo_video_overview"] if enabled is None else enabled):
184
+ return None
227
185
  path = Path(work_dir) / "mimo_video_overview.json"
228
186
  if not path.exists():
229
- return {}
187
+ return None
230
188
  try:
231
189
  overview = json.loads(path.read_text(encoding="utf-8"))
232
- except (json.JSONDecodeError, OSError):
233
- return {}
234
- return (
235
- overview
236
- if _mimo_overview_matches_current_inputs(
237
- overview, scenes_analysis, video_path=video_path
238
- )
239
- else {}
240
- )
190
+ except (OSError, json.JSONDecodeError):
191
+ return None
192
+ if not _mimo_overview_matches_current_inputs(
193
+ overview, scenes, video_path=video_path
194
+ ):
195
+ return None
196
+ return overview
241
197
 
242
198
 
243
199
  def _load_optional_stage_status(work_dir, filename):
244
- """Load optional-stage status sidecars defensively."""
200
+ """Optional-stage status sidecar, or None when the stage never ran."""
245
201
  path = Path(work_dir) / filename
246
202
  if not path.exists():
247
- return {}
203
+ return None
248
204
  try:
249
- data = json.loads(path.read_text(encoding="utf-8"))
250
- except (json.JSONDecodeError, OSError):
251
- return {}
252
- return data if isinstance(data, dict) else {}
253
-
254
-
255
- def _status_message(message, limit=180):
256
- text = " ".join(str(message or "").split())
257
- return text[:limit]
205
+ status = json.loads(path.read_text(encoding="utf-8"))
206
+ except (OSError, json.JSONDecodeError):
207
+ return None
208
+ if not (
209
+ isinstance(status, dict)
210
+ and isinstance(status.get("enabled"), bool)
211
+ and isinstance(status.get("status"), str)
212
+ and isinstance(status.get("message"), str)
213
+ ):
214
+ return None
215
+ return status
258
216
 
259
217
 
260
218
  def _optional_stage_warning(stage, status, message):
261
219
  line = f"- {stage}: {status}"
262
- msg = _status_message(message)
263
- return f"{line} — {msg}" if msg else line
220
+ return f"{line} — {message[:180]}" if message else line
264
221
 
265
222
 
266
223
  def _format_optional_stage_warnings(
@@ -275,22 +232,23 @@ def _format_optional_stage_warnings(
275
232
  warnings = []
276
233
 
277
234
  overview_enabled = (
278
- bool(CONFIG.get("mimo_video_overview", False))
235
+ CONFIG["mimo_video_overview"]
279
236
  if mimo_overview_enabled is None
280
- else bool(mimo_overview_enabled)
237
+ else mimo_overview_enabled
281
238
  )
282
239
  overview_status = _load_optional_stage_status(
283
240
  work_dir, "mimo_video_overview.status.json"
284
241
  )
285
- if overview_status.get("enabled") and overview_status.get("status") in {
286
- "failed",
287
- "skipped_no_key",
288
- }:
242
+ if (
243
+ overview_status is not None
244
+ and overview_status["enabled"]
245
+ and overview_status["status"] in {"failed", "skipped_no_key"}
246
+ ):
289
247
  warnings.append(
290
248
  _optional_stage_warning(
291
249
  "mimo_video_overview",
292
- overview_status.get("status"),
293
- overview_status.get("message"),
250
+ overview_status["status"],
251
+ overview_status["message"],
294
252
  )
295
253
  )
296
254
  elif overview_enabled and not mimo_overview:
@@ -305,14 +263,14 @@ def _format_optional_stage_warnings(
305
263
  consolidation_status = _load_optional_stage_status(
306
264
  work_dir, "consolidation.status.json"
307
265
  )
308
- if consolidation_status.get("enabled"):
309
- if consolidation_status.get("status") == "failed":
266
+ if consolidation_status is not None and consolidation_status["enabled"]:
267
+ if consolidation_status["status"] == "failed":
310
268
  warnings.append(
311
269
  _optional_stage_warning(
312
- "consolidation", "failed", consolidation_status.get("message")
270
+ "consolidation", "failed", consolidation_status["message"]
313
271
  )
314
272
  )
315
- elif consolidation_status.get("do_index") and not consolidation_index:
273
+ elif consolidation_status.get("do_index") is True and not consolidation_index:
316
274
  warnings.append(
317
275
  _optional_stage_warning(
318
276
  "consolidation",