@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,61 +1,29 @@
1
1
  """Load and format research, consolidation, and substrate context."""
2
2
 
3
3
  import hashlib
4
-
5
- import importlib.util
6
-
7
-
8
4
  import json
9
-
10
-
11
5
  import re
12
-
13
6
  from pathlib import Path
14
7
 
15
8
  from lib import CONFIG
16
9
 
17
- from lib import log
18
-
19
- try:
20
- from deslop_qc import analyze_deslop_qc
21
- except ModuleNotFoundError:
22
- _deslop_qc_path = Path(__file__).with_name("deslop_qc.py")
23
- _deslop_qc_spec = importlib.util.spec_from_file_location(
24
- "deslop_qc", _deslop_qc_path
25
- )
26
- if _deslop_qc_spec is None or _deslop_qc_spec.loader is None:
27
- raise
28
- _deslop_qc_module = importlib.util.module_from_spec(_deslop_qc_spec)
29
- _deslop_qc_spec.loader.exec_module(_deslop_qc_module)
30
- analyze_deslop_qc = _deslop_qc_module.analyze_deslop_qc
31
-
32
10
 
33
11
  def _load_background_research(work_dir):
34
- """Load the agent-authored background_research.json if present.
35
-
36
- This file is the single highest-leverage quality input (character names,
37
- relationships, plot context). It used to be documented but never read by
38
- any code, so researched story knowledge never reached the writing brief.
39
- """
12
+ """Load the agent-authored background_research.json ({} when absent)."""
40
13
  path = Path(work_dir) / "background_research.json"
41
14
  if not path.exists():
42
15
  return {}
43
- try:
44
- data = json.loads(path.read_text(encoding="utf-8"))
45
- except (json.JSONDecodeError, OSError) as exc:
46
- log(f"警告: background_research.json 读取失败,忽略: {exc}")
47
- return {}
48
- return data if isinstance(data, dict) else {}
16
+ return json.loads(path.read_text(encoding="utf-8"))
49
17
 
50
18
 
51
19
  def _clip_text(text, limit):
52
- value = re.sub(r"\s+", " ", str(text or "")).strip()
53
- return value[:limit]
20
+ """Collapse whitespace and clip. Research fields are optional, so None clips to ""."""
21
+ return re.sub(r"\s+", " ", str(text or "")).strip()[:limit]
54
22
 
55
23
 
56
24
  def _format_background_research(research, limit=1800):
57
25
  """Render background_research.json into a bounded Story-context brief section."""
58
- if not isinstance(research, dict) or not research:
26
+ if not research:
59
27
  return []
60
28
  lines = [
61
29
  "## Story context (from background_research.json)",
@@ -72,50 +40,33 @@ def _format_background_research(research, limit=1800):
72
40
  if value:
73
41
  lines.append(f"- {label}: {value}")
74
42
 
75
- characters = research.get("characters")
76
- if isinstance(characters, dict) and characters:
43
+ characters = research.get("characters", {})
44
+ if characters:
77
45
  lines.append("- Characters:")
78
46
  for name, desc in list(characters.items())[:12]:
79
- clean_name = _clip_text(name, 60)
80
- clean_desc = _clip_text(desc, 160)
81
- if clean_name:
82
- lines.append(f" - {clean_name}: {clean_desc}")
47
+ lines.append(f" - {_clip_text(name, 60)}: {_clip_text(desc, 160)}")
83
48
 
84
- details = research.get("character_details")
85
- if isinstance(details, dict) and details:
49
+ details = research.get("character_details", {})
50
+ if details:
86
51
  lines.append("- Character details:")
87
52
  for name, info in list(details.items())[:8]:
88
- if not isinstance(info, dict):
89
- continue
90
53
  bits = []
91
- aliases = info.get("aliases")
92
- if isinstance(aliases, list) and aliases:
93
- clean_aliases = [_clip_text(alias, 40) for alias in aliases[:4]]
94
- clean_aliases = [alias for alias in clean_aliases if alias]
95
- if clean_aliases:
96
- bits.append("别名 " + "/".join(clean_aliases))
54
+ aliases = [_clip_text(alias, 40) for alias in info.get("aliases", [])[:4]]
55
+ if aliases:
56
+ bits.append("别名 " + "/".join(aliases))
97
57
  role = _clip_text(info.get("role"), 80)
98
58
  if role:
99
59
  bits.append(role)
100
- rels = info.get("relationships")
101
- if isinstance(rels, list) and rels:
102
- clean_rels = [_clip_text(rel, 80) for rel in rels[:4]]
103
- clean_rels = [rel for rel in clean_rels if rel]
104
- if clean_rels:
105
- bits.append(";".join(clean_rels))
106
- clean_name = _clip_text(name, 60)
107
- if clean_name and bits:
108
- lines.append(f" - {clean_name}: {'; '.join(bits)}")
109
-
110
- arcs = research.get("plot_arcs")
111
- if isinstance(arcs, list) and arcs:
60
+ rels = [_clip_text(rel, 80) for rel in info.get("relationships", [])[:4]]
61
+ if rels:
62
+ bits.append(";".join(rels))
63
+ if bits:
64
+ lines.append(f" - {_clip_text(name, 60)}: {'; '.join(bits)}")
65
+
66
+ arcs = research.get("plot_arcs", [])
67
+ if arcs:
112
68
  lines.append("- Plot arcs:")
113
69
  for arc in arcs[:8]:
114
- if not isinstance(arc, dict):
115
- value = _clip_text(arc, 180)
116
- if value:
117
- lines.append(f" - {value}")
118
- continue
119
70
  name = _clip_text(arc.get("name"), 80)
120
71
  desc = _clip_text(arc.get("description"), 180)
121
72
  status = _clip_text(arc.get("status"), 40)
@@ -123,15 +74,10 @@ def _format_background_research(research, limit=1800):
123
74
  tail = f" [{status}]" if status else ""
124
75
  lines.append(f" - {name}: {desc}{tail}".rstrip())
125
76
 
126
- notes = research.get("cultural_notes")
127
- if isinstance(notes, list) and notes:
77
+ notes = research.get("cultural_notes", [])
78
+ if notes:
128
79
  lines.append("- Cultural notes:")
129
80
  for note in notes[:6]:
130
- if not isinstance(note, dict):
131
- value = _clip_text(note, 160)
132
- if value:
133
- lines.append(f" - {value}")
134
- continue
135
81
  item = _clip_text(note.get("item"), 80)
136
82
  expl = _clip_text(note.get("explanation"), 160)
137
83
  if item and expl:
@@ -150,7 +96,8 @@ def _format_background_research(research, limit=1800):
150
96
  if len(text) <= limit:
151
97
  return lines
152
98
  clipped = text[:limit].rsplit("\n", 1)[0].rstrip()
153
- return clipped.splitlines() + [
99
+ return [
100
+ *clipped.splitlines(),
154
101
  "",
155
102
  "[Story context clipped to keep ASR/visual evidence in context]",
156
103
  "",
@@ -169,16 +116,9 @@ def assess_understanding_substrate(
169
116
  so it must not grade as "rich"; otherwise the sparse-substrate warning, the research
170
117
  directive, and the density relief never fire for exactly that cold-narration case.
171
118
  """
172
- scenes = scenes_analysis or []
173
- asr_chars = sum(len(str(seg.get("text", "")).strip()) for seg in (asr_result or []))
174
- scenes_with_facts = sum(
175
- 1 for s in scenes if isinstance(s, dict) and s.get("frame_facts")
176
- )
177
- desc_lens = [
178
- len(str(s.get("description", "")).strip())
179
- for s in scenes
180
- if isinstance(s, dict)
181
- ]
119
+ asr_chars = sum(len(seg["text"]) for seg in asr_result)
120
+ scenes_with_facts = sum(1 for s in scenes_analysis if s.get("frame_facts"))
121
+ desc_lens = [len(s["description"]) for s in scenes_analysis]
182
122
  avg_desc = sum(desc_lens) // len(desc_lens) if desc_lens else 0
183
123
 
184
124
  has_asr = asr_chars >= 20
@@ -193,7 +133,7 @@ def assess_understanding_substrate(
193
133
  return {
194
134
  "level": level,
195
135
  "asr_chars": asr_chars,
196
- "scene_count": len(scenes),
136
+ "scene_count": len(scenes_analysis),
197
137
  "scenes_with_frame_facts": scenes_with_facts,
198
138
  "avg_description_len": avg_desc,
199
139
  "has_story_context": bool(has_story_context),
@@ -202,7 +142,7 @@ def assess_understanding_substrate(
202
142
 
203
143
  def _format_substrate_warning(assessment):
204
144
  """Render a loud brief banner when the understanding substrate is weak."""
205
- if not assessment or assessment.get("level") == "rich":
145
+ if assessment["level"] == "rich":
206
146
  return []
207
147
  if assessment["level"] == "empty":
208
148
  head = "⚠️ UNDERSTANDING SUBSTRATE IS EMPTY — narration will be generic guesswork unless you fix this first."
@@ -236,8 +176,8 @@ def _format_asr_chunks_for_brief(chunks, max_chunks=24):
236
176
  "",
237
177
  ]
238
178
  for chunk in chunks[:max_chunks]:
239
- scene_ids = ",".join(str(sid) for sid in chunk.get("scene_ids", [])) or "n/a"
240
- text = str(chunk.get("text", "")).strip()
179
+ scene_ids = ",".join(str(sid) for sid in chunk["scene_ids"]) or "n/a"
180
+ text = chunk["text"]
241
181
  if len(text) > 900:
242
182
  text = text[:897] + "..."
243
183
  lines.extend(
@@ -266,29 +206,27 @@ def _format_timeline_fusion_for_brief(fusion, max_items=40):
266
206
  "",
267
207
  ]
268
208
  for item in fusion[:max_items]:
269
- start, end = item.get("time_range", [0, 0])
270
- slots = item.get("narration_slots") or []
209
+ start, end = item["time_range"]
271
210
  slot_text = (
272
211
  ", ".join(
273
212
  f"{slot['start']:.1f}-{slot['end']:.1f}s/{slot['char_budget']}字"
274
- for slot in slots[:4]
213
+ for slot in item["narration_slots"][:4]
275
214
  )
276
215
  or "none"
277
216
  )
278
- dialogue = item.get("dialogue_segments") or []
279
217
  dialogue_text = (
280
218
  "; ".join(
281
- f"{seg['start']:.1f}-{seg['end']:.1f}s {seg.get('text', '')[:80]}"
282
- for seg in dialogue[:3]
283
- if seg.get("text")
219
+ f"{seg['start']:.1f}-{seg['end']:.1f}s {seg['text'][:80]}"
220
+ for seg in item["dialogue_segments"][:3]
221
+ if seg["text"]
284
222
  )
285
223
  or "none"
286
224
  )
287
225
  lines.extend(
288
226
  [
289
- f"### Fusion scene {item.get('scene_id')}: {start:.1f}-{end:.1f}s ({item.get('recommended_mode')})",
290
- f"- Visual: {item.get('visual_description', '')}",
291
- f"- Dialogue overlap: {item.get('dialogue_overlap_seconds', 0):.1f}s | {dialogue_text}",
227
+ f"### Fusion scene {item['scene_id']}: {start:.1f}-{end:.1f}s ({item['recommended_mode']})",
228
+ f"- Visual: {item['visual_description']}",
229
+ f"- Dialogue overlap: {item['dialogue_overlap_seconds']:.1f}s | {dialogue_text}",
292
230
  f"- Narration slots: {slot_text}",
293
231
  "",
294
232
  ]
@@ -302,7 +240,7 @@ def _format_timeline_fusion_for_brief(fusion, max_items=40):
302
240
 
303
241
 
304
242
  def _consolidation_model():
305
- return CONFIG.get("vlm_model", "")
243
+ return CONFIG["vlm_model"]
306
244
 
307
245
 
308
246
  def _clean_asr_prompt_fingerprint():
@@ -332,70 +270,77 @@ def _index_prompt_fingerprint():
332
270
  return hashlib.md5(prompt.encode("utf-8")).hexdigest()
333
271
 
334
272
 
335
- def _load_consolidation(work_dir, scenes_analysis=None):
336
- """Load consolidate.py's understanding_index.json only when provenance matches VLM input."""
273
+ def _load_consolidation(work_dir, scenes_analysis):
274
+ """Load consolidate.py's understanding_index.json only when its provenance matches
275
+ the current vlm_analysis.json, model and index prompt ({} otherwise)."""
337
276
  work_dir = Path(work_dir)
338
277
  path = work_dir / "understanding_index.json"
339
278
  meta_path = work_dir / "understanding_index.json.meta.json"
340
279
  if not path.exists() or not meta_path.exists():
341
280
  return {}
342
281
  try:
343
- data = json.loads(path.read_text(encoding="utf-8"))
344
282
  meta = json.loads(meta_path.read_text(encoding="utf-8"))
345
- except (json.JSONDecodeError, OSError):
283
+ source_md5 = hashlib.md5(
284
+ (work_dir / "vlm_analysis.json").read_bytes()
285
+ ).hexdigest()
286
+ except (OSError, json.JSONDecodeError):
346
287
  return {}
347
- if not isinstance(data, dict) or not isinstance(meta, dict):
288
+ if not isinstance(meta, dict):
348
289
  return {}
349
- src_path = work_dir / "vlm_analysis.json"
350
- try:
351
- source_md5 = hashlib.md5(src_path.read_bytes()).hexdigest()
352
- except OSError:
290
+ expected = {
291
+ "source_md5": source_md5,
292
+ "model": _consolidation_model(),
293
+ "prompt_md5": _index_prompt_fingerprint(),
294
+ }
295
+ if scenes_analysis:
296
+ expected["scene_count"] = len(scenes_analysis)
297
+ if not meta.items() >= expected.items():
353
298
  return {}
354
- if meta.get("source_md5") != source_md5:
299
+ try:
300
+ index = json.loads(path.read_text(encoding="utf-8"))
301
+ except (OSError, json.JSONDecodeError):
355
302
  return {}
356
- expected_count = len([s for s in (scenes_analysis or []) if isinstance(s, dict)])
357
- if expected_count and meta.get("scene_count") != expected_count:
303
+ if not isinstance(index, dict) or not all(
304
+ isinstance(index.get(key), list)
305
+ for key in ("characters", "relationships", "plot_points", "entities")
306
+ ):
358
307
  return {}
359
- if meta.get("model") != _consolidation_model():
308
+ if not all(isinstance(item, dict) for item in index["characters"]):
360
309
  return {}
361
- if meta.get("prompt_md5") != _index_prompt_fingerprint():
310
+ if not all(isinstance(item, dict) for item in index["relationships"]):
362
311
  return {}
363
- return data
312
+ return index
364
313
 
365
314
 
366
315
  def _format_consolidation(index):
367
- """Render consolidate.py's understanding_index.json into a compact brief section."""
316
+ """Render consolidate.py's understanding_index.json into a compact brief section.
317
+
318
+ plot_points/entities items are model output and may be bare strings or objects."""
368
319
  if not index:
369
320
  return []
370
321
  lines = ["## Understanding index (from consolidate.py)", ""]
371
- chars = index.get("characters") or []
322
+ chars = index["characters"]
372
323
  if chars:
373
324
  lines.append("- Characters:")
374
325
  for c in chars:
375
- if isinstance(c, dict):
376
- lines.append(
377
- f" - {c.get('name', '?')}: {str(c.get('description', '')).strip()}"
378
- )
379
- else:
380
- lines.append(f" - {c}")
381
- rels = index.get("relationships") or []
326
+ lines.append(
327
+ f" - {c.get('name', '?')}: {str(c.get('description', '')).strip()}"
328
+ )
329
+ rels = index["relationships"]
382
330
  if rels:
383
331
  lines.append("- Relationships:")
384
332
  for r in rels:
385
- if isinstance(r, dict):
386
- lines.append(
387
- f" - {r.get('a', '?')} — {r.get('relation', '?')} — {r.get('b', '?')}"
388
- )
389
- else:
390
- lines.append(f" - {r}")
391
- plot = index.get("plot_points") or []
333
+ lines.append(
334
+ f" - {r.get('a', '?')} — {r.get('relation', '?')} — {r.get('b', '?')}"
335
+ )
336
+ plot = index["plot_points"]
392
337
  if plot:
393
338
  lines.append("- Plot spine:")
394
339
  lines.extend(
395
340
  f" {i + 1}. {p.get('text', p) if isinstance(p, dict) else p}"
396
341
  for i, p in enumerate(plot)
397
342
  )
398
- ents = index.get("entities") or []
343
+ ents = index["entities"]
399
344
  if ents:
400
345
  ent_names = [str(e.get("name", e) if isinstance(e, dict) else e) for e in ents]
401
346
  lines.append(f"- Entities: {', '.join(ent_names)}")