@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
@@ -5,20 +5,19 @@ from pathlib import Path
5
5
  from lib import CONFIG, log
6
6
 
7
7
  def _maybe_export_jianying(work_dir, out_dir, stem):
8
- """Lazy-import the optional 剪映 exporter and write a draft from timeline.json."""
9
- timeline_path = Path(work_dir) / "timeline.json"
10
- if not timeline_path.exists():
11
- log(" ⚠️ 跳过剪映导出:未找到 timeline.json")
12
- return
8
+ """Lazy-import the optional 剪映 exporter and write a draft from timeline.json.
9
+
10
+ The export is a documented fail-open sidecar: any failure is logged and never
11
+ fails the already-rendered recap."""
13
12
  try:
14
13
  from export_jianying import export_timeline_to_jianying
15
14
  from timeline import load_timeline
16
- parent = out_dir or CONFIG.get("jianying_draft_dir") or str(work_dir)
15
+ parent = out_dir or CONFIG["jianying_draft_dir"] or str(work_dir)
17
16
  draft_dir, notes = export_timeline_to_jianying(
18
- load_timeline(timeline_path), parent, draft_name=f"recap_{stem}",
19
- bundle_media=CONFIG.get("jianying_bundle_media", False))
17
+ load_timeline(Path(work_dir) / "timeline.json"), parent, draft_name=f"recap_{stem}",
18
+ bundle_media=CONFIG["jianying_bundle_media"])
20
19
  for n in notes:
21
20
  log(f" 注意: {n}")
22
21
  log(f"剪映草稿已导出: {draft_dir}")
23
- except Exception as exc: # optional feature must never fail the render
22
+ except Exception as exc:
24
23
  log(f" ⚠️ 剪映导出失败(不影响成片): {exc}")
@@ -8,15 +8,7 @@ from jianying_templates import template
8
8
 
9
9
  # The full 剪映 materials object: ~45 parallel arrays. Only arrays backed by a
10
10
  # production builder are populated; retaining the full shape preserves compatibility.
11
- MATERIAL_KEYS = (
12
- "ai_translates audio_balances audio_effects audio_fades audio_track_indexes audios "
13
- "beats canvases chromas color_curves digital_humans drafts effects flowers green_screens "
14
- "handwrites hsl images log_color_wheels loudnesses manual_deformations masks common_mask "
15
- "material_animations material_colors multi_language_refs placeholders plugin_effects "
16
- "primary_color_wheels realtime_denoises shapes smart_crops smart_relights "
17
- "sound_channel_mappings speeds stickers tail_leaders text_templates texts time_marks "
18
- "transitions video_effects video_trackings videos vocal_beautifys vocal_separations"
19
- ).split()
11
+ MATERIAL_KEYS = ["ai_translates", "audio_balances", "audio_effects", "audio_fades", "audio_track_indexes", "audios", "beats", "canvases", "chromas", "color_curves", "digital_humans", "drafts", "effects", "flowers", "green_screens", "handwrites", "hsl", "images", "log_color_wheels", "loudnesses", "manual_deformations", "masks", "common_mask", "material_animations", "material_colors", "multi_language_refs", "placeholders", "plugin_effects", "primary_color_wheels", "realtime_denoises", "shapes", "smart_crops", "smart_relights", "sound_channel_mappings", "speeds", "stickers", "tail_leaders", "text_templates", "texts", "time_marks", "transitions", "video_effects", "video_trackings", "videos", "vocal_beautifys", "vocal_separations"]
20
12
 
21
13
 
22
14
  def us(seconds):
@@ -34,11 +26,8 @@ def full_materials(filled):
34
26
  def scrub_platform_identity(project):
35
27
  """Remove hardware fingerprints carried by the pinned upstream templates."""
36
28
  for platform_key in ("last_modified_platform", "platform"):
37
- platform = project.get(platform_key)
38
- if not isinstance(platform, dict):
39
- continue
40
29
  for identity_key in ("device_id", "hard_disk_id", "mac_address"):
41
- platform[identity_key] = ""
30
+ project[platform_key][identity_key] = ""
42
31
  return project
43
32
 
44
33
 
@@ -96,7 +85,7 @@ def material_category_registry():
96
85
 
97
86
  def validate_material_category(category):
98
87
  """Return deterministic support metadata for a public or future material kind."""
99
- normalized = (category or "").strip().lower()
88
+ normalized = category.strip().lower()
100
89
  registry = material_category_registry()
101
90
  info = dict(registry.get(normalized, {"status": "unsupported", "materials_key": None, "track_type": None}))
102
91
  info["category"] = normalized
@@ -9,18 +9,12 @@ RESOURCE_TRACK_KINDS = {
9
9
  "face_effect", "sound", "sticker", "text_template", "video_effect",
10
10
  }
11
11
 
12
-
13
12
  def _error(path, expectation):
14
13
  raise ValueError(f"invalid timeline {path}: {expectation}")
15
14
 
16
15
 
17
16
  def _is_number(value):
18
- if not isinstance(value, (int, float)) or isinstance(value, bool):
19
- return False
20
- try:
21
- return math.isfinite(value)
22
- except OverflowError:
23
- return False
17
+ return isinstance(value, (int, float)) and not isinstance(value, bool) and math.isfinite(value)
24
18
 
25
19
 
26
20
  def _field_path(path, key):
@@ -88,6 +82,19 @@ def _validate_video_clip(clip, path):
88
82
  _error(f"{path}.compound", "must be a boolean")
89
83
 
90
84
 
85
+ def _validate_resource_config(config, path):
86
+ if not isinstance(config, dict):
87
+ _error(path, "must be an object")
88
+ if not isinstance(config.get("main_config"), dict):
89
+ _error(f"{path}.main_config", "must be an object")
90
+ resources = config.get("resources", [])
91
+ if not isinstance(resources, list) or any(
92
+ not isinstance(item, dict) or not isinstance(item.get("source_path"), str)
93
+ for item in resources
94
+ ):
95
+ _error(f"{path}.resources", "must contain source_path objects")
96
+
97
+
91
98
  def _validate_segment(segment, path, kind):
92
99
  if not isinstance(segment, dict):
93
100
  _error(path, "must be an object")
@@ -110,8 +117,8 @@ def _validate_segment(segment, path, kind):
110
117
  _error(f"{path}.resource_package", "must be a non-empty string")
111
118
  elif sources[0] == "material" and not isinstance(source, dict):
112
119
  _error(f"{path}.material", "must be an object")
113
- elif sources[0] == "resource_config" and not isinstance(source, (dict, str)):
114
- _error(f"{path}.resource_config", "must be an object or JSON file path")
120
+ elif sources[0] == "resource_config":
121
+ _validate_resource_config(source, f"{path}.resource_config")
115
122
 
116
123
 
117
124
  def _validate_track(track, path):
@@ -158,9 +165,13 @@ def _validate_v2(timeline):
158
165
  _error("canvas.fps", "must be greater than 0")
159
166
 
160
167
  _require_number(timeline, "duration", "", minimum=0)
161
- for registry_name in ("resource_packages", "style_presets"):
162
- if registry_name in timeline and not isinstance(timeline[registry_name], dict):
163
- _error(registry_name, "must be an object")
168
+ resource_packages = timeline.get("resource_packages", {})
169
+ if not isinstance(resource_packages, dict):
170
+ _error("resource_packages", "must be an object")
171
+ for name, config in resource_packages.items():
172
+ _validate_resource_config(config, f"resource_packages.{name}")
173
+ if "style_presets" in timeline and not isinstance(timeline["style_presets"], dict):
174
+ _error("style_presets", "must be an object")
164
175
  tracks = timeline.get("tracks")
165
176
  if not isinstance(tracks, list):
166
177
  _error("tracks", "must be an array")
@@ -60,10 +60,7 @@ class TrackAllocator:
60
60
  return any(int(start_us) < old_end and end_us > old_start for old_start, old_end in existing)
61
61
 
62
62
  def allocate(self, kind, base_name, start_us, duration_us):
63
- band = TRACK_LAYOUT_BANDS.get(kind)
64
- if band is None:
65
- band = TrackBand(kind, "video", 120_000, "unregistered fallback")
66
- base_name = base_name or kind
63
+ band = TRACK_LAYOUT_BANDS[kind]
67
64
  suffix = 0
68
65
  while True:
69
66
  name = base_name if suffix == 0 else f"{base_name}-{suffix}"
@@ -118,17 +118,19 @@ def _replace_value(value, old, new):
118
118
  elif isinstance(value, str):
119
119
  if value == old:
120
120
  return new
121
- stripped = value.lstrip()
122
- if stripped.startswith(("{", "[")):
123
- try:
124
- parsed = json.loads(value)
125
- except json.JSONDecodeError:
126
- return value
127
- replaced = _replace_value(parsed, old, new)
128
- return json.dumps(replaced, ensure_ascii=False, separators=(",", ":"))
129
121
  return value
130
122
 
131
123
 
124
+ def _replace_material_resource_path(material, materials_key, old, new):
125
+ """Rewrite one declared resource, including the known rich-text JSON field."""
126
+ _replace_value(material, old, new)
127
+ if materials_key != "texts" or not isinstance(material.get("content"), str):
128
+ return
129
+ content = json.loads(material["content"])
130
+ _replace_value(content, old, new)
131
+ material["content"] = json.dumps(content, ensure_ascii=False, separators=(",", ":"))
132
+
133
+
132
134
  def _safe_resource_target(target_path):
133
135
  normalized = os.path.normpath(str(target_path).replace("\\", "/"))
134
136
  if normalized in {"", "."} or os.path.isabs(normalized):
@@ -182,16 +184,11 @@ def _is_packaged_path(value):
182
184
 
183
185
 
184
186
  def _descriptor(raw, default_kind, *, required):
185
- if isinstance(raw, str):
186
- source = raw
187
- resource_kind = default_kind
188
- target_path = None
189
- elif isinstance(raw, dict):
190
- source = raw.get("source_path") or raw.get("source")
191
- resource_kind = raw.get("resource_kind") or raw.get("kind") or default_kind
192
- target_path = raw.get("target_path")
193
- else:
194
- raise ValueError("JianYing resources entries must be paths or objects")
187
+ if not isinstance(raw, dict):
188
+ raise ValueError("JianYing resources entries must be objects")
189
+ source = raw.get("source_path")
190
+ resource_kind = raw.get("resource_kind", default_kind)
191
+ target_path = raw.get("target_path")
195
192
  if not isinstance(source, str) or not source:
196
193
  raise ValueError("JianYing resource source_path must be a non-empty string")
197
194
  if not isinstance(resource_kind, str) or resource_kind not in {
@@ -199,11 +196,6 @@ def _descriptor(raw, default_kind, *, required):
199
196
  "text", "text_template", "transition", "video",
200
197
  }:
201
198
  raise ValueError(f"invalid JianYing resource kind: {resource_kind}")
202
- suffix = os.path.splitext(source)[1].lower()
203
- if suffix == ".cube":
204
- resource_kind = "lut"
205
- elif suffix in {".ttf", ".otf"}:
206
- resource_kind = "fonts"
207
199
  return {
208
200
  "source_path": source,
209
201
  "resource_kind": resource_kind,
@@ -220,7 +212,12 @@ def _material_resource_descriptors(material, default_kind):
220
212
  path = material.get("path")
221
213
  if isinstance(path, str) and path and not _is_packaged_path(path):
222
214
  if not any(item["source_path"] == path for item in descriptors):
223
- descriptors.append(_descriptor(path, default_kind, required=False))
215
+ descriptors.append({
216
+ "source_path": path,
217
+ "resource_kind": default_kind,
218
+ "target_path": None,
219
+ "required": False,
220
+ })
224
221
  return descriptors
225
222
 
226
223
 
@@ -308,7 +305,9 @@ def bundle_media(content, meta, draft_dir):
308
305
  copied[source_key] = {"draft_path": draft_path}
309
306
  else:
310
307
  draft_path = existing["draft_path"]
311
- _replace_value(material, src, draft_path)
308
+ _replace_material_resource_path(
309
+ material, materials_key, src, draft_path
310
+ )
312
311
 
313
312
  material_group = next(group for group in meta["draft_materials"] if group["type"] == 0)
314
313
  material_group["value"] = meta_values
@@ -1,4 +1,5 @@
1
1
  """Self-contained config + utilities for this skill (no cross-skill imports)."""
2
+ import math
2
3
  import os
3
4
  import subprocess
4
5
  from pathlib import Path
@@ -7,85 +8,40 @@ from pathlib import Path
7
8
  # ── 配置 ──────────────────────────────────────────────────────────────
8
9
  _EXISTING_CONFIG_REF = globals().get("CONFIG")
9
10
 
10
- DEFAULT_MIMO_API_URL = "https://api.xiaomimimo.com/v1"
11
- DEFAULT_MIMO_TOKEN_PLAN_CLUSTER = "cn"
12
- MIMO_TOKEN_PLAN_API_URLS = {
13
- "cn": "https://token-plan-cn.xiaomimimo.com/v1",
14
- "sgp": "https://token-plan-sgp.xiaomimimo.com/v1",
15
- "ams": "https://token-plan-ams.xiaomimimo.com/v1",
16
- }
17
- DEFAULT_MIMO_MODEL = "mimo-v2.5" # VLM / chat (vision understanding)
18
- DEFAULT_MIMO_ASR_MODEL = "mimo-v2.5-asr" # speech-to-text
19
- DEFAULT_MIMO_TTS_MODEL = "mimo-v2.5-tts" # text-to-speech
20
-
21
-
22
- def normalize_api_url(raw_url):
23
- """Normalize a MiMo (OpenAI-compatible) base URL or chat/completions endpoint."""
24
- url = (raw_url or DEFAULT_MIMO_API_URL).rstrip("/")
25
- if url.endswith("/chat/completions"):
26
- return url
27
- return f"{url}/chat/completions"
28
-
29
-
30
- def is_mimo_token_plan_key(api_key):
31
- """Return True for Xiaomi MiMo Token Plan keys, which use token-plan base URLs."""
32
- return str(api_key or "").strip().startswith("tp-")
33
-
34
-
35
- def default_mimo_api_url(api_key="", cluster=None):
36
- """Pick the correct MiMo base URL for pay-as-you-go vs Token Plan keys.
37
-
38
- MiMo uses independent credentials for pay-as-you-go (`sk-*`) and Token Plan
39
- (`tp-*`). Token Plan keys must be sent to the Token Plan cluster base URL,
40
- not the pay-as-you-go `api.xiaomimimo.com` endpoint.
41
- """
42
- if is_mimo_token_plan_key(api_key):
43
- cluster_name = (cluster or os.environ.get("MIMO_TOKEN_PLAN_CLUSTER") or DEFAULT_MIMO_TOKEN_PLAN_CLUSTER)
44
- cluster_name = str(cluster_name).strip().lower()
45
- return MIMO_TOKEN_PLAN_API_URLS.get(cluster_name, MIMO_TOKEN_PLAN_API_URLS[DEFAULT_MIMO_TOKEN_PLAN_CLUSTER])
46
- return DEFAULT_MIMO_API_URL
47
-
48
11
 
49
12
  def env_int(name, default, *, minimum=None):
50
- """Read an integer env var; ignore malformed values instead of crashing import."""
51
- raw = os.environ.get(name)
52
- if raw is None or raw == "":
13
+ """Read an integer env var; an unset/empty value yields `default`, a malformed one is an error."""
14
+ raw = os.environ.get(name, "")
15
+ if raw == "":
53
16
  return default
54
17
  try:
55
18
  value = int(raw)
56
- except (TypeError, ValueError):
57
- return default
58
- if minimum is not None:
59
- value = max(minimum, value)
60
- return value
19
+ except ValueError as exc:
20
+ raise ValueError(f"环境变量 {name}={raw!r} 不是整数") from exc
21
+ return value if minimum is None else max(minimum, value)
61
22
 
62
23
 
63
24
  def env_bool(name, default=False):
64
25
  """Read common boolean env var forms."""
65
- raw = os.environ.get(name)
66
- if raw is None or raw == "":
26
+ raw = os.environ.get(name, "")
27
+ if raw == "":
67
28
  return default
68
29
  return raw.strip().lower() in {"1", "true", "yes", "y", "on"}
69
30
 
70
31
 
71
32
  def env_float(name, default, *, minimum=None):
72
- """Read a float env var; ignore malformed values instead of crashing import."""
73
- raw = os.environ.get(name)
74
- if raw is None or raw == "":
33
+ """Read a float env var; an unset/empty value yields `default`, a malformed one is an error."""
34
+ raw = os.environ.get(name, "")
35
+ if raw == "":
75
36
  return default
76
37
  try:
77
38
  value = float(raw)
78
- except (TypeError, ValueError):
79
- return default
80
- if minimum is not None:
81
- value = max(minimum, value)
82
- return value
83
-
39
+ except ValueError as exc:
40
+ raise ValueError(f"环境变量 {name}={raw!r} 不是数字") from exc
41
+ if not math.isfinite(value):
42
+ raise ValueError(f"环境变量 {name}={raw!r} 必须是有限数")
43
+ return value if minimum is None else max(minimum, value)
84
44
 
85
- # Single MiMo credential powers ASR + VLM + TTS. Per-capability overrides
86
- # (MIMO_VIDEO_API_KEY / MIMO_TTS_API_KEY / MIMO_ASR_API_KEY and their *_API_URL forms)
87
- # are optional and fall back to MIMO_API_KEY / MIMO_API_URL. Token-Plan keys (tp-*) auto-
88
- # route to the Token-Plan cluster base URL; pay-as-you-go keys use api.xiaomimimo.com.
89
45
 
90
46
  # Cross-language source: when the original audio is in a language the narration is NOT in
91
47
  # (e.g. a Japanese drama recapped in Chinese), the original speech bleeding under the narration
@@ -181,9 +137,8 @@ if isinstance(_EXISTING_CONFIG_REF, dict):
181
137
  CONFIG = _EXISTING_CONFIG_REF
182
138
 
183
139
  SCRIPT_DIR = Path(__file__).parent
184
- PROMPTS_DIR = SCRIPT_DIR.parent / "references"
185
140
 
186
- def narration_tempo_budget(tts_rate_offset=0.0, *, config=None):
141
+ def narration_tempo_budget(tts_rate_offset=0.0):
187
142
  """Return the canonical tempo budget shared by voiceover and assemble.
188
143
 
189
144
  `effective_tempo` is the user-perceived cumulative compression:
@@ -192,13 +147,13 @@ def narration_tempo_budget(tts_rate_offset=0.0, *, config=None):
192
147
  offset; callers must fail/shorten instead of time-trimming speech when the
193
148
  needed ratio exceeds `segment_tempo_max`.
194
149
  """
195
- cfg = config or CONFIG
196
- global_speed = max(0.01, float(cfg.get("narration_speed", 1.0) or 1.0))
197
- rate_factor = max(0.01, 1.0 + float(tts_rate_offset or 0.0))
198
- cumulative_max = max(1.0, float(cfg.get("narration_cumulative_tempo_max", 1.35) or 1.35))
199
- hard_max = max(cumulative_max, float(cfg.get("narration_cumulative_tempo_hard_max", 1.40) or 1.40))
200
- legacy_segment_cap = max(1.0, float(cfg.get("tts_segment_tempo_max", 1.20) or 1.20))
201
- segment_tempo_max = max(1.0, min(legacy_segment_cap, cumulative_max / (global_speed * rate_factor)))
150
+ global_speed = CONFIG["narration_speed"]
151
+ rate_factor = max(0.01, 1.0 + float(tts_rate_offset))
152
+ cumulative_max = CONFIG["narration_cumulative_tempo_max"]
153
+ hard_max = max(cumulative_max, CONFIG["narration_cumulative_tempo_hard_max"])
154
+ segment_tempo_max = max(1.0, min(
155
+ CONFIG["tts_segment_tempo_max"], cumulative_max / (global_speed * rate_factor)
156
+ ))
202
157
  return {
203
158
  "global_narration_speed": global_speed,
204
159
  "tts_rate_factor": rate_factor,
@@ -213,16 +168,10 @@ def log(msg):
213
168
 
214
169
  def run_cmd(cmd, **kwargs):
215
170
  """运行命令,返回 CompletedProcess"""
216
- if isinstance(cmd, list):
217
- display_parts = []
218
- for part in cmd:
219
- text = str(part)
220
- display_parts.append(text if len(text) <= 240 else text[:237] + "...")
221
- display = " ".join(display_parts)
222
- else:
223
- display = str(cmd)
224
- if len(display) > 2000:
225
- display = display[:1997] + "..."
171
+ display = " ".join(
172
+ text if len(text) <= 240 else text[:237] + "..."
173
+ for text in map(str, cmd)
174
+ )
226
175
  log(f"运行: {display}")
227
176
  return subprocess.run(cmd, capture_output=True, text=True, **kwargs)
228
177
 
@@ -232,8 +181,5 @@ def get_video_duration(video_path):
232
181
  "-of", "csv=p=0", str(video_path)]
233
182
  result = run_cmd(cmd)
234
183
  if result.returncode != 0:
235
- return 0.0
236
- try:
237
- return float(result.stdout.strip())
238
- except (TypeError, ValueError):
239
- return 0.0
184
+ raise RuntimeError(f"ffprobe 无法读取时长 {video_path}: {result.stderr}")
185
+ return float(result.stdout.strip())