@oh-story/dsh 0.1.4 → 0.1.5

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 (479) hide show
  1. package/README.md +56 -12
  2. package/lib/client.js +2692 -268
  3. package/lib/client.js.map +4 -4
  4. package/lib/drama/manifest.json +136 -101
  5. package/lib/drama/skills/short-drama/SKILL.md +61 -1
  6. package/lib/drama/skills/short-drama/references/contract-and-ownership.md +7 -4
  7. package/lib/drama/skills/short-drama/references/creator-documents.md +133 -8
  8. package/lib/drama/skills/short-drama/references/creator-workflow.md +38 -2
  9. package/lib/drama/skills/short-drama/references/knowhow-index.md +3 -0
  10. package/lib/drama/skills/short-drama/references/reference-roles.md +10 -7
  11. package/lib/drama/skills/short-drama/references/runtime-preflight.md +3 -0
  12. package/lib/drama/skills/short-drama/scripts/creator_markdown_check.py +1002 -0
  13. package/lib/drama/skills/short-drama/scripts/project_tool.py +485 -5
  14. package/lib/drama/skills/short-drama-assets/SKILL.md +13 -3
  15. package/lib/drama/skills/short-drama-assets/references/continuity-lock.md +116 -0
  16. package/lib/drama/skills/short-drama-assets/references/identity-vs-variant.md +2 -1
  17. package/lib/drama/skills/short-drama-assets/references/prop-and-state.md +19 -0
  18. package/lib/drama/skills/short-drama-assets/references/stage-contract.md +2 -0
  19. package/lib/drama/skills/short-drama-assets/scripts/asset_check.py +1 -1
  20. package/lib/drama/skills/short-drama-image-prompts/SKILL.md +6 -2
  21. package/lib/drama/skills/short-drama-image-prompts/references/common-recipe.md +4 -3
  22. package/lib/drama/skills/short-drama-image-prompts/references/lookdev-frame.md +1 -1
  23. package/lib/drama/skills/short-drama-image-prompts/references/review-and-fixtures.md +1 -1
  24. package/lib/drama/skills/short-drama-image-prompts/references/stage-contract.md +3 -1
  25. package/lib/drama/skills/short-drama-image-prompts/scripts/image_prompt_check.py +8 -8
  26. package/lib/drama/skills/short-drama-image-prompts/scripts/selftest.py +4 -2
  27. package/lib/drama/skills/short-drama-produce/SKILL.md +46 -10
  28. package/lib/drama/skills/short-drama-produce/references/adapter-contract.md +65 -8
  29. package/lib/drama/skills/short-drama-produce/references/providers/minimax-h3-video.md +85 -0
  30. package/lib/drama/skills/short-drama-produce/references/providers/seedance.md +27 -10
  31. package/lib/drama/skills/short-drama-produce/scripts/production_tool.py +362 -3
  32. package/lib/drama/skills/short-drama-produce/scripts/provider_adapters.py +627 -40
  33. package/lib/drama/skills/short-drama-produce/scripts/selftest.py +5 -4
  34. package/lib/drama/skills/short-drama-review/references/rubric-visual-motion.md +12 -0
  35. package/lib/drama/skills/short-drama-review/scripts/review_check.py +1 -1
  36. package/lib/drama/skills/short-drama-storyboard/SKILL.md +76 -7
  37. package/lib/drama/skills/short-drama-storyboard/assets/shot-template.jsonl +1 -1
  38. package/lib/drama/skills/short-drama-storyboard/references/keyframe-craft.md +6 -2
  39. package/lib/drama/skills/short-drama-storyboard/references/production-shot-grammar.md +19 -4
  40. package/lib/drama/skills/short-drama-storyboard/references/review-and-fixtures.md +5 -2
  41. package/lib/drama/skills/short-drama-storyboard/references/screenplay-to-keyframe-example.md +16 -0
  42. package/lib/drama/skills/short-drama-storyboard/references/stage-contract.md +8 -3
  43. package/lib/drama/skills/short-drama-storyboard/scripts/storyboard_check.py +1 -1
  44. package/lib/drama/skills/short-drama-video-prompts/SKILL.md +81 -13
  45. package/lib/drama/skills/short-drama-video-prompts/assets/motion-spec.jsonl.md +6 -1
  46. package/lib/drama/skills/short-drama-video-prompts/assets/video-prompts.md +17 -1
  47. package/lib/drama/skills/short-drama-video-prompts/references/delivery-profile.md +21 -1
  48. package/lib/drama/skills/short-drama-video-prompts/references/minimax-h3.md +154 -0
  49. package/lib/drama/skills/short-drama-video-prompts/references/motion-recipe.md +24 -6
  50. package/lib/drama/skills/short-drama-video-prompts/references/production-prompt-grammar.md +11 -7
  51. package/lib/drama/skills/short-drama-video-prompts/references/seedance-2.0.md +41 -0
  52. package/lib/drama/skills/short-drama-video-prompts/references/seedance-2.5.md +66 -0
  53. package/lib/drama/skills/short-drama-video-prompts/references/stage-contract.md +5 -2
  54. package/lib/drama/skills/short-drama-video-prompts/references/target-model-profile.md +115 -0
  55. package/lib/drama/skills/short-drama-video-prompts/scripts/container_check.py +1 -1
  56. package/lib/drama/skills/short-drama-video-prompts/scripts/music_spec_check.py +1 -1
  57. package/lib/drama/skills/short-drama-write/SKILL.md +3 -2
  58. package/lib/drama/skills/short-drama-write/scripts/duration_estimate.py +1 -1
  59. package/lib/drama/skills/short-drama-write/scripts/screenplay_index.py +2 -2
  60. package/lib/drama/skills/short-drama-write/scripts/voice_sheet_check.py +1 -1
  61. package/lib/index.js +865 -33
  62. package/lib/index.js.map +4 -4
  63. package/lib/novel-to-game/LICENSE +21 -0
  64. package/lib/novel-to-game/README.md +180 -0
  65. package/lib/novel-to-game/README_ZH.md +178 -0
  66. package/lib/novel-to-game/VERSION +1 -0
  67. package/lib/novel-to-game/examples/jin-ping-mei/build/BUILD_BRIEF.md +217 -0
  68. package/lib/novel-to-game/examples/jin-ping-mei/build/app/.vercelignore +3 -0
  69. package/lib/novel-to-game/examples/jin-ping-mei/build/app/RUN.md +130 -0
  70. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/bg/compound_new_guofeng.webp +0 -0
  71. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/finale/fate_coda.webp +0 -0
  72. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/finale/meng.webp +0 -0
  73. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/finale/pan.webp +0 -0
  74. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/finale/pinger.webp +0 -0
  75. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/finale/xuee.webp +0 -0
  76. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/finale/yue.webp +0 -0
  77. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/group/inner_court_accord_five.webp +0 -0
  78. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/group/inner_court_afterglow_five.webp +0 -0
  79. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/group/inner_court_alliance.webp +0 -0
  80. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/group/public_day10.webp +0 -0
  81. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/group/public_day15.webp +0 -0
  82. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/group/public_day5.webp +0 -0
  83. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/group/title_new_guofeng.webp +0 -0
  84. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/joint/pan_pinger.webp +0 -0
  85. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/joint/pan_xuee.webp +0 -0
  86. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/joint/yue_meng.webp +0 -0
  87. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/joint/yue_pan.webp +0 -0
  88. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/joint/yue_pinger.webp +0 -0
  89. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/meng/explicit.webp +0 -0
  90. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/meng/prelude.webp +0 -0
  91. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/milestone/meng.webp +0 -0
  92. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/milestone/pan.webp +0 -0
  93. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/milestone/pinger.webp +0 -0
  94. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/milestone/xuee.webp +0 -0
  95. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/milestone/yue.webp +0 -0
  96. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/pan/explicit.webp +0 -0
  97. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/pan/prelude.webp +0 -0
  98. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/pinger/explicit.webp +0 -0
  99. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/pinger/prelude.webp +0 -0
  100. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/xuee/explicit.webp +0 -0
  101. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/xuee/prelude.webp +0 -0
  102. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/yue/explicit.webp +0 -0
  103. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/yue/prelude.webp +0 -0
  104. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/heroine/meng/night.webp +0 -0
  105. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/heroine/pan/night.webp +0 -0
  106. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/heroine/pinger/night.webp +0 -0
  107. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/heroine/xuee/night.webp +0 -0
  108. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/heroine/yue/night.webp +0 -0
  109. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/household/li_jiaoer.webp +0 -0
  110. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/scene/gate_collector.webp +0 -0
  111. package/lib/novel-to-game/examples/jin-ping-mei/build/app/css/new-guofeng.css +1105 -0
  112. package/lib/novel-to-game/examples/jin-ping-mei/build/app/css/style.css +2539 -0
  113. package/lib/novel-to-game/examples/jin-ping-mei/build/app/index.html +18 -0
  114. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/assets.js +147 -0
  115. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/audio.js +332 -0
  116. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/data.js +7472 -0
  117. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/engine.js +13077 -0
  118. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/main.js +3028 -0
  119. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/text.js +32 -0
  120. package/lib/novel-to-game/examples/jin-ping-mei/build/app/package.json +10 -0
  121. package/lib/novel-to-game/examples/jin-ping-mei/build/app/vercel.json +23 -0
  122. package/lib/novel-to-game/examples/jin-ping-mei/build/art/generated-art.json +161 -0
  123. package/lib/novel-to-game/examples/jin-ping-mei/example.json +12 -0
  124. package/lib/novel-to-game/examples/jin-ping-mei/qa/README.md +31 -0
  125. package/lib/novel-to-game/examples/jin-ping-mei/qa/playable-model.json +99 -0
  126. package/lib/novel-to-game/examples/jin-ping-mei/qa/verification.json +41 -0
  127. package/lib/novel-to-game/examples/jin-ping-mei/qa/verify_playable_model.mjs +129 -0
  128. package/lib/novel-to-game/manifest.json +508 -0
  129. package/lib/novel-to-game/skills/game-art-direction/SKILL.md +73 -0
  130. package/lib/novel-to-game/skills/game-art-direction/agents/openai.yaml +4 -0
  131. package/lib/novel-to-game/skills/game-art-direction/references/art-direction-method.md +119 -0
  132. package/lib/novel-to-game/skills/game-build/SKILL.md +84 -0
  133. package/lib/novel-to-game/skills/game-build/agents/openai.yaml +4 -0
  134. package/lib/novel-to-game/skills/game-build/references/build-brief-contract.md +109 -0
  135. package/lib/novel-to-game/skills/game-build/references/generative-media-pipeline.md +56 -0
  136. package/lib/novel-to-game/skills/game-build/references/playable-model-contract.md +81 -0
  137. package/lib/novel-to-game/skills/game-build/references/tts-production-contract.md +37 -0
  138. package/lib/novel-to-game/skills/game-concept/SKILL.md +72 -0
  139. package/lib/novel-to-game/skills/game-concept/agents/openai.yaml +4 -0
  140. package/lib/novel-to-game/skills/game-concept/references/concept-method.md +94 -0
  141. package/lib/novel-to-game/skills/game-qa/SKILL.md +50 -0
  142. package/lib/novel-to-game/skills/game-qa/agents/openai.yaml +4 -0
  143. package/lib/novel-to-game/skills/game-qa/references/qa-contract.md +66 -0
  144. package/lib/novel-to-game/skills/game-qa/references/test-design-method.md +45 -0
  145. package/lib/novel-to-game/skills/game-world-design/SKILL.md +63 -0
  146. package/lib/novel-to-game/skills/game-world-design/agents/openai.yaml +4 -0
  147. package/lib/novel-to-game/skills/game-world-design/references/dialogue-design-method.md +59 -0
  148. package/lib/novel-to-game/skills/game-world-design/references/game-writing-craft.md +53 -0
  149. package/lib/novel-to-game/skills/game-world-design/references/narrative-design-method.md +84 -0
  150. package/lib/novel-to-game/skills/game-world-design/references/numeric-design-method.md +56 -0
  151. package/lib/novel-to-game/skills/game-world-design/references/world-design-method.md +124 -0
  152. package/lib/novel-to-game/skills/novel-game-analyze/SKILL.md +77 -0
  153. package/lib/novel-to-game/skills/novel-game-analyze/agents/openai.yaml +4 -0
  154. package/lib/novel-to-game/skills/novel-game-analyze/references/gameability-protocol.md +65 -0
  155. package/lib/novel-to-game/skills/novel-to-game/SKILL.md +71 -0
  156. package/lib/novel-to-game/skills/novel-to-game/agents/openai.yaml +4 -0
  157. package/lib/novel-to-game/skills/novel-to-game/references/intake-benchmark-reference.md +42 -0
  158. package/lib/novel-to-game/skills/novel-to-game/references/intake-method.md +51 -0
  159. package/lib/novel-to-game/skills/novel-to-game/references/pipeline-contract.md +65 -0
  160. package/lib/oh-story/manifest.json +415 -305
  161. package/lib/oh-story/roles/consistency-checker.md +1 -1
  162. package/lib/oh-story/roles/narrative-writer.md +32 -33
  163. package/lib/oh-story/roles/story-architect.md +16 -26
  164. package/lib/oh-story/skills/browser-cdp/SKILL.md +1 -1
  165. package/lib/oh-story/skills/story/SKILL.md +16 -9
  166. package/lib/oh-story/skills/story/VERSION +1 -1
  167. package/lib/oh-story/skills/story/references/author-memory.md +163 -0
  168. package/lib/oh-story/skills/story/scripts/author_memory_commit.py +871 -0
  169. package/lib/oh-story/skills/story-cover/SKILL.md +1 -1
  170. package/lib/oh-story/skills/story-deslop/SKILL.md +10 -5
  171. package/lib/oh-story/skills/story-deslop/references/anti-ai-writing.md +2 -1
  172. package/lib/oh-story/skills/story-deslop/references/author-memory.md +163 -0
  173. package/lib/oh-story/skills/story-deslop/references/banned-words.md +6 -4
  174. package/lib/oh-story/skills/story-deslop/scripts/author_memory_commit.py +871 -0
  175. package/lib/oh-story/skills/story-deslop/scripts/check-ai-patterns.js +64 -1
  176. package/lib/oh-story/skills/story-import/SKILL.md +19 -10
  177. package/lib/oh-story/skills/story-import/references/format-and-structure.md +9 -10
  178. package/lib/oh-story/skills/story-import/references/structure-mapping-long.md +2 -2
  179. package/lib/oh-story/skills/story-import/references/structure-mapping-short.md +1 -1
  180. package/lib/oh-story/skills/story-import/references/tracking-transaction.md +14 -6
  181. package/lib/oh-story/skills/story-import/scripts/check-outline-contract.js +251 -0
  182. package/lib/oh-story/skills/story-import/scripts/tracking_commit.py +98 -9
  183. package/lib/oh-story/skills/story-import/scripts/wordcount_core.py +246 -0
  184. package/lib/oh-story/skills/story-long-analyze/SKILL.md +4 -4
  185. package/lib/oh-story/skills/story-long-scan/SKILL.md +1 -1
  186. package/lib/oh-story/skills/story-long-write/SKILL.md +35 -21
  187. package/lib/oh-story/skills/story-long-write/references/anti-ai-writing.md +2 -1
  188. package/lib/oh-story/skills/story-long-write/references/artifact-protocols.md +7 -3
  189. package/lib/oh-story/skills/story-long-write/references/author-memory.md +163 -0
  190. package/lib/oh-story/skills/story-long-write/references/banned-words.md +6 -4
  191. package/lib/oh-story/skills/story-long-write/references/cross-book-recall.md +6 -6
  192. package/lib/oh-story/skills/story-long-write/references/genre-prose-cards.md +1 -1
  193. package/lib/oh-story/skills/story-long-write/references/genre-readers.md +1 -1
  194. package/lib/oh-story/skills/{story-setup/references/agent-references/hooks-chapter.md → story-long-write/references/long-chapter-hooks.md} +5 -5
  195. package/lib/oh-story/skills/story-long-write/references/{quality-checklist.md → long-chapter-quality.md} +2 -1
  196. package/lib/oh-story/skills/story-long-write/references/long-format.md +42 -0
  197. package/lib/oh-story/skills/{story-setup/references/agent-references/genre-catalog.md → story-long-write/references/long-genre-catalog.md} +11 -97
  198. package/lib/oh-story/skills/{story-setup/references/agent-references/genre-core-mechanics.md → story-long-write/references/long-genre-mechanics.md} +1 -1
  199. package/lib/oh-story/skills/{story-short-write/references/reversal-toolkit.md → story-long-write/references/long-reversal.md} +18 -32
  200. package/lib/oh-story/skills/story-long-write/references/{hooks-suspense.md → long-suspense.md} +6 -6
  201. package/lib/oh-story/skills/story-long-write/references/opening-design.md +1 -1
  202. package/lib/oh-story/skills/story-long-write/references/outline-structure-theory.md +1 -1
  203. package/lib/oh-story/skills/story-long-write/references/style-genre-modules.md +1 -1
  204. package/lib/oh-story/skills/story-long-write/references/tracking-transaction.md +14 -6
  205. package/lib/oh-story/skills/story-long-write/references/workflow-chapter.md +32 -27
  206. package/lib/oh-story/skills/story-long-write/references/workflow-daily.md +4 -4
  207. package/lib/oh-story/skills/story-long-write/references/workflow-revision.md +2 -2
  208. package/lib/oh-story/skills/story-long-write/references/workflow-setup.md +14 -14
  209. package/lib/oh-story/skills/story-long-write/references/writing-craft.md +54 -54
  210. package/lib/oh-story/skills/story-long-write/scripts/author_memory_commit.py +871 -0
  211. package/lib/oh-story/skills/story-long-write/scripts/check-ai-patterns.js +64 -1
  212. package/lib/oh-story/skills/story-long-write/scripts/check-outline-contract.js +251 -0
  213. package/lib/oh-story/skills/story-long-write/scripts/storyctl.py +303 -0
  214. package/lib/oh-story/skills/story-long-write/scripts/tracking_commit.py +98 -9
  215. package/lib/oh-story/skills/story-long-write/scripts/wordcount_core.py +246 -0
  216. package/lib/oh-story/skills/story-review/SKILL.md +26 -17
  217. package/lib/oh-story/skills/story-review/references/anti-ai-writing.md +2 -1
  218. package/lib/oh-story/skills/story-review/references/author-memory.md +163 -0
  219. package/lib/oh-story/skills/story-review/references/banned-words.md +6 -4
  220. package/lib/oh-story/skills/story-review/references/{quality-checklist.md → review-quality.md} +22 -21
  221. package/lib/oh-story/skills/story-review/references/tracking-transaction.md +14 -6
  222. package/lib/oh-story/skills/story-review/scripts/author_memory_commit.py +871 -0
  223. package/lib/oh-story/skills/story-review/scripts/check-ai-patterns.js +64 -1
  224. package/lib/oh-story/skills/story-review/scripts/tracking_commit.py +98 -9
  225. package/lib/oh-story/skills/story-review/scripts/wordcount_core.py +246 -0
  226. package/lib/oh-story/skills/story-setup/SKILL.md +74 -33
  227. package/lib/oh-story/skills/story-setup/references/agent-references/agent-quality.md +72 -0
  228. package/lib/oh-story/skills/story-setup/references/agent-references/agent-reference-profiles.md +55 -0
  229. package/lib/oh-story/skills/story-setup/references/agent-references/anti-ai-writing.md +2 -1
  230. package/lib/oh-story/skills/story-setup/references/agent-references/banned-words.md +6 -4
  231. package/lib/oh-story/skills/story-setup/references/agent-references/format-and-structure.md +9 -10
  232. package/lib/oh-story/skills/story-setup/references/agent-references/genre-prose-cards.md +1 -1
  233. package/lib/oh-story/skills/story-setup/references/agent-references/genre-readers.md +1 -1
  234. package/lib/oh-story/skills/{story-short-analyze/references/hooks-chapter.md → story-setup/references/agent-references/long-chapter-hooks.md} +5 -5
  235. package/lib/oh-story/skills/story-setup/references/agent-references/long-emotional-methods.md +209 -0
  236. package/lib/oh-story/skills/{story-long-write/references/genre-catalog.md → story-setup/references/agent-references/long-genre-catalog.md} +11 -97
  237. package/lib/oh-story/skills/{story-long-write/references/genre-core-mechanics.md → story-setup/references/agent-references/long-genre-mechanics.md} +1 -1
  238. package/lib/oh-story/skills/story-setup/references/agent-references/long-quality.md +40 -0
  239. package/lib/oh-story/skills/story-setup/references/agent-references/{reversal-toolkit.md → long-reversal.md} +18 -32
  240. package/lib/oh-story/skills/{story-short-write/references/hooks-suspense.md → story-setup/references/agent-references/long-suspense.md} +6 -6
  241. package/lib/oh-story/skills/story-setup/references/agent-references/opening-design.md +1 -1
  242. package/lib/oh-story/skills/story-setup/references/agent-references/{emotional-methods.md → short-emotional-methods.md} +4 -4
  243. package/lib/oh-story/skills/{story-long-write/references/genre-writing-formulas.md → story-setup/references/agent-references/short-genre-formulas.md} +32 -44
  244. package/lib/oh-story/skills/story-setup/references/agent-references/short-quality.md +39 -0
  245. package/lib/oh-story/skills/{story-long-write/references/reversal-toolkit.md → story-setup/references/agent-references/short-reversal.md} +29 -23
  246. package/lib/oh-story/skills/story-setup/references/agent-references/{hooks-suspense.md → short-suspense.md} +31 -31
  247. package/lib/oh-story/skills/story-setup/references/agent-references/style-genre-modules.md +1 -1
  248. package/lib/oh-story/skills/story-setup/references/agent-references/writing-craft.md +55 -55
  249. package/lib/oh-story/skills/story-short-analyze/SKILL.md +28 -28
  250. package/lib/oh-story/skills/story-short-analyze/references/{character-basics.md → analysis-character-basics.md} +1 -1
  251. package/lib/oh-story/skills/story-short-analyze/references/{character-design-methods.md → analysis-character-design.md} +1 -1
  252. package/lib/oh-story/skills/story-short-analyze/references/{character-relations.md → analysis-character-relations.md} +1 -1
  253. package/lib/oh-story/skills/story-short-analyze/references/{hooks-paragraph.md → analysis-paragraph-hooks.md} +1 -1
  254. package/lib/oh-story/skills/story-short-analyze/references/{genre-readers.md → analysis-reader-profile.md} +2 -2
  255. package/lib/oh-story/skills/story-short-analyze/references/analysis-report-style.md +45 -0
  256. package/lib/oh-story/skills/story-short-analyze/references/analysis-short-genres.md +65 -0
  257. package/lib/oh-story/skills/story-short-analyze/references/analysis-short-hooks.md +77 -0
  258. package/lib/oh-story/skills/story-short-analyze/references/analysis-short-mechanics.md +82 -0
  259. package/lib/oh-story/skills/story-short-analyze/references/analysis-short-patterns.md +50 -0
  260. package/lib/oh-story/skills/story-short-analyze/references/analysis-short-suspense.md +98 -0
  261. package/lib/oh-story/skills/story-short-analyze/references/{genre-writing-techniques.md → analysis-writing-techniques.md} +21 -19
  262. package/lib/oh-story/skills/story-short-analyze/references/deconstruction-examples.md +1 -1
  263. package/lib/oh-story/skills/story-short-analyze/references/material-decomposition.md +21 -28
  264. package/lib/oh-story/skills/story-short-analyze/references/output-contract.md +1 -1
  265. package/lib/oh-story/skills/story-short-analyze/references/output-templates.md +17 -17
  266. package/lib/oh-story/skills/story-short-analyze/references/{quality-checklist.md → source-story-quality.md} +28 -49
  267. package/lib/oh-story/skills/story-short-analyze/references/zhihu-style.md +2 -2
  268. package/lib/oh-story/skills/story-short-scan/SKILL.md +1 -1
  269. package/lib/oh-story/skills/story-short-write/SKILL.md +78 -55
  270. package/lib/oh-story/skills/story-short-write/references/author-memory.md +163 -0
  271. package/lib/oh-story/skills/story-short-write/references/banned-words.md +6 -4
  272. package/lib/oh-story/skills/story-short-write/references/cross-book-recall.md +3 -3
  273. package/lib/oh-story/skills/story-short-write/references/emotional-methods.md +4 -4
  274. package/lib/oh-story/skills/story-short-write/references/genre-styles//344/270/226/346/203/205/346/211/223/350/204/270.md +3 -3
  275. package/lib/oh-story/skills/story-short-write/references/genre-styles//345/217/214/347/224/267/344/270/273.md +2 -2
  276. 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
  277. package/lib/oh-story/skills/story-short-write/references/genre-styles//345/256/205/346/226/227/345/256/253/346/226/227.md +6 -6
  278. 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
  279. package/lib/oh-story/skills/story-short-write/references/genre-styles//346/202/254/347/226/221.md +4 -4
  280. package/lib/oh-story/skills/story-short-write/references/genre-styles//346/260/221/344/277/227/346/200/252/350/260/210.md +7 -7
  281. package/lib/oh-story/skills/story-short-write/references/genre-styles//346/262/231/351/233/225/350/204/221/346/264/236.md +5 -5
  282. package/lib/oh-story/skills/story-short-write/references/genre-styles//347/224/234/345/256/240.md +4 -4
  283. 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
  284. package/lib/oh-story/skills/story-short-write/references/genre-writing-formulas.md +32 -44
  285. package/lib/oh-story/skills/story-short-write/references/genre-writing-techniques.md +20 -18
  286. package/lib/oh-story/skills/story-short-write/references/output-contract.md +1 -1
  287. package/lib/oh-story/skills/story-short-write/references/short-craft.md +18 -18
  288. package/lib/oh-story/skills/story-short-write/references/short-deslop.md +1 -1
  289. package/lib/oh-story/skills/story-short-write/references/short-format.md +38 -8
  290. package/lib/oh-story/skills/story-short-write/references/{quality-checklist.md → short-prose-quality.md} +22 -21
  291. package/lib/oh-story/skills/story-short-write/references/short-reversal.md +355 -0
  292. package/lib/oh-story/skills/{story-short-analyze/references/hooks-suspense.md → story-short-write/references/short-suspense.md} +31 -31
  293. package/lib/oh-story/skills/story-short-write/references/submission-craft.md +2 -2
  294. package/lib/oh-story/skills/story-short-write/references/writing-workflow.md +31 -22
  295. package/lib/oh-story/skills/story-short-write/scripts/author_memory_commit.py +871 -0
  296. package/lib/oh-story/skills/story-short-write/scripts/check-ai-patterns.js +64 -1
  297. package/lib/oh-story/skills/story-short-write/scripts/check-delivery-contract.js +212 -0
  298. package/lib/oh-story/skills/story-short-write/scripts/check-phase2-contract.js +363 -0
  299. package/lib/types/client/drama-production-view.d.ts +34 -0
  300. package/lib/types/client/drama-production-view.d.ts.map +1 -0
  301. package/lib/types/client/drama-production.d.ts +92 -0
  302. package/lib/types/client/drama-production.d.ts.map +1 -0
  303. package/lib/types/client/file-activity.d.ts +4 -2
  304. package/lib/types/client/file-activity.d.ts.map +1 -1
  305. package/lib/types/client/index.d.ts +1 -1
  306. package/lib/types/client/index.d.ts.map +1 -1
  307. package/lib/types/client/production-intents.d.ts +9 -0
  308. package/lib/types/client/production-intents.d.ts.map +1 -0
  309. package/lib/types/client/production-prompts.d.ts +9 -0
  310. package/lib/types/client/production-prompts.d.ts.map +1 -0
  311. package/lib/types/client/production-runtime.d.ts +58 -0
  312. package/lib/types/client/production-runtime.d.ts.map +1 -0
  313. package/lib/types/client/video-studio.d.ts +41 -0
  314. package/lib/types/client/video-studio.d.ts.map +1 -0
  315. package/lib/types/client/workbench-ui.d.ts +5 -0
  316. package/lib/types/client/workbench-ui.d.ts.map +1 -0
  317. package/lib/types/game-verification.d.ts +16 -0
  318. package/lib/types/game-verification.d.ts.map +1 -0
  319. package/lib/types/index.d.ts +3 -1
  320. package/lib/types/index.d.ts.map +1 -1
  321. package/lib/types/production-intent.d.ts +21 -0
  322. package/lib/types/production-intent.d.ts.map +1 -0
  323. package/lib/types/production-tool.d.ts +5 -0
  324. package/lib/types/production-tool.d.ts.map +1 -0
  325. package/lib/types/role-tool.d.ts.map +1 -1
  326. package/lib/types/skill-provider.d.ts +7 -0
  327. package/lib/types/skill-provider.d.ts.map +1 -1
  328. package/lib/types/video-project.d.ts +42 -0
  329. package/lib/types/video-project.d.ts.map +1 -0
  330. package/lib/types/workspace-request-trust.d.ts +6 -0
  331. package/lib/types/workspace-request-trust.d.ts.map +1 -1
  332. package/lib/types/workspace-route.d.ts +20 -0
  333. package/lib/types/workspace-route.d.ts.map +1 -1
  334. package/lib/video-recap/CHANGELOG.md +282 -0
  335. package/lib/video-recap/LICENSE +21 -0
  336. package/lib/video-recap/README.en.md +257 -0
  337. package/lib/video-recap/README.md +255 -0
  338. package/lib/video-recap/manifest.json +684 -0
  339. package/lib/video-recap/skills/video-assemble/SKILL.md +97 -0
  340. package/lib/video-recap/skills/video-assemble/references/jianying/LICENSE.duo-video +21 -0
  341. package/lib/video-recap/skills/video-assemble/references/jianying/SOURCE.md +15 -0
  342. package/lib/video-recap/skills/video-assemble/references/jianying/empty_draft_meta_info.json +69 -0
  343. package/lib/video-recap/skills/video-assemble/references/jianying/empty_jy_combination_segment.json +84 -0
  344. package/lib/video-recap/skills/video-assemble/references/jianying/empty_jy_combination_video_material.json +98 -0
  345. package/lib/video-recap/skills/video-assemble/references/jianying/empty_jy_draft.json +204 -0
  346. package/lib/video-recap/skills/video-assemble/references/jianying/empty_jy_material_video.json +83 -0
  347. package/lib/video-recap/skills/video-assemble/references/jianying/empty_jy_meta_material_value.json +23 -0
  348. package/lib/video-recap/skills/video-assemble/references/jianying/empty_jy_project_info.json +133 -0
  349. package/lib/video-recap/skills/video-assemble/references/jianying/empty_jy_segment.json +70 -0
  350. package/lib/video-recap/skills/video-assemble/references/jianying/empty_jy_text_styles.json +30 -0
  351. package/lib/video-recap/skills/video-assemble/references/jianying/empty_yj_material_audio.json +41 -0
  352. package/lib/video-recap/skills/video-assemble/references/jianying/empty_yj_material_text.json +109 -0
  353. package/lib/video-recap/skills/video-assemble/scripts/artifacts.py +88 -0
  354. package/lib/video-recap/skills/video-assemble/scripts/assemble.py +345 -0
  355. package/lib/video-recap/skills/video-assemble/scripts/assemble_constants.py +34 -0
  356. package/lib/video-recap/skills/video-assemble/scripts/assembly_contract.py +305 -0
  357. package/lib/video-recap/skills/video-assemble/scripts/assembly_settings.py +94 -0
  358. package/lib/video-recap/skills/video-assemble/scripts/audio_automation.py +205 -0
  359. package/lib/video-recap/skills/video-assemble/scripts/audio_mix.py +555 -0
  360. package/lib/video-recap/skills/video-assemble/scripts/export_jianying.py +261 -0
  361. package/lib/video-recap/skills/video-assemble/scripts/jianying_builders.py +767 -0
  362. package/lib/video-recap/skills/video-assemble/scripts/jianying_model.py +111 -0
  363. package/lib/video-recap/skills/video-assemble/scripts/jianying_optional.py +24 -0
  364. package/lib/video-recap/skills/video-assemble/scripts/jianying_schema.py +109 -0
  365. package/lib/video-recap/skills/video-assemble/scripts/jianying_templates.py +37 -0
  366. package/lib/video-recap/skills/video-assemble/scripts/jianying_timeline_contract.py +191 -0
  367. package/lib/video-recap/skills/video-assemble/scripts/jianying_tracks.py +75 -0
  368. package/lib/video-recap/skills/video-assemble/scripts/jianying_writer.py +394 -0
  369. package/lib/video-recap/skills/video-assemble/scripts/lib.py +239 -0
  370. package/lib/video-recap/skills/video-assemble/scripts/media.py +243 -0
  371. package/lib/video-recap/skills/video-assemble/scripts/narration_audio.py +477 -0
  372. package/lib/video-recap/skills/video-assemble/scripts/render_preflight.py +46 -0
  373. package/lib/video-recap/skills/video-assemble/scripts/source_subtitles.py +486 -0
  374. package/lib/video-recap/skills/video-assemble/scripts/subtitle_core.py +376 -0
  375. package/lib/video-recap/skills/video-assemble/scripts/subtitle_render.py +88 -0
  376. package/lib/video-recap/skills/video-assemble/scripts/timeline.py +248 -0
  377. package/lib/video-recap/skills/video-assemble/scripts/timeline_emit.py +89 -0
  378. package/lib/video-recap/skills/video-assemble/scripts/visual_render.py +512 -0
  379. package/lib/video-recap/skills/video-cut/SKILL.md +90 -0
  380. package/lib/video-recap/skills/video-cut/scripts/cut.py +67 -0
  381. package/lib/video-recap/skills/video-cut/scripts/cut_cli.py +320 -0
  382. package/lib/video-recap/skills/video-cut/scripts/cut_contract.py +545 -0
  383. package/lib/video-recap/skills/video-cut/scripts/cut_render.py +348 -0
  384. package/lib/video-recap/skills/video-cut/scripts/lib.py +77 -0
  385. package/lib/video-recap/skills/video-cut/scripts/media_geometry.py +341 -0
  386. package/lib/video-recap/skills/video-cut/scripts/narration_mapping.py +249 -0
  387. package/lib/video-recap/skills/video-cut/scripts/sentence_boundaries.py +745 -0
  388. package/lib/video-recap/skills/video-recap/SKILL.md +216 -0
  389. package/lib/video-recap/skills/video-recap/references/config-playbook.md +61 -0
  390. package/lib/video-recap/skills/video-recap/references/creative-editing-playbook.md +225 -0
  391. package/lib/video-recap/skills/video-recap/references/data-schema.md +534 -0
  392. package/lib/video-recap/skills/video-recap/references/research-guide.md +74 -0
  393. package/lib/video-recap/skills/video-recap/references/shift-left-qc-schema.md +23 -0
  394. package/lib/video-recap/skills/video-recap/references/timeline-and-jianying.md +251 -0
  395. package/lib/video-recap/skills/video-recap/scripts/doctor.py +483 -0
  396. package/lib/video-recap/skills/video-recap/scripts/final_qc.py +737 -0
  397. package/lib/video-recap/skills/video-recap/scripts/lib.py +242 -0
  398. package/lib/video-recap/skills/video-recap/scripts/materials.py +457 -0
  399. package/lib/video-recap/skills/video-recap/scripts/mimo_qc.py +95 -0
  400. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_client.py +18 -0
  401. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_contract.py +9 -0
  402. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_evidence.py +371 -0
  403. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_observations.py +201 -0
  404. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_payload.py +227 -0
  405. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_report.py +362 -0
  406. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_runner.py +129 -0
  407. package/lib/video-recap/skills/video-recap/scripts/qc_contract.py +338 -0
  408. package/lib/video-recap/skills/video-recap/scripts/recap.py +9 -0
  409. package/lib/video-recap/skills/video-recap/scripts/recap_cli.py +140 -0
  410. package/lib/video-recap/skills/video-recap/scripts/recap_inspect.py +550 -0
  411. package/lib/video-recap/skills/video-recap/scripts/recap_review.py +77 -0
  412. package/lib/video-recap/skills/video-recap/scripts/recap_runner.py +654 -0
  413. package/lib/video-recap/skills/video-recap/scripts/recap_runtime.py +335 -0
  414. package/lib/video-recap/skills/video-recap/scripts/recap_stage_qc.py +217 -0
  415. package/lib/video-recap/skills/video-recap/scripts/recap_timeline.py +795 -0
  416. package/lib/video-recap/skills/video-script/SKILL.md +248 -0
  417. package/lib/video-recap/skills/video-script/references/creative-editing-playbook.md +225 -0
  418. package/lib/video-recap/skills/video-script/references/research-guide.md +75 -0
  419. package/lib/video-recap/skills/video-script/scripts/agent_brief.py +399 -0
  420. package/lib/video-recap/skills/video-script/scripts/agent_text.py +410 -0
  421. package/lib/video-recap/skills/video-script/scripts/brief_context.py +403 -0
  422. package/lib/video-recap/skills/video-script/scripts/brief_inputs.py +332 -0
  423. package/lib/video-recap/skills/video-script/scripts/brief_timeline.py +442 -0
  424. package/lib/video-recap/skills/video-script/scripts/deslop_qc.py +237 -0
  425. package/lib/video-recap/skills/video-script/scripts/evidence_bundle.py +492 -0
  426. package/lib/video-recap/skills/video-script/scripts/lib.py +327 -0
  427. package/lib/video-recap/skills/video-script/scripts/narration.py +12 -0
  428. package/lib/video-recap/skills/video-script/scripts/narration_lint.py +678 -0
  429. package/lib/video-recap/skills/video-script/scripts/review.py +42 -0
  430. package/lib/video-recap/skills/video-script/scripts/review_grounding.py +262 -0
  431. package/lib/video-recap/skills/video-script/scripts/review_response.py +761 -0
  432. package/lib/video-recap/skills/video-script/scripts/review_runner.py +200 -0
  433. package/lib/video-recap/skills/video-script/scripts/speech_ownership.py +204 -0
  434. package/lib/video-recap/skills/video-script/scripts/timeline_fusion.py +199 -0
  435. package/lib/video-recap/skills/video-script/scripts/validate.py +164 -0
  436. package/lib/video-recap/skills/video-understanding/SKILL.md +75 -0
  437. package/lib/video-recap/skills/video-understanding/references/data-schema.md +299 -0
  438. package/lib/video-recap/skills/video-understanding/references/prompt-templates.md +37 -0
  439. package/lib/video-recap/skills/video-understanding/references/research-guide.md +74 -0
  440. package/lib/video-recap/skills/video-understanding/scripts/agent_brief.py +399 -0
  441. package/lib/video-recap/skills/video-understanding/scripts/agent_text.py +410 -0
  442. package/lib/video-recap/skills/video-understanding/scripts/asr.py +260 -0
  443. package/lib/video-recap/skills/video-understanding/scripts/brief.py +12 -0
  444. package/lib/video-recap/skills/video-understanding/scripts/brief_context.py +403 -0
  445. package/lib/video-recap/skills/video-understanding/scripts/brief_inputs.py +332 -0
  446. package/lib/video-recap/skills/video-understanding/scripts/brief_timeline.py +442 -0
  447. package/lib/video-recap/skills/video-understanding/scripts/consolidate.py +647 -0
  448. package/lib/video-recap/skills/video-understanding/scripts/deslop_qc.py +237 -0
  449. package/lib/video-recap/skills/video-understanding/scripts/detect.py +513 -0
  450. package/lib/video-recap/skills/video-understanding/scripts/extract.py +62 -0
  451. package/lib/video-recap/skills/video-understanding/scripts/lib.py +470 -0
  452. package/lib/video-recap/skills/video-understanding/scripts/narration_lint.py +678 -0
  453. package/lib/video-recap/skills/video-understanding/scripts/speech_ownership.py +204 -0
  454. package/lib/video-recap/skills/video-understanding/scripts/storyboard.py +507 -0
  455. package/lib/video-recap/skills/video-understanding/scripts/timeline_fusion.py +199 -0
  456. package/lib/video-recap/skills/video-understanding/scripts/understand.py +9 -0
  457. package/lib/video-recap/skills/video-understanding/scripts/understanding_brief.py +216 -0
  458. package/lib/video-recap/skills/video-understanding/scripts/understanding_cache.py +327 -0
  459. package/lib/video-recap/skills/video-understanding/scripts/understanding_runner.py +367 -0
  460. package/lib/video-recap/skills/video-understanding/scripts/understanding_storyboard.py +162 -0
  461. package/lib/video-recap/skills/video-understanding/scripts/vlm.py +705 -0
  462. package/lib/video-recap/skills/video-voiceover/SKILL.md +81 -0
  463. package/lib/video-recap/skills/video-voiceover/scripts/dub.py +637 -0
  464. package/lib/video-recap/skills/video-voiceover/scripts/fish_audio.py +109 -0
  465. package/lib/video-recap/skills/video-voiceover/scripts/lib.py +421 -0
  466. package/lib/video-recap/skills/video-voiceover/scripts/tts_audio.py +113 -0
  467. package/lib/video-recap/skills/video-voiceover/scripts/voiceover.py +782 -0
  468. package/package.json +55 -40
  469. package/lib/oh-story/skills/story-deslop/scripts/check-outline-copy.js +0 -241
  470. package/lib/oh-story/skills/story-long-write/references/format-and-structure.md +0 -169
  471. package/lib/oh-story/skills/story-setup/references/agent-references/genre-writing-formulas.md +0 -444
  472. package/lib/oh-story/skills/story-setup/references/agent-references/quality-checklist.md +0 -247
  473. package/lib/oh-story/skills/story-short-analyze/references/anti-ai-writing.md +0 -582
  474. package/lib/oh-story/skills/story-short-analyze/references/banned-words.md +0 -147
  475. package/lib/oh-story/skills/story-short-analyze/references/genre-catalog.md +0 -490
  476. package/lib/oh-story/skills/story-short-analyze/references/genre-core-mechanics.md +0 -425
  477. package/lib/oh-story/skills/story-short-analyze/references/genre-writing-formulas.md +0 -453
  478. /package/lib/oh-story/skills/{story-long-write/references/hooks-chapter.md → story-setup/references/agent-references/short-chapter-hooks.md} +0 -0
  479. /package/lib/oh-story/skills/{story-long-write/references/hooks-paragraph.md → story-setup/references/agent-references/short-paragraph-hooks.md} +0 -0
@@ -0,0 +1,111 @@
1
+ """Thin internal model used at the JianYing adapter boundary."""
2
+
3
+ import os
4
+ from dataclasses import dataclass, field
5
+ from typing import Callable
6
+
7
+ from jianying_schema import us
8
+ from jianying_tracks import TrackAllocator
9
+
10
+
11
+ ProbeFn = Callable[[str], tuple[int, int, int]]
12
+ NewIdFn = Callable[[], str]
13
+
14
+
15
+ @dataclass
16
+ class DraftBuildContext:
17
+ """Normalized draft build state and overlap-safe track collection."""
18
+
19
+ width: int
20
+ height: int
21
+ fps: float
22
+ total_us: int
23
+ new_id: NewIdFn
24
+ probe: ProbeFn
25
+ resource_packages: dict = field(default_factory=dict)
26
+ style_presets: dict = field(default_factory=dict)
27
+ materials: dict[str, list] = field(
28
+ default_factory=lambda: {
29
+ key: []
30
+ for key in (
31
+ "audios",
32
+ "chromas",
33
+ "common_mask",
34
+ "drafts",
35
+ "effects",
36
+ "masks",
37
+ "speeds",
38
+ "stickers",
39
+ "text_templates",
40
+ "texts",
41
+ "transitions",
42
+ "video_effects",
43
+ "videos",
44
+ )
45
+ }
46
+ )
47
+ tracks: list[dict] = field(default_factory=list)
48
+ notes: list[str] = field(default_factory=list)
49
+ track_allocator: TrackAllocator = field(default_factory=TrackAllocator)
50
+
51
+ @classmethod
52
+ def from_timeline(cls, timeline, new_id, probe):
53
+ canvas = timeline.get("canvas", {})
54
+ return cls(
55
+ width=int(canvas.get("width", 1920)),
56
+ height=int(canvas.get("height", 1080)),
57
+ fps=float(canvas.get("fps", 30)),
58
+ total_us=us(timeline.get("duration", 0)),
59
+ new_id=new_id,
60
+ probe=probe,
61
+ resource_packages=dict(timeline.get("resource_packages") or {}),
62
+ style_presets=dict(timeline.get("style_presets") or {}),
63
+ )
64
+
65
+ def media_duration(self, path, fallback_us):
66
+ if path and os.path.exists(path):
67
+ duration_us, width, height = self.probe(path)
68
+ return duration_us or fallback_us, width, height
69
+ return fallback_us, 0, 0
70
+
71
+ def add_segment(self, kind, base_name, start_us, duration_us, segment):
72
+ allocated = self.track_allocator.allocate(kind, base_name, start_us, duration_us)
73
+ track = next(
74
+ (
75
+ item for item in self.tracks
76
+ if item["_semantic_kind"] == allocated.kind
77
+ and item["name"] == allocated.name
78
+ ),
79
+ None,
80
+ )
81
+ if track is None:
82
+ track = {
83
+ "attribute": 0,
84
+ "flag": 0,
85
+ "id": self.new_id(),
86
+ "is_default_name": True,
87
+ "name": allocated.name,
88
+ "segments": [],
89
+ "type": allocated.track_type,
90
+ "_semantic_kind": allocated.kind,
91
+ "_layout_order": allocated.layout_order,
92
+ }
93
+ self.tracks.append(track)
94
+ track["segments"].append(segment)
95
+ return track
96
+
97
+ def finalize_tracks(self):
98
+ # Python's stable sort preserves the timeline's authored order inside
99
+ # one semantic band (for example narration before BGM).
100
+ self.tracks.sort(key=lambda item: item.get("_layout_order", 120_000))
101
+ type_counts = {}
102
+ for track in self.tracks:
103
+ count = type_counts.get(track["type"], 0)
104
+ track["flag"] = 0 if count == 0 else 2
105
+ type_counts[track["type"]] = count + 1
106
+ track.pop("_semantic_kind", None)
107
+ track.pop("_layout_order", None)
108
+ return self.tracks
109
+
110
+ def note(self, message):
111
+ self.notes.append(message)
@@ -0,0 +1,24 @@
1
+ """Failure-isolated optional JianYing export invoked after canonical rendering."""
2
+
3
+ from pathlib import Path
4
+
5
+ from lib import CONFIG, log
6
+
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
13
+ try:
14
+ from export_jianying import export_timeline_to_jianying
15
+ from timeline import load_timeline
16
+ parent = out_dir or CONFIG.get("jianying_draft_dir") or str(work_dir)
17
+ 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))
20
+ for n in notes:
21
+ log(f" 注意: {n}")
22
+ log(f"剪映草稿已导出: {draft_dir}")
23
+ except Exception as exc: # optional feature must never fail the render
24
+ log(f" ⚠️ 剪映导出失败(不影响成片): {exc}")
@@ -0,0 +1,109 @@
1
+ """Schema constants, skeleton factories, and material registry for JianYing export.
2
+
3
+ This module is intentionally data-oriented: it owns draft version metadata, the
4
+ full `materials` parallel-array shape, and the implemented material dispatch.
5
+ """
6
+
7
+ from jianying_templates import template
8
+
9
+ # The full 剪映 materials object: ~45 parallel arrays. Only arrays backed by a
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()
20
+
21
+
22
+ def us(seconds):
23
+ """Seconds (float) -> integer microseconds. The single seconds->µs boundary."""
24
+ return int(round(float(seconds) * 1_000_000))
25
+
26
+
27
+ def full_materials(filled):
28
+ """Return a complete JianYing `materials` object with all known arrays."""
29
+ out = {k: [] for k in MATERIAL_KEYS}
30
+ out.update(filled)
31
+ return out
32
+
33
+
34
+ def scrub_platform_identity(project):
35
+ """Remove hardware fingerprints carried by the pinned upstream templates."""
36
+ for platform_key in ("last_modified_platform", "platform"):
37
+ platform = project.get(platform_key)
38
+ if not isinstance(platform, dict):
39
+ continue
40
+ for identity_key in ("device_id", "hard_disk_id", "mac_address"):
41
+ platform[identity_key] = ""
42
+ return project
43
+
44
+
45
+ def draft_content_skeleton(draft_id, width, height, fps, total_us, materials, tracks):
46
+ """Build the root `draft_content.json` / `draft_info.json` skeleton."""
47
+ content = template("project")
48
+ scrub_platform_identity(content)
49
+ content["canvas_config"] = {"width": width, "height": height, "ratio": "original"}
50
+ content["duration"] = int(total_us)
51
+ content["fps"] = float(fps)
52
+ content["id"] = draft_id
53
+ content["materials"] = full_materials(materials)
54
+ content["tracks"] = tracks
55
+ return content
56
+
57
+
58
+ def meta_info(draft_id, total_us):
59
+ """Build the companion `draft_meta_info.json` skeleton."""
60
+ meta = template("meta")
61
+ meta["draft_id"] = draft_id
62
+ meta["draft_timeline_materials_size_"] = 0
63
+ meta["tm_duration"] = int(total_us)
64
+ return meta
65
+
66
+
67
+ def material_category_registry():
68
+ """Material category support table inspired by duo-video's MaterialTypeEnum.
69
+
70
+ Keyframes, bundling, and path rewrite are exporter behavior, not material
71
+ categories, so they intentionally do not appear here.
72
+ """
73
+ return {
74
+ "video": {"status": "supported", "materials_key": "videos", "track_type": "video"},
75
+ "audio": {"status": "supported", "materials_key": "audios", "track_type": "audio"},
76
+ "text": {"status": "supported", "materials_key": "texts", "track_type": "text"},
77
+ "subtitle": {"status": "supported", "materials_key": "texts", "track_type": "text"},
78
+ "speed": {"status": "supported_auxiliary", "materials_key": "speeds", "track_type": None},
79
+ # JianYing represents photos in materials.videos with material.type=photo.
80
+ "image": {"status": "supported", "materials_key": "videos", "track_type": "video"},
81
+ "sticker": {"status": "supported_offline_payload", "materials_key": "stickers", "track_type": "sticker"},
82
+ "sound": {"status": "supported_offline_payload", "materials_key": "audios", "track_type": "audio"},
83
+ "text_template": {"status": "supported_offline_payload", "materials_key": "text_templates", "track_type": "text"},
84
+ "lut": {"status": "supported_offline_payload", "materials_key": "effects", "track_type": "video"},
85
+ "transition": {"status": "supported_offline_payload", "materials_key": "transitions", "track_type": "video"},
86
+ "video_effect": {"status": "supported_offline_payload", "materials_key": "video_effects", "track_type": "effect"},
87
+ "face_effect": {"status": "supported_offline_payload", "materials_key": "video_effects", "track_type": "effect"},
88
+ "mask": {"status": "supported_offline_payload", "materials_key": "masks", "track_type": "video"},
89
+ "chroma": {"status": "supported_offline_payload", "materials_key": "chromas", "track_type": "video"},
90
+ "green_screen": {"status": "supported", "materials_key": "drafts", "track_type": "video"},
91
+ "compound": {"status": "supported", "materials_key": "drafts", "track_type": "video"},
92
+ # duo-video style is authoring input rather than a direct material array.
93
+ "style": {"status": "supported", "materials_key": None, "track_type": None},
94
+ }
95
+
96
+
97
+ def validate_material_category(category):
98
+ """Return deterministic support metadata for a public or future material kind."""
99
+ normalized = (category or "").strip().lower()
100
+ registry = material_category_registry()
101
+ info = dict(registry.get(normalized, {"status": "unsupported", "materials_key": None, "track_type": None}))
102
+ info["category"] = normalized
103
+ info["supported"] = info["status"] in {
104
+ "supported", "supported_auxiliary", "supported_offline_payload",
105
+ }
106
+ if not info["supported"]:
107
+ status = "保留但暂未实现" if info["status"] == "reserved" else "未知"
108
+ info["note"] = f"暂不支持的 JianYing material category: {normalized}({status}),已跳过以避免生成无效草稿"
109
+ return info
@@ -0,0 +1,37 @@
1
+ """Load the pinned duo-video JianYing protocol templates."""
2
+
3
+ import copy
4
+ import json
5
+ from functools import lru_cache
6
+ from pathlib import Path
7
+
8
+
9
+ _TEMPLATE_DIR = Path(__file__).resolve().parent.parent / "references" / "jianying"
10
+ _TEMPLATE_FILES = {
11
+ "project": "empty_jy_project_info.json",
12
+ "video": "empty_jy_material_video.json",
13
+ "audio": "empty_yj_material_audio.json",
14
+ "text": "empty_yj_material_text.json",
15
+ "text_style": "empty_jy_text_styles.json",
16
+ "segment": "empty_jy_segment.json",
17
+ "draft": "empty_jy_draft.json",
18
+ "combination_segment": "empty_jy_combination_segment.json",
19
+ "combination_video": "empty_jy_combination_video_material.json",
20
+ "meta": "empty_draft_meta_info.json",
21
+ "meta_material": "empty_jy_meta_material_value.json",
22
+ }
23
+
24
+
25
+ @lru_cache(maxsize=None)
26
+ def _read_template(name):
27
+ try:
28
+ filename = _TEMPLATE_FILES[name]
29
+ except KeyError as exc:
30
+ raise ValueError(f"unknown JianYing template: {name}") from exc
31
+ with open(_TEMPLATE_DIR / filename, encoding="utf-8") as source:
32
+ return json.load(source)
33
+
34
+
35
+ def template(name):
36
+ """Return a mutable deep copy of a pinned protocol template."""
37
+ return copy.deepcopy(_read_template(name))
@@ -0,0 +1,191 @@
1
+ """Timeline validation and migration at the JianYing adapter boundary."""
2
+
3
+ import copy
4
+ import math
5
+
6
+
7
+ CURRENT_SCHEMA_VERSION = 2
8
+ RESOURCE_TRACK_KINDS = {
9
+ "face_effect", "sound", "sticker", "text_template", "video_effect",
10
+ }
11
+
12
+
13
+ def _error(path, expectation):
14
+ raise ValueError(f"invalid timeline {path}: {expectation}")
15
+
16
+
17
+ 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
24
+
25
+
26
+ def _field_path(path, key):
27
+ return f"{path}.{key}" if path else key
28
+
29
+
30
+ def _require_number(container, key, path, *, minimum=None):
31
+ field_path = _field_path(path, key)
32
+ if key not in container or not _is_number(container[key]):
33
+ _error(field_path, "must be a finite number")
34
+ value = container[key]
35
+ if minimum is not None and value < minimum:
36
+ _error(field_path, f"must be >= {minimum}")
37
+ return value
38
+
39
+
40
+ def _require_string(container, key, path):
41
+ value = container.get(key)
42
+ if not isinstance(value, str) or not value:
43
+ _error(_field_path(path, key), "must be a non-empty string")
44
+ return value
45
+
46
+
47
+ def _validate_span(item, path, start_key="timeline_start", end_key="timeline_end"):
48
+ start = _require_number(item, start_key, path, minimum=0)
49
+ end = _require_number(item, end_key, path, minimum=0)
50
+ if end <= start:
51
+ _error(f"{path}.{end_key}", f"must be greater than {start_key}")
52
+
53
+
54
+ def _validate_video_clip(clip, path):
55
+ if not isinstance(clip, dict):
56
+ _error(path, "must be an object")
57
+ _require_string(clip, "source_path", path)
58
+ _validate_span(clip, path)
59
+ _validate_span(clip, path, "source_start", "source_end")
60
+ if "audio" in clip and not isinstance(clip["audio"], dict):
61
+ _error(f"{path}.audio", "must be an object")
62
+ if "speed" in clip:
63
+ speed = _require_number(clip, "speed", path)
64
+ if speed <= 0:
65
+ _error(f"{path}.speed", "must be greater than 0")
66
+ source_duration = float(clip["source_end"]) - float(clip["source_start"])
67
+ target_duration = float(clip["timeline_end"]) - float(clip["timeline_start"])
68
+ expected_source_duration = target_duration * float(speed)
69
+ if not math.isclose(source_duration, expected_source_duration, rel_tol=1e-6, abs_tol=1e-4):
70
+ _error(
71
+ path,
72
+ "source duration must equal target duration multiplied by speed "
73
+ f"({source_duration} != {target_duration} * {speed})",
74
+ )
75
+ if "reverse" in clip and not isinstance(clip["reverse"], bool):
76
+ _error(f"{path}.reverse", "must be a boolean")
77
+ if clip.get("reverse"):
78
+ _require_string(clip, "reverse_path", path)
79
+ for field in ("scale", "position", "flip"):
80
+ if field in clip and not isinstance(clip[field], dict):
81
+ _error(f"{path}.{field}", "must be an object")
82
+ for field in ("transition", "mask", "lut", "chroma"):
83
+ if field in clip and not isinstance(clip[field], (dict, str)):
84
+ _error(f"{path}.{field}", "must be an object or resource-package name")
85
+ if "green_background" in clip and not isinstance(clip["green_background"], dict):
86
+ _error(f"{path}.green_background", "must be a local media object")
87
+ if "compound" in clip and not isinstance(clip["compound"], bool):
88
+ _error(f"{path}.compound", "must be a boolean")
89
+
90
+
91
+ def _validate_segment(segment, path, kind):
92
+ if not isinstance(segment, dict):
93
+ _error(path, "must be an object")
94
+ _validate_span(segment, path)
95
+ if "speed" in segment:
96
+ speed = _require_number(segment, "speed", path)
97
+ if speed <= 0:
98
+ _error(f"{path}.speed", "must be greater than 0")
99
+ if kind in {"audio", "image"}:
100
+ _require_string(segment, "source_path", path)
101
+ elif kind == "text" and not isinstance(segment.get("text"), str):
102
+ _error(f"{path}.text", "must be a string")
103
+ elif kind in RESOURCE_TRACK_KINDS:
104
+ sources = [key for key in ("material", "resource_config", "resource_package") if key in segment]
105
+ if len(sources) != 1:
106
+ _error(path, "must define exactly one of material, resource_config, or resource_package")
107
+ source = segment[sources[0]]
108
+ if sources[0] == "resource_package":
109
+ if not isinstance(source, str) or not source:
110
+ _error(f"{path}.resource_package", "must be a non-empty string")
111
+ elif sources[0] == "material" and not isinstance(source, dict):
112
+ _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")
115
+
116
+
117
+ def _validate_track(track, path):
118
+ if not isinstance(track, dict):
119
+ _error(path, "must be an object")
120
+ kind = _require_string(track, "kind", path)
121
+ if "name" in track and (not isinstance(track["name"], str) or not track["name"]):
122
+ _error(f"{path}.name", "must be a non-empty string")
123
+
124
+ if kind == "video":
125
+ clips = track.get("clips")
126
+ if not isinstance(clips, list):
127
+ _error(f"{path}.clips", "must be an array")
128
+ for index, clip in enumerate(clips):
129
+ _validate_video_clip(clip, f"{path}.clips[{index}]")
130
+ return
131
+
132
+ if kind in {"audio", "image", "text"} | RESOURCE_TRACK_KINDS:
133
+ segments = track.get("segments")
134
+ if not isinstance(segments, list):
135
+ _error(f"{path}.segments", "must be an array")
136
+ if kind == "audio":
137
+ if "role" in track and not isinstance(track["role"], str):
138
+ _error(f"{path}.role", "must be a string")
139
+ if "loop" in track and not isinstance(track["loop"], bool):
140
+ _error(f"{path}.loop", "must be a boolean")
141
+ for index, segment in enumerate(segments):
142
+ _validate_segment(segment, f"{path}.segments[{index}]", kind)
143
+ return
144
+
145
+ _error(f"{path}.kind", f"unsupported track kind {kind!r}")
146
+
147
+
148
+ def _validate_v2(timeline):
149
+ canvas = timeline.get("canvas")
150
+ if not isinstance(canvas, dict):
151
+ _error("canvas", "must be an object")
152
+ for dimension in ("width", "height"):
153
+ value = canvas.get(dimension)
154
+ if not isinstance(value, int) or isinstance(value, bool) or value <= 0:
155
+ _error(f"canvas.{dimension}", "must be a positive integer")
156
+ fps = _require_number(canvas, "fps", "canvas")
157
+ if fps <= 0:
158
+ _error("canvas.fps", "must be greater than 0")
159
+
160
+ _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")
164
+ tracks = timeline.get("tracks")
165
+ if not isinstance(tracks, list):
166
+ _error("tracks", "must be an array")
167
+ for index, track in enumerate(tracks):
168
+ _validate_track(track, f"tracks[{index}]")
169
+
170
+
171
+ def normalize_timeline(timeline):
172
+ """Return a validated schema-v2 copy, migrating schema v1 when necessary."""
173
+ if not isinstance(timeline, dict):
174
+ _error("root", "must be an object")
175
+ schema_version = timeline.get("schema_version")
176
+ if not isinstance(schema_version, int) or isinstance(schema_version, bool):
177
+ _error("schema_version", "must be integer 1 or 2")
178
+ if schema_version not in {1, CURRENT_SCHEMA_VERSION}:
179
+ raise ValueError(
180
+ f"unsupported timeline schema_version {schema_version}; "
181
+ f"supported versions are 1 and {CURRENT_SCHEMA_VERSION}"
182
+ )
183
+
184
+ normalized = copy.deepcopy(timeline)
185
+ if schema_version == 1:
186
+ # Schema v2 is an additive extension of v1 (local image tracks). The
187
+ # migration therefore preserves all authored v1 fields and only advances
188
+ # the version before applying the current contract.
189
+ normalized["schema_version"] = CURRENT_SCHEMA_VERSION
190
+ _validate_v2(normalized)
191
+ return normalized
@@ -0,0 +1,75 @@
1
+ """Semantic track ordering and overlap-safe allocation for JianYing export.
2
+
3
+ The order mirrors duo-video's authoring layout. It is used only to order track
4
+ objects; JianYing segment ``render_index`` remains the schema default and must
5
+ not be confused with this semantic layout value.
6
+ """
7
+
8
+ from dataclasses import dataclass
9
+
10
+
11
+ @dataclass(frozen=True)
12
+ class TrackBand:
13
+ kind: str
14
+ track_type: str
15
+ layout_order: int
16
+ description: str
17
+
18
+
19
+ SEGMENT_RENDER_INDEX = 2
20
+
21
+
22
+ TRACK_LAYOUT_BANDS = {
23
+ "sound": TrackBand("sound", "audio", 10_000, "sound effects"),
24
+ "audio": TrackBand("audio", "audio", 20_000, "narration, music, and general audio"),
25
+ "green_screen": TrackBand("green_screen", "video", 30_000, "green-screen background"),
26
+ "video": TrackBand("video", "video", 40_000, "base video"),
27
+ "image": TrackBand("image", "video", 50_000, "image and photo overlays"),
28
+ "mask": TrackBand("mask", "video", 60_000, "masks"),
29
+ "effect": TrackBand("effect", "effect", 70_000, "video effects"),
30
+ "video_effect": TrackBand("video_effect", "effect", 70_000, "video effects"),
31
+ "face_effect": TrackBand("face_effect", "effect", 70_010, "face effects"),
32
+ "sticker": TrackBand("sticker", "sticker", 80_000, "stickers"),
33
+ "subtitle": TrackBand("subtitle", "text", 90_000, "subtitles"),
34
+ "text": TrackBand("text", "text", 100_000, "plain text"),
35
+ "text_template": TrackBand("text_template", "text", 110_000, "text templates"),
36
+ }
37
+
38
+
39
+ @dataclass(frozen=True)
40
+ class AllocatedTrack:
41
+ kind: str
42
+ name: str
43
+ track_type: str
44
+ layout_order: int
45
+
46
+
47
+ class TrackAllocator:
48
+ """Allocate deterministic suffix tracks when same-name segments overlap.
49
+
50
+ Intervals are half-open, so adjacent segments reuse a track while true
51
+ overlap creates ``name-1``, ``name-2``, and so on.
52
+ """
53
+
54
+ def __init__(self):
55
+ self._occupied = {}
56
+
57
+ @staticmethod
58
+ def _overlaps(start_us, duration_us, existing):
59
+ end_us = int(start_us) + int(duration_us)
60
+ return any(int(start_us) < old_end and end_us > old_start for old_start, old_end in existing)
61
+
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
67
+ suffix = 0
68
+ while True:
69
+ name = base_name if suffix == 0 else f"{base_name}-{suffix}"
70
+ key = (kind, name)
71
+ occupied = self._occupied.setdefault(key, [])
72
+ if not self._overlaps(start_us, duration_us, occupied):
73
+ occupied.append((int(start_us), int(start_us) + int(duration_us)))
74
+ return AllocatedTrack(kind, name, band.track_type, band.layout_order + suffix)
75
+ suffix += 1