@oh-story/dsh 0.1.9 → 0.1.11

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 (529) hide show
  1. package/README.md +14 -14
  2. package/lib/client.js +209 -73
  3. package/lib/client.js.map +3 -3
  4. package/lib/drama/manifest.json +248 -168
  5. package/lib/drama/skills/short-drama/SKILL.md +16 -4
  6. package/lib/drama/skills/short-drama/assets/project-template/short-drama.json +3 -0
  7. package/lib/drama/skills/short-drama/references/creator-documents.md +3 -0
  8. package/lib/drama/skills/short-drama/references/knowhow-index.md +15 -0
  9. package/lib/drama/skills/short-drama/scripts/creator_markdown_check.py +14 -0
  10. package/lib/drama/skills/short-drama/scripts/project_tool.py +131 -0
  11. package/lib/drama/skills/short-drama-assets/SKILL.md +2 -1
  12. package/lib/drama/skills/short-drama-assets/references/asset-review-checklist.md +2 -0
  13. package/lib/drama/skills/short-drama-assets/references/character-and-look.md +10 -0
  14. package/lib/drama/skills/short-drama-assets/references/continuity-lock.md +6 -9
  15. package/lib/drama/skills/short-drama-assets/references/era-anchors.md +37 -0
  16. package/lib/drama/skills/short-drama-assets/references/stage-contract.md +2 -0
  17. package/lib/drama/skills/short-drama-assets/references/voice-direction.md +28 -0
  18. package/lib/drama/skills/short-drama-develop/SKILL.md +10 -4
  19. package/lib/drama/skills/short-drama-develop/assets/adaptation-map.example.jsonl +1 -1
  20. package/lib/drama/skills/short-drama-develop/assets/creative-brief.md +8 -0
  21. package/lib/drama/skills/short-drama-develop/references/adaptation-craft.md +80 -13
  22. package/lib/drama/skills/short-drama-develop/references/episode-design.md +95 -12
  23. package/lib/drama/skills/short-drama-develop/references/genre-and-hook-playbook.md +9 -5
  24. package/lib/drama/skills/short-drama-develop/references/genre-cards//345/244/215/344/273/207/346/211/223/350/204/270.md +1 -1
  25. package/lib/drama/skills/short-drama-develop/references/genre-cards//347/263/273/347/273/237/346/265/201.md +51 -0
  26. package/lib/drama/skills/short-drama-develop/references/genre-cards//351/200/206/350/242/255.md +51 -0
  27. package/lib/drama/skills/short-drama-develop/references/genre-cards//351/207/215/347/224/237/347/251/277/350/266/212.md +51 -0
  28. package/lib/drama/skills/short-drama-develop/references/genre-cards.md +10 -7
  29. package/lib/drama/skills/short-drama-develop/references/premise-devices.md +15 -9
  30. package/lib/drama/skills/short-drama-develop/references/serial-character-and-memory.md +3 -2
  31. package/lib/drama/skills/short-drama-develop/references/stage-contract.md +14 -7
  32. package/lib/drama/skills/short-drama-develop/references/story-craft.md +7 -3
  33. package/lib/drama/skills/short-drama-edit/SKILL.md +65 -44
  34. package/lib/drama/skills/short-drama-edit/assets/remotion/README.md +49 -13
  35. package/lib/drama/skills/short-drama-edit/assets/remotion/package.json +4 -2
  36. package/lib/drama/skills/short-drama-edit/assets/remotion/src/Overlay.tsx +31 -0
  37. package/lib/drama/skills/short-drama-edit/assets/remotion/src/Root.tsx +5 -5
  38. package/lib/drama/skills/short-drama-edit/assets/remotion/src/Subtitles.tsx +68 -69
  39. package/lib/drama/skills/short-drama-edit/assets/remotion/src/font.ts +39 -29
  40. package/lib/drama/skills/short-drama-edit/assets/remotion/src/rules.mjs +188 -0
  41. package/lib/drama/skills/short-drama-edit/assets/remotion/src/schema.ts +26 -13
  42. package/lib/drama/skills/short-drama-edit/assets/remotion/src/screen/Card.tsx +72 -0
  43. package/lib/drama/skills/short-drama-edit/assets/remotion/src/screen/CornerChip.tsx +39 -0
  44. package/lib/drama/skills/short-drama-edit/assets/remotion/src/screen/ScreenText.tsx +36 -0
  45. package/lib/drama/skills/short-drama-edit/assets/remotion/src/screen/SystemPanel.tsx +44 -0
  46. package/lib/drama/skills/short-drama-edit/assets/remotion/src/screen/TaskPanel.tsx +100 -0
  47. package/lib/drama/skills/short-drama-edit/assets/remotion/src/screen/chrome.tsx +197 -0
  48. package/lib/drama/skills/short-drama-edit/assets/remotion/src/screen/tokens.ts +62 -0
  49. package/lib/drama/skills/short-drama-edit/assets/remotion/tsconfig.json +1 -0
  50. package/lib/drama/skills/short-drama-edit/assets//345/211/252/350/276/221/345/215/225.md +6 -4
  51. package/lib/drama/skills/short-drama-edit/references/cut-craft.md +26 -67
  52. package/lib/drama/skills/short-drama-edit/references/delivery-verify.md +31 -56
  53. package/lib/drama/skills/short-drama-edit/references/generated-footage.md +42 -78
  54. package/lib/drama/skills/short-drama-edit/references/sound-and-subtitles.md +44 -92
  55. package/lib/drama/skills/short-drama-edit/references/stage-contract.md +14 -16
  56. package/lib/drama/skills/short-drama-edit/scripts/edit_tool.py +1272 -125
  57. package/lib/drama/skills/short-drama-edit/scripts/selftest.py +113 -9
  58. package/lib/drama/skills/short-drama-image-prompts/SKILL.md +2 -0
  59. package/lib/drama/skills/short-drama-image-prompts/references/common-recipe.md +11 -0
  60. package/lib/drama/skills/short-drama-novel-analyze/SKILL.md +3 -3
  61. package/lib/drama/skills/short-drama-novel-analyze/assets/episode-candidate.example.jsonl +2 -2
  62. package/lib/drama/skills/short-drama-novel-analyze/references/adaptation-triage.md +8 -4
  63. package/lib/drama/skills/short-drama-novel-analyze/references/adaptation-value.md +10 -3
  64. package/lib/drama/skills/short-drama-novel-analyze/references/aggregation-and-entities.md +25 -1
  65. package/lib/drama/skills/short-drama-novel-analyze/references/stage-contract.md +1 -0
  66. package/lib/drama/skills/short-drama-produce/SKILL.md +5 -1
  67. package/lib/drama/skills/short-drama-produce/references/adapter-contract.md +14 -20
  68. package/lib/drama/skills/short-drama-produce/references/providers/minimax-h3-video.md +8 -26
  69. package/lib/drama/skills/short-drama-produce/references/providers/minimax-speech.md +41 -0
  70. package/lib/drama/skills/short-drama-produce/scripts/provider_adapters.py +142 -1
  71. package/lib/drama/skills/short-drama-review/SKILL.md +4 -5
  72. package/lib/drama/skills/short-drama-review/references/production-quality-gates.md +6 -19
  73. package/lib/drama/skills/short-drama-review/references/review-method.md +6 -1
  74. package/lib/drama/skills/short-drama-review/references/rubric-assets-prompts.md +11 -0
  75. package/lib/drama/skills/short-drama-review/references/rubric-story-script.md +77 -3
  76. package/lib/drama/skills/short-drama-review/references/rubric-visual-motion.md +24 -35
  77. package/lib/drama/skills/short-drama-review/references/stage-contract.md +3 -0
  78. package/lib/drama/skills/short-drama-storyboard/SKILL.md +23 -11
  79. package/lib/drama/skills/short-drama-storyboard/references/blocking-playbooks.md +1 -1
  80. package/lib/drama/skills/short-drama-storyboard/references/lighting-craft.md +39 -0
  81. package/lib/drama/skills/short-drama-storyboard/references/production-shot-grammar.md +2 -2
  82. package/lib/drama/skills/short-drama-storyboard/references/shot-craft.md +139 -34
  83. package/lib/drama/skills/short-drama-storyboard/references/stage-contract.md +5 -3
  84. package/lib/drama/skills/short-drama-video-prompts/SKILL.md +15 -12
  85. package/lib/drama/skills/short-drama-video-prompts/references/camera-audio-continuity.md +29 -0
  86. package/lib/drama/skills/short-drama-video-prompts/references/delivery-profile.md +4 -5
  87. package/lib/drama/skills/short-drama-video-prompts/references/generability.md +4 -2
  88. package/lib/drama/skills/short-drama-video-prompts/references/minimax-h3.md +52 -39
  89. package/lib/drama/skills/short-drama-video-prompts/references/motion-recipe.md +24 -21
  90. package/lib/drama/skills/short-drama-video-prompts/references/performance-action-timing.md +9 -23
  91. package/lib/drama/skills/short-drama-video-prompts/references/production-prompt-grammar.md +14 -26
  92. package/lib/drama/skills/short-drama-video-prompts/references/review-and-fixtures.md +1 -1
  93. package/lib/drama/skills/short-drama-video-prompts/references/seedance-2.0.md +2 -3
  94. package/lib/drama/skills/short-drama-video-prompts/references/seedance-2.5.md +34 -9
  95. package/lib/drama/skills/short-drama-video-prompts/references/stage-contract.md +3 -2
  96. package/lib/drama/skills/short-drama-video-prompts/references/target-model-profile.md +8 -1
  97. package/lib/drama/skills/short-drama-video-prompts/references/wan-3.0.md +103 -0
  98. package/lib/drama/skills/short-drama-video-prompts/scripts/motion_timing_check.py +8 -7
  99. package/lib/drama/skills/short-drama-write/SKILL.md +10 -7
  100. package/lib/drama/skills/short-drama-write/references/dialogue-craft.md +1 -1
  101. package/lib/drama/skills/short-drama-write/references/script-craft.md +56 -3
  102. package/lib/drama/skills/short-drama-write/references/stage-contract.md +4 -0
  103. package/lib/drama/skills/short-drama-write/scripts/duration_estimate.py +48 -2
  104. package/lib/index.js +346 -98
  105. package/lib/index.js.map +4 -4
  106. package/lib/novel-to-game/README.md +299 -59
  107. package/lib/novel-to-game/README_ZH.md +299 -56
  108. package/lib/novel-to-game/VERSION +1 -1
  109. package/lib/novel-to-game/examples/jin-ping-mei/build/BUILD_BRIEF.md +3 -1
  110. package/lib/novel-to-game/examples/jin-ping-mei/build/app/RUN.md +11 -3
  111. package/lib/novel-to-game/examples/jin-ping-mei/build/app/css/new-guofeng.css +397 -8
  112. package/lib/novel-to-game/examples/jin-ping-mei/build/app/css/style.css +0 -31
  113. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/data.js +71 -73
  114. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/engine.js +78 -89
  115. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/main.js +37 -14
  116. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/text.js +5 -5
  117. package/lib/novel-to-game/examples/jin-ping-mei/build/app/package.json +2 -1
  118. package/lib/novel-to-game/examples/jin-ping-mei/qa/README.md +11 -16
  119. package/lib/novel-to-game/examples/jin-ping-mei/qa/verification.json +11 -11
  120. package/lib/novel-to-game/examples/jin-ping-mei/qa/verify_playable_model.mjs +23 -0
  121. package/lib/novel-to-game/manifest.json +77 -87
  122. package/lib/novel-to-game/skills/game-art-direction/SKILL.md +7 -12
  123. package/lib/novel-to-game/skills/game-art-direction/references/art-direction-method.md +27 -106
  124. package/lib/novel-to-game/skills/game-build/SKILL.md +13 -14
  125. package/lib/novel-to-game/skills/game-build/references/blender-asset-workflow.md +24 -0
  126. package/lib/novel-to-game/skills/game-build/references/build-brief-contract.md +45 -82
  127. package/lib/novel-to-game/skills/game-build/references/generative-media-pipeline.md +14 -45
  128. package/lib/novel-to-game/skills/game-build/references/playable-model-contract.md +30 -65
  129. package/lib/novel-to-game/skills/game-build/references/tts-production-contract.md +13 -27
  130. package/lib/novel-to-game/skills/game-concept/SKILL.md +13 -15
  131. package/lib/novel-to-game/skills/game-concept/references/concept-method.md +28 -75
  132. package/lib/novel-to-game/skills/game-qa/SKILL.md +6 -7
  133. package/lib/novel-to-game/skills/game-qa/references/test-design-method.md +9 -5
  134. package/lib/novel-to-game/skills/game-world-design/SKILL.md +12 -16
  135. package/lib/novel-to-game/skills/game-world-design/references/narrative-design-method.md +23 -72
  136. package/lib/novel-to-game/skills/game-world-design/references/numeric-design-method.md +14 -48
  137. package/lib/novel-to-game/skills/game-world-design/references/world-design-method.md +36 -103
  138. package/lib/novel-to-game/skills/novel-game-analyze/SKILL.md +4 -10
  139. package/lib/novel-to-game/skills/novel-game-analyze/references/gameability-protocol.md +4 -4
  140. package/lib/novel-to-game/skills/novel-to-game/SKILL.md +9 -21
  141. package/lib/novel-to-game/skills/novel-to-game/references/intake-method.md +5 -4
  142. package/lib/novel-to-game/skills/novel-to-game/references/pipeline-contract.md +5 -8
  143. package/lib/oh-story/manifest.json +607 -327
  144. package/lib/oh-story/roles/chapter-extractor.md +177 -237
  145. package/lib/oh-story/roles/character-designer.md +1 -2
  146. package/lib/oh-story/roles/consistency-checker.md +15 -13
  147. package/lib/oh-story/roles/narrative-writer.md +36 -38
  148. package/lib/oh-story/roles/story-architect.md +9 -61
  149. package/lib/oh-story/roles/story-explorer.md +20 -125
  150. package/lib/oh-story/roles/story-researcher.md +0 -1
  151. package/lib/oh-story/skills/story/SKILL.md +15 -9
  152. package/lib/oh-story/skills/story/VERSION +1 -1
  153. package/lib/oh-story/skills/story/references/author-memory-maintenance.md +75 -0
  154. package/lib/oh-story/skills/story/references/author-memory.md +46 -94
  155. package/lib/oh-story/skills/story/scripts/author_memory_commit.py +934 -170
  156. package/lib/oh-story/skills/story-cover/SKILL.md +10 -1
  157. package/lib/oh-story/skills/story-deslop/SKILL.md +59 -73
  158. package/lib/oh-story/skills/story-deslop/references/agent-calls.md +5 -0
  159. package/lib/oh-story/skills/story-deslop/references/anti-ai-writing.md +12 -11
  160. package/lib/oh-story/skills/story-deslop/references/author-memory-maintenance.md +75 -0
  161. package/lib/oh-story/skills/story-deslop/references/author-memory.md +46 -94
  162. package/lib/oh-story/skills/story-deslop/references/banned-words.md +2 -2
  163. package/lib/oh-story/skills/story-deslop/references/deslop-gates.md +22 -5
  164. package/lib/oh-story/skills/story-deslop/references/style-resolution.md +4 -4
  165. package/lib/oh-story/skills/story-deslop/scripts/author_memory_commit.py +934 -170
  166. package/lib/oh-story/skills/story-deslop/scripts/check-ai-patterns.js +155 -8
  167. package/lib/oh-story/skills/story-import/SKILL.md +64 -584
  168. package/lib/oh-story/skills/story-import/references/character-state-reverse.md +1 -1
  169. package/lib/oh-story/skills/story-import/references/deep-analysis.md +47 -0
  170. package/lib/oh-story/skills/story-import/references/format-and-structure.md +1 -1
  171. package/lib/oh-story/skills/story-import/references/import-report.md +64 -0
  172. package/lib/oh-story/skills/story-import/references/import-special-cases.md +34 -0
  173. package/lib/oh-story/skills/story-import/references/import-tracking.md +95 -0
  174. package/lib/oh-story/skills/story-import/references/length-routing.md +1 -1
  175. package/lib/oh-story/skills/story-import/references/outline-reverse-rules.md +79 -0
  176. package/lib/oh-story/skills/story-import/references/outline-reverse.md +24 -0
  177. package/lib/oh-story/skills/story-import/references/state-tracking.md +4 -69
  178. package/lib/oh-story/skills/story-import/references/structure-mapping-long.md +98 -92
  179. package/lib/oh-story/skills/story-import/references/structure-mapping-short.md +24 -12
  180. package/lib/oh-story/skills/story-import/references/tracking-initialization.md +1 -1
  181. package/lib/oh-story/skills/story-import/references/tracking-transaction.md +15 -11
  182. package/lib/oh-story/skills/story-import/scripts/build_outline_brief.py +332 -0
  183. package/lib/oh-story/skills/story-import/scripts/check-outline-contract.js +132 -27
  184. package/lib/oh-story/skills/story-import/scripts/tracking_commit.py +478 -15
  185. package/lib/oh-story/skills/story-import/scripts/wordcount_core.py +182 -23
  186. package/lib/oh-story/skills/story-long-analyze/SKILL.md +81 -270
  187. package/lib/oh-story/skills/story-long-analyze/references/author-facing.md +252 -0
  188. package/lib/oh-story/skills/story-long-analyze/references/deconstruction-notes.md +3 -3
  189. package/lib/oh-story/skills/story-long-analyze/references/final-checks.md +19 -0
  190. package/lib/oh-story/skills/story-long-analyze/references/index-rebuild.md +11 -0
  191. package/lib/oh-story/skills/story-long-analyze/references/inspiration-library.md +105 -0
  192. package/lib/oh-story/skills/story-long-analyze/references/pipeline-ops.md +138 -64
  193. package/lib/oh-story/skills/story-long-analyze/references/semantic-acceptance-fixtures.md +84 -0
  194. package/lib/oh-story/skills/story-long-analyze/references/stage1-golden-chapters.md +128 -0
  195. package/lib/oh-story/skills/story-long-analyze/references/stage2-extraction.md +229 -0
  196. package/lib/oh-story/skills/story-long-analyze/references/stage3-plot-rhythm.md +480 -0
  197. package/lib/oh-story/skills/story-long-analyze/references/stage4-characters-settings.md +195 -0
  198. package/lib/oh-story/skills/story-long-analyze/references/stage5-report.md +33 -0
  199. package/lib/oh-story/skills/story-long-analyze/references/style-profile-generator.md +55 -128
  200. package/lib/oh-story/skills/story-long-analyze/references/style-profile-protocol.md +6 -6
  201. package/lib/oh-story/skills/story-long-analyze/references/synthesis-inputs.md +39 -0
  202. package/lib/oh-story/skills/story-long-analyze/scripts/build_chapter_index.py +466 -0
  203. package/lib/oh-story/skills/story-long-analyze/scripts/inspect_existing_assets.py +702 -0
  204. package/lib/oh-story/skills/story-long-analyze/scripts/inspiration_index.py +864 -0
  205. package/lib/oh-story/skills/story-long-analyze/scripts/manage_analysis_run.py +1145 -0
  206. package/lib/oh-story/skills/story-long-analyze/scripts/render_relation_chart.py +425 -0
  207. package/lib/oh-story/skills/story-long-scan/SKILL.md +45 -194
  208. package/lib/oh-story/skills/story-long-scan/references/platform-ciweimao.md +19 -0
  209. package/lib/oh-story/skills/story-long-scan/references/platform-fanqie.md +45 -0
  210. package/lib/oh-story/skills/story-long-scan/references/platform-jjwxc.md +28 -0
  211. package/lib/oh-story/skills/story-long-scan/references/platform-qidian.md +35 -0
  212. package/lib/oh-story/skills/story-long-scan/references/platform-qimao.md +27 -0
  213. package/lib/oh-story/skills/story-long-scan/references/topic-decision.md +6 -2
  214. package/lib/oh-story/skills/story-long-write/SKILL.md +49 -81
  215. package/lib/oh-story/skills/story-long-write/references/agent-calls.md +47 -0
  216. package/lib/oh-story/skills/story-long-write/references/anti-ai-writing.md +12 -11
  217. package/lib/oh-story/skills/story-long-write/references/artifact-protocols.md +24 -202
  218. package/lib/oh-story/skills/story-long-write/references/author-memory-maintenance.md +75 -0
  219. package/lib/oh-story/skills/story-long-write/references/author-memory.md +46 -94
  220. package/lib/oh-story/skills/story-long-write/references/banned-words.md +2 -2
  221. package/lib/oh-story/skills/story-long-write/references/benchmark-recall.md +7 -11
  222. package/lib/oh-story/skills/story-long-write/references/character-relations.md +49 -1
  223. package/lib/oh-story/skills/story-long-write/references/cross-book-recall.md +26 -0
  224. package/lib/oh-story/skills/story-long-write/references/deslop-gates.md +171 -0
  225. package/lib/oh-story/skills/story-long-write/references/dialogue-mastery.md +4 -0
  226. package/lib/oh-story/skills/story-long-write/references/emotion-on-page.md +116 -0
  227. package/lib/oh-story/skills/story-long-write/references/emotional-arc-design.md +1 -114
  228. package/lib/oh-story/skills/story-long-write/references/emotional-methods.md +2 -2
  229. package/lib/oh-story/skills/story-long-write/references/genre-prose-cards.md +1 -1
  230. package/lib/oh-story/skills/story-long-write/references/long-chapter-quality.md +7 -15
  231. package/lib/oh-story/skills/story-long-write/references/long-format.md +0 -8
  232. package/lib/oh-story/skills/story-long-write/references/long-genre-catalog.md +1 -1
  233. package/lib/oh-story/skills/story-long-write/references/long-genre-mechanics.md +3 -3
  234. package/lib/oh-story/skills/story-long-write/references/opening-design.md +2 -2
  235. package/lib/oh-story/skills/story-long-write/references/outline-conflict.md +4 -4
  236. package/lib/oh-story/skills/story-long-write/references/outline-methods.md +7 -7
  237. package/lib/oh-story/skills/story-long-write/references/outline-rhythm.md +3 -3
  238. package/lib/oh-story/skills/story-long-write/references/outline-structure-theory.md +14 -69
  239. package/lib/oh-story/skills/story-long-write/references/plot-frameworks.md +1 -1
  240. package/lib/oh-story/skills/story-long-write/references/plot-special-topics.md +2 -2
  241. package/lib/oh-story/skills/story-long-write/references/project-files.md +19 -13
  242. package/lib/oh-story/skills/story-long-write/references/reader-contract-and-progression.md +13 -5
  243. package/lib/oh-story/skills/story-long-write/references/reference-index.md +5 -6
  244. package/lib/oh-story/skills/story-long-write/references/stop-and-fix.md +9 -0
  245. package/lib/oh-story/skills/story-long-write/references/style-combat-face.md +1 -58
  246. package/lib/oh-story/skills/story-long-write/references/style-genre-modules.md +1 -1
  247. package/lib/oh-story/skills/story-long-write/references/style-resolution.md +4 -4
  248. package/lib/oh-story/skills/story-long-write/references/tracking-initialization.md +1 -1
  249. package/lib/oh-story/skills/story-long-write/references/tracking-transaction.md +15 -11
  250. package/lib/oh-story/skills/story-long-write/references/workflow-chapter.md +81 -121
  251. package/lib/oh-story/skills/story-long-write/references/workflow-daily.md +41 -139
  252. package/lib/oh-story/skills/story-long-write/references/workflow-outline.md +157 -0
  253. package/lib/oh-story/skills/story-long-write/references/workflow-revision.md +57 -67
  254. package/lib/oh-story/skills/story-long-write/references/workflow-setup.md +112 -253
  255. package/lib/oh-story/skills/story-long-write/references/workflow-volume.md +142 -0
  256. package/lib/oh-story/skills/story-long-write/references/writer-prompt-fallback.md +6 -4
  257. package/lib/oh-story/skills/story-long-write/references/writing-craft.md +19 -65
  258. package/lib/oh-story/skills/story-long-write/scripts/author_memory_commit.py +934 -170
  259. package/lib/oh-story/skills/story-long-write/scripts/build_architect_brief.py +260 -0
  260. package/lib/oh-story/skills/story-long-write/scripts/build_writer_prompt.py +226 -25
  261. package/lib/oh-story/skills/story-long-write/scripts/check-ai-patterns.js +155 -8
  262. package/lib/oh-story/skills/story-long-write/scripts/check-outline-contract.js +132 -27
  263. package/lib/oh-story/skills/story-long-write/scripts/check-outline-copy.js +7 -2
  264. package/lib/oh-story/skills/story-long-write/scripts/outline_view.py +7 -4
  265. package/lib/oh-story/skills/story-long-write/scripts/storyctl.py +304 -46
  266. package/lib/oh-story/skills/story-long-write/scripts/tracking_commit.py +478 -15
  267. package/lib/oh-story/skills/story-long-write/scripts/wordcount_core.py +182 -23
  268. package/lib/oh-story/skills/story-review/SKILL.md +31 -302
  269. package/lib/oh-story/skills/story-review/references/agent-prompts.md +184 -0
  270. package/lib/oh-story/skills/story-review/references/anti-ai-writing.md +12 -11
  271. package/lib/oh-story/skills/story-review/references/author-memory-maintenance.md +75 -0
  272. package/lib/oh-story/skills/story-review/references/author-memory.md +46 -94
  273. package/lib/oh-story/skills/story-review/references/banned-words.md +2 -2
  274. package/lib/oh-story/skills/story-review/references/batch-review.md +19 -0
  275. package/lib/oh-story/skills/story-review/references/character-relations.md +49 -1
  276. package/lib/oh-story/skills/story-review/references/dialogue-mastery.md +4 -0
  277. package/lib/oh-story/skills/story-review/references/review-quality.md +6 -11
  278. package/lib/oh-story/skills/story-review/references/review-tracking.md +10 -0
  279. package/lib/oh-story/skills/story-review/references/solo.md +52 -0
  280. package/lib/oh-story/skills/story-review/references/style-resolution.md +4 -4
  281. package/lib/oh-story/skills/story-review/references/tracking-initialization.md +1 -1
  282. package/lib/oh-story/skills/story-review/references/tracking-transaction.md +15 -11
  283. package/lib/oh-story/skills/story-review/scripts/author_memory_commit.py +934 -170
  284. package/lib/oh-story/skills/story-review/scripts/check-ai-patterns.js +155 -8
  285. package/lib/oh-story/skills/story-review/scripts/tracking_commit.py +478 -15
  286. package/lib/oh-story/skills/story-review/scripts/wordcount_core.py +182 -23
  287. package/lib/oh-story/skills/story-setup/SKILL.md +71 -452
  288. package/lib/oh-story/skills/story-setup/references/agent-references/agent-quality.md +4 -4
  289. package/lib/oh-story/skills/story-setup/references/agent-references/anti-ai-writing.md +12 -11
  290. package/lib/oh-story/skills/story-setup/references/agent-references/banned-words.md +2 -2
  291. package/lib/oh-story/skills/story-setup/references/agent-references/benchmark-style-load.md +107 -0
  292. package/lib/oh-story/skills/story-setup/references/agent-references/character-relations.md +49 -1
  293. package/lib/oh-story/skills/story-setup/references/agent-references/deslop-gates.md +22 -5
  294. package/lib/oh-story/skills/story-setup/references/agent-references/dialogue-mastery.md +4 -0
  295. package/lib/oh-story/skills/story-setup/references/agent-references/emotion-on-page.md +116 -0
  296. package/lib/oh-story/skills/story-setup/references/agent-references/emotional-arc-design.md +1 -114
  297. package/lib/oh-story/skills/story-setup/references/agent-references/format-and-structure.md +1 -1
  298. package/lib/oh-story/skills/story-setup/references/agent-references/genre-prose-cards.md +1 -1
  299. package/lib/oh-story/skills/story-setup/references/agent-references/long-emotional-methods.md +2 -2
  300. package/lib/oh-story/skills/story-setup/references/agent-references/long-genre-catalog.md +1 -1
  301. package/lib/oh-story/skills/story-setup/references/agent-references/long-genre-mechanics.md +3 -3
  302. package/lib/oh-story/skills/story-setup/references/agent-references/long-quality.md +7 -5
  303. package/lib/oh-story/skills/story-setup/references/agent-references/opening-design.md +2 -2
  304. package/lib/oh-story/skills/story-setup/references/agent-references/outline-conflict.md +4 -4
  305. package/lib/oh-story/skills/story-setup/references/agent-references/outline-methods.md +7 -7
  306. package/lib/oh-story/skills/story-setup/references/agent-references/outline-rhythm.md +3 -3
  307. package/lib/oh-story/skills/story-setup/references/agent-references/short-emotional-methods.md +1 -1
  308. package/lib/oh-story/skills/story-setup/references/agent-references/short-genre-formulas.md +0 -15
  309. package/lib/oh-story/skills/story-setup/references/agent-references/short-reversal.md +1 -1
  310. package/lib/oh-story/skills/story-setup/references/agent-references/style-combat-face.md +1 -58
  311. package/lib/oh-story/skills/story-setup/references/agent-references/style-genre-modules.md +1 -1
  312. package/lib/oh-story/skills/story-setup/references/agent-references/style-resolution.md +4 -4
  313. package/lib/oh-story/skills/story-setup/references/agent-references/writing-craft.md +19 -65
  314. package/lib/oh-story/skills/story-setup/references/diagnostics.md +4 -4
  315. package/lib/oh-story/skills/story-short-analyze/SKILL.md +56 -110
  316. package/lib/oh-story/skills/story-short-analyze/references/analysis-character-relations.md +1 -1
  317. package/lib/oh-story/skills/story-short-analyze/references/analysis-method.md +42 -0
  318. package/lib/oh-story/skills/story-short-analyze/references/optional-modules.md +97 -0
  319. package/lib/oh-story/skills/story-short-analyze/references/output-contract.md +9 -47
  320. package/lib/oh-story/skills/story-short-analyze/references/quality-checklist.md +80 -0
  321. package/lib/oh-story/skills/story-short-analyze/references/source-story-quality.md +4 -4
  322. package/lib/oh-story/skills/story-short-analyze/references/stage2-3-structure-emotion.md +308 -0
  323. package/lib/oh-story/skills/story-short-analyze/references/stage4-6-reversal-summary.md +611 -0
  324. package/lib/oh-story/skills/story-short-scan/SKILL.md +35 -65
  325. package/lib/oh-story/skills/story-short-scan/references/manual-rank-input.md +36 -0
  326. package/lib/oh-story/skills/story-short-scan/references/platform-dianzhong.md +24 -0
  327. package/lib/oh-story/skills/story-short-scan/references/platform-heiyan.md +21 -0
  328. package/lib/oh-story/skills/story-short-scan/references/topic-match.md +38 -0
  329. package/lib/oh-story/skills/story-short-write/SKILL.md +23 -175
  330. package/lib/oh-story/skills/story-short-write/references/agent-calls.md +13 -0
  331. package/lib/oh-story/skills/story-short-write/references/author-memory-maintenance.md +75 -0
  332. package/lib/oh-story/skills/story-short-write/references/author-memory.md +46 -94
  333. package/lib/oh-story/skills/story-short-write/references/banned-words.md +2 -2
  334. package/lib/oh-story/skills/story-short-write/references/benchmark-recall.md +1 -21
  335. package/lib/oh-story/skills/story-short-write/references/dialogue-mastery.md +4 -0
  336. package/lib/oh-story/skills/story-short-write/references/emotional-methods.md +1 -1
  337. package/lib/oh-story/skills/story-short-write/references/genre-writing-formulas.md +0 -15
  338. package/lib/oh-story/skills/story-short-write/references/output-contract.md +9 -47
  339. package/lib/oh-story/skills/story-short-write/references/reference-index.md +17 -47
  340. package/lib/oh-story/skills/story-short-write/references/scene-craft.md +40 -0
  341. package/lib/oh-story/skills/story-short-write/references/short-craft.md +7 -43
  342. package/lib/oh-story/skills/story-short-write/references/short-format.md +3 -3
  343. package/lib/oh-story/skills/story-short-write/references/short-prose-quality.md +2 -1
  344. package/lib/oh-story/skills/story-short-write/references/short-reversal.md +1 -1
  345. package/lib/oh-story/skills/story-short-write/references/style-resolution.md +4 -4
  346. package/lib/oh-story/skills/story-short-write/references/submission-craft.md +1 -1
  347. package/lib/oh-story/skills/story-short-write/references/villain-and-reveal.md +5 -10
  348. package/lib/oh-story/skills/story-short-write/references/workflow-design.md +21 -13
  349. package/lib/oh-story/skills/story-short-write/references/workflow-draft.md +116 -0
  350. package/lib/oh-story/skills/story-short-write/references/workflow-revision.md +99 -0
  351. package/lib/oh-story/skills/story-short-write/references/writing-workflow.md +7 -119
  352. package/lib/oh-story/skills/story-short-write/scripts/author_memory_commit.py +934 -170
  353. package/lib/oh-story/skills/story-short-write/scripts/check-ai-patterns.js +155 -8
  354. package/lib/oh-story/skills/story-short-write/scripts/check-outline-copy.js +7 -2
  355. package/lib/types/client/drama-production-view.d.ts.map +1 -1
  356. package/lib/types/client/file-activity.d.ts +19 -1
  357. package/lib/types/client/file-activity.d.ts.map +1 -1
  358. package/lib/types/client/index.d.ts.map +1 -1
  359. package/lib/types/client/production-prompts.d.ts +7 -0
  360. package/lib/types/client/production-prompts.d.ts.map +1 -1
  361. package/lib/types/client/production-runtime.d.ts +16 -0
  362. package/lib/types/client/production-runtime.d.ts.map +1 -1
  363. package/lib/types/client/video-studio.d.ts.map +1 -1
  364. package/lib/types/drama-adapters.d.ts +5 -3
  365. package/lib/types/drama-adapters.d.ts.map +1 -1
  366. package/lib/types/native-hooks.d.ts +53 -1
  367. package/lib/types/native-hooks.d.ts.map +1 -1
  368. package/lib/types/role-identity.d.ts +17 -0
  369. package/lib/types/role-identity.d.ts.map +1 -0
  370. package/lib/types/role-tool.d.ts.map +1 -1
  371. package/lib/types/skill-provider.d.ts.map +1 -1
  372. package/lib/types/video-project.d.ts.map +1 -1
  373. package/lib/types/workspace-route.d.ts.map +1 -1
  374. package/lib/video-recap/CHANGELOG.md +60 -1
  375. package/lib/video-recap/README.en.md +348 -137
  376. package/lib/video-recap/README.md +340 -136
  377. package/lib/video-recap/manifest.json +406 -251
  378. package/lib/video-recap/skills/video-assemble/SKILL.md +30 -15
  379. package/lib/video-recap/skills/video-assemble/references/audio-modes.md +63 -0
  380. package/lib/video-recap/skills/video-assemble/references/explicit-audio-mix.md +75 -0
  381. package/lib/video-recap/skills/video-assemble/references/foreground-compose.md +118 -0
  382. package/lib/video-recap/skills/video-assemble/references/narration-adoption.md +103 -0
  383. package/lib/video-recap/skills/video-assemble/references/packaging.md +45 -0
  384. package/lib/video-recap/skills/video-assemble/references/pair-media.md +74 -0
  385. package/lib/video-recap/skills/video-assemble/references/source-score.md +147 -0
  386. package/lib/video-recap/skills/video-assemble/references/subtitle-track.md +202 -0
  387. package/lib/video-recap/skills/video-assemble/scripts/adoption/__init__.py +1 -0
  388. package/lib/video-recap/skills/video-assemble/scripts/adoption/audio_mix_binding.py +279 -0
  389. package/lib/video-recap/skills/video-assemble/scripts/adoption/frozen_audio.py +187 -0
  390. package/lib/video-recap/skills/video-assemble/scripts/adoption/narration_binding.py +323 -0
  391. package/lib/video-recap/skills/video-assemble/scripts/adoption/strict_inputs.py +109 -0
  392. package/lib/video-recap/skills/video-assemble/scripts/adoption/strict_publish.py +130 -0
  393. package/lib/video-recap/skills/video-assemble/scripts/artifacts.py +10 -46
  394. package/lib/video-recap/skills/video-assemble/scripts/assemble.py +378 -119
  395. package/lib/video-recap/skills/video-assemble/scripts/assemble_constants.py +29 -12
  396. package/lib/video-recap/skills/video-assemble/scripts/assembly_contract.py +95 -44
  397. package/lib/video-recap/skills/video-assemble/scripts/assembly_settings.py +75 -32
  398. package/lib/video-recap/skills/video-assemble/scripts/audio_mix.py +11 -11
  399. package/lib/video-recap/skills/video-assemble/scripts/compose_foreground.py +293 -0
  400. package/lib/video-recap/skills/video-assemble/scripts/export_jianying.py +71 -78
  401. package/lib/video-recap/skills/video-assemble/scripts/jianying/__init__.py +1 -0
  402. package/lib/video-recap/skills/video-assemble/scripts/{jianying_builders.py → jianying/builders.py} +39 -67
  403. package/lib/video-recap/skills/video-assemble/scripts/{jianying_model.py → jianying/model.py} +3 -11
  404. package/lib/video-recap/skills/video-assemble/scripts/{jianying_optional.py → jianying/optional.py} +1 -1
  405. package/lib/video-recap/skills/video-assemble/scripts/jianying/schema.py +53 -0
  406. package/lib/video-recap/skills/video-assemble/scripts/{jianying_templates.py → jianying/templates.py} +1 -1
  407. package/lib/video-recap/skills/video-assemble/scripts/{jianying_timeline_contract.py → jianying/timeline_contract.py} +34 -13
  408. package/lib/video-recap/skills/video-assemble/scripts/{jianying_writer.py → jianying/writer.py} +7 -17
  409. package/lib/video-recap/skills/video-assemble/scripts/lib.py +37 -4
  410. package/lib/video-recap/skills/video-assemble/scripts/media.py +12 -21
  411. package/lib/video-recap/skills/video-assemble/scripts/narration_audio.py +64 -34
  412. package/lib/video-recap/skills/video-assemble/scripts/packaging.py +122 -0
  413. package/lib/video-recap/skills/video-assemble/scripts/pair_media.py +216 -0
  414. package/lib/video-recap/skills/video-assemble/scripts/render_preflight.py +1 -3
  415. package/lib/video-recap/skills/video-assemble/scripts/source_score.py +469 -0
  416. package/lib/video-recap/skills/video-assemble/scripts/source_subtitles.py +25 -9
  417. package/lib/video-recap/skills/video-assemble/scripts/subtitles/__init__.py +1 -0
  418. package/lib/video-recap/skills/video-assemble/scripts/{subtitle_core.py → subtitles/core.py} +13 -6
  419. package/lib/video-recap/skills/video-assemble/scripts/{subtitle_render.py → subtitles/render.py} +5 -4
  420. package/lib/video-recap/skills/video-assemble/scripts/subtitles/track.py +324 -0
  421. package/lib/video-recap/skills/video-assemble/scripts/subtitles/track_binding.py +200 -0
  422. package/lib/video-recap/skills/video-assemble/scripts/timeline.py +30 -46
  423. package/lib/video-recap/skills/video-assemble/scripts/timeline_emit.py +91 -7
  424. package/lib/video-recap/skills/video-assemble/scripts/visual_render.py +22 -8
  425. package/lib/video-recap/skills/video-cut/SKILL.md +34 -16
  426. package/lib/video-recap/skills/video-cut/references/shot-review.md +50 -0
  427. package/lib/video-recap/skills/video-cut/scripts/cut.py +2 -28
  428. package/lib/video-recap/skills/video-cut/scripts/cut_cli.py +58 -62
  429. package/lib/video-recap/skills/video-cut/scripts/cut_contract.py +21 -106
  430. package/lib/video-recap/skills/video-cut/scripts/cut_render.py +14 -9
  431. package/lib/video-recap/skills/video-cut/scripts/lib.py +39 -0
  432. package/lib/video-recap/skills/video-cut/scripts/narration_mapping.py +2 -155
  433. package/lib/video-recap/skills/video-cut/scripts/narrative_selection.py +244 -0
  434. package/lib/video-recap/skills/video-cut/scripts/shot_review.py +357 -0
  435. package/lib/video-recap/skills/video-recap/SKILL.md +86 -42
  436. package/lib/video-recap/skills/video-recap/references/audio-routing.md +126 -0
  437. package/lib/video-recap/skills/video-recap/references/config-playbook.md +12 -8
  438. package/lib/video-recap/skills/video-recap/references/data-schema.md +44 -33
  439. package/lib/video-recap/skills/video-recap/references/resource-library.md +151 -0
  440. package/lib/video-recap/skills/video-recap/references/shift-left-qc-schema.md +1 -1
  441. package/lib/video-recap/skills/video-recap/scripts/doctor.py +123 -30
  442. package/lib/video-recap/skills/video-recap/scripts/final_qc.py +22 -75
  443. package/lib/video-recap/skills/video-recap/scripts/lib.py +3 -3
  444. package/lib/video-recap/skills/video-recap/scripts/library.py +509 -0
  445. package/lib/video-recap/skills/video-recap/scripts/materials.py +63 -91
  446. package/lib/video-recap/skills/video-recap/scripts/mimo_qc.py +8 -70
  447. package/lib/video-recap/skills/video-recap/scripts/project_binding.py +286 -0
  448. package/lib/video-recap/skills/video-recap/scripts/qc/__init__.py +1 -0
  449. package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_client.py → qc/mimo_client.py} +1 -1
  450. package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_evidence.py → qc/mimo_evidence.py} +33 -66
  451. package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_observations.py → qc/mimo_observations.py} +3 -11
  452. package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_payload.py → qc/mimo_payload.py} +4 -7
  453. package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_report.py → qc/mimo_report.py} +17 -28
  454. package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_runner.py → qc/mimo_runner.py} +2 -2
  455. package/lib/video-recap/skills/video-recap/scripts/qc_contract.py +0 -19
  456. package/lib/video-recap/skills/video-recap/scripts/recap_cli.py +119 -50
  457. package/lib/video-recap/skills/video-recap/scripts/recap_inspect.py +32 -114
  458. package/lib/video-recap/skills/video-recap/scripts/recap_review.py +9 -23
  459. package/lib/video-recap/skills/video-recap/scripts/recap_runner.py +387 -334
  460. package/lib/video-recap/skills/video-recap/scripts/recap_runtime.py +23 -23
  461. package/lib/video-recap/skills/video-recap/scripts/recap_source.py +278 -0
  462. package/lib/video-recap/skills/video-recap/scripts/recap_stage_qc.py +25 -12
  463. package/lib/video-recap/skills/video-recap/scripts/recap_timeline.py +59 -52
  464. package/lib/video-recap/skills/video-recap/scripts/resource_lock.py +162 -0
  465. package/lib/video-recap/skills/video-script/SKILL.md +16 -13
  466. package/lib/video-recap/skills/video-script/references/creative-editing-playbook.md +9 -5
  467. package/lib/video-recap/skills/video-script/references/promotional-copy.md +68 -0
  468. package/lib/video-recap/skills/video-script/references/research-guide.md +1 -1
  469. package/lib/video-recap/skills/video-script/scripts/evidence_bundle.py +24 -76
  470. package/lib/video-recap/skills/video-script/scripts/lib.py +10 -87
  471. package/lib/video-recap/skills/video-script/scripts/narration_lint.py +63 -39
  472. package/lib/video-recap/skills/video-script/scripts/review.py +0 -2
  473. package/lib/video-recap/skills/video-script/scripts/review_grounding.py +35 -59
  474. package/lib/video-recap/skills/video-script/scripts/review_response.py +66 -128
  475. package/lib/video-recap/skills/video-script/scripts/review_runner.py +12 -20
  476. package/lib/video-recap/skills/video-script/scripts/speech_ownership.py +4 -4
  477. package/lib/video-recap/skills/video-script/scripts/validate.py +53 -42
  478. package/lib/video-recap/skills/video-understanding/SKILL.md +6 -4
  479. package/lib/video-recap/skills/video-understanding/references/data-schema.md +50 -156
  480. package/lib/video-recap/skills/video-understanding/references/research-guide.md +4 -4
  481. package/lib/video-recap/skills/video-understanding/scripts/asr.py +85 -65
  482. package/lib/video-recap/skills/video-understanding/scripts/asr_timing_evidence.py +270 -0
  483. package/lib/video-recap/skills/video-understanding/scripts/brief.py +2 -5
  484. package/lib/video-recap/skills/video-understanding/scripts/briefing/__init__.py +1 -0
  485. package/lib/video-recap/skills/{video-script/scripts/agent_brief.py → video-understanding/scripts/briefing/builder.py} +38 -35
  486. package/lib/video-recap/skills/video-understanding/scripts/{brief_context.py → briefing/context.py} +4 -35
  487. package/lib/video-recap/skills/{video-script/scripts/brief_inputs.py → video-understanding/scripts/briefing/inputs.py} +9 -31
  488. package/lib/video-recap/skills/{video-script/scripts/brief_timeline.py → video-understanding/scripts/briefing/timeline.py} +5 -6
  489. package/lib/video-recap/skills/video-understanding/scripts/consolidate.py +65 -116
  490. package/lib/video-recap/skills/video-understanding/scripts/detect.py +31 -40
  491. package/lib/video-recap/skills/video-understanding/scripts/lib.py +30 -86
  492. package/lib/video-recap/skills/video-understanding/scripts/narration_lint.py +63 -39
  493. package/lib/video-recap/skills/video-understanding/scripts/speech_ownership.py +4 -4
  494. package/lib/video-recap/skills/video-understanding/scripts/storyboard.py +17 -79
  495. package/lib/video-recap/skills/video-understanding/scripts/understanding_brief.py +14 -40
  496. package/lib/video-recap/skills/video-understanding/scripts/understanding_cache.py +64 -46
  497. package/lib/video-recap/skills/video-understanding/scripts/understanding_runner.py +31 -11
  498. package/lib/video-recap/skills/video-understanding/scripts/understanding_storyboard.py +19 -14
  499. package/lib/video-recap/skills/video-understanding/scripts/vlm.py +80 -108
  500. package/lib/video-recap/skills/video-voiceover/SKILL.md +29 -19
  501. package/lib/video-recap/skills/video-voiceover/references/index-tts.md +31 -0
  502. package/lib/video-recap/skills/video-voiceover/scripts/approved_text_policy.py +123 -0
  503. package/lib/video-recap/skills/video-voiceover/scripts/dub.py +51 -59
  504. package/lib/video-recap/skills/video-voiceover/scripts/lib.py +8 -44
  505. package/lib/video-recap/skills/video-voiceover/scripts/providers/__init__.py +1 -0
  506. package/lib/video-recap/skills/video-voiceover/scripts/providers/index_tts.py +184 -0
  507. package/lib/video-recap/skills/video-voiceover/scripts/voiceover.py +212 -134
  508. package/package.json +45 -45
  509. package/lib/novel-to-game/skills/game-world-design/references/dialogue-design-method.md +0 -58
  510. package/lib/novel-to-game/skills/game-world-design/references/game-writing-craft.md +0 -53
  511. package/lib/novel-to-game/skills/novel-to-game/references/intake-benchmark-reference.md +0 -38
  512. package/lib/oh-story/skills/story-long-analyze/references/material-decomposition.md +0 -530
  513. package/lib/oh-story/skills/story-long-analyze/references/output-templates.md +0 -559
  514. package/lib/oh-story/skills/story-long-scan/references/scan-output-format.md +0 -207
  515. package/lib/oh-story/skills/story-long-write/references/state-tracking.md +0 -108
  516. package/lib/oh-story/skills/story-short-analyze/references/material-decomposition.md +0 -678
  517. package/lib/oh-story/skills/story-short-analyze/references/output-templates.md +0 -561
  518. package/lib/video-recap/skills/video-assemble/scripts/jianying_schema.py +0 -98
  519. package/lib/video-recap/skills/video-recap/references/creative-editing-playbook.md +0 -225
  520. package/lib/video-recap/skills/video-recap/references/research-guide.md +0 -74
  521. package/lib/video-recap/skills/video-recap/references/timeline-and-jianying.md +0 -250
  522. package/lib/video-recap/skills/video-script/scripts/brief_context.py +0 -348
  523. package/lib/video-recap/skills/video-script/scripts/narration.py +0 -12
  524. package/lib/video-recap/skills/video-understanding/scripts/agent_brief.py +0 -399
  525. package/lib/video-recap/skills/video-understanding/scripts/brief_inputs.py +0 -290
  526. package/lib/video-recap/skills/video-understanding/scripts/brief_timeline.py +0 -327
  527. /package/lib/video-recap/skills/video-assemble/scripts/{jianying_tracks.py → jianying/tracks.py} +0 -0
  528. /package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_contract.py → qc/mimo_contract.py} +0 -0
  529. /package/lib/video-recap/skills/video-voiceover/scripts/{fish_audio.py → providers/fish_audio.py} +0 -0
@@ -1,12 +1,34 @@
1
1
  """Shared constants for the self-contained video-assemble skill."""
2
2
 
3
- SUBTITLE_RENDER_VERSION = 8
4
- SUBTITLE_TEXT_NORMALIZE_VERSION = 1
3
+ from fractions import Fraction
4
+ import math
5
+
5
6
  ASSEMBLY_MANIFEST = "assembly_manifest.json"
6
7
  ASSEMBLY_QC = "assembly_qc.json"
7
8
  VISUAL_QC = "visual_qc.json"
8
9
  VISUAL_OVERLAYS = "visual_overlays.json"
9
10
  SEGMENT_AUDIO_SCHEMA_VERSION = 1
11
+
12
+ # The picture codecs every packet/frame clock proof in this skill accepts.
13
+ SUPPORTED_PICTURE_CODECS = frozenset({"h264", "hevc"})
14
+
15
+ # The one exact output audio clock every explicit-sound artifact in this skill uses.
16
+ OUTPUT_SAMPLE_RATE = 48_000
17
+
18
+
19
+ def frame_clock_samples(frame, fps, rate=OUTPUT_SAMPLE_RATE):
20
+ """Project an exact frame boundary of a video clock onto the output sample clock.
21
+
22
+ Broadcast rates such as 30000/1001 put frame boundaries between whole samples, so
23
+ the exact Fraction position is rounded half-up once, here. Every sample bound in
24
+ the skill - a segment edge and the whole-picture `total_samples` alike - is this
25
+ single projection, so an integral clock is unchanged and a fractional one stays
26
+ consistent across the explicit-sound tools.
27
+ """
28
+ exact = Fraction(int(frame) * rate, 1) / Fraction(fps)
29
+ return math.floor(exact + Fraction(1, 2))
30
+
31
+
10
32
  FILTER_SCRIPT_THRESHOLD_BYTES = 8000
11
33
 
12
34
  # The default subtitle metrics were tuned in this reference canvas.
@@ -18,17 +40,12 @@ _SUBTITLE_CLOSING_QUOTES = "」』”’))]】》〉\"'"
18
40
  _MIN_GAP_TO_SUBTITLE = 0.8
19
41
  _MIN_READABLE_SECONDS = 0.3
20
42
  _MIN_ASR_CLIP_OVERLAP = 0.05
43
+ # timeline.py serializes interval bounds onto a 1e-4 second grid, flooring starts
44
+ # and ceiling ends, so two bounds that were identical before serialization can come
45
+ # back one grid step apart. Contiguity joins must tolerate that whole step.
46
+ _TIMELINE_TIME_GRID_SECONDS = 1e-4
47
+ _CLIP_CONTIGUITY_TOLERANCE = 1.5 * _TIMELINE_TIME_GRID_SECONDS
21
48
  _MAX_ORIGINAL_READ_CPS = 9.0
22
49
  _AUTO_ORIGINAL_READ_CPS = 6.0
23
50
 
24
- _VISUAL_DELIVERY_FORBIDDEN_KEYS = {
25
- "video_encode_passes",
26
- "reencode_reason",
27
- "audio_sample_rate",
28
- "final_compat_notes",
29
- "double_encode",
30
- "delivery_compatibility",
31
- "loudness_mode",
32
- "loudnorm_measurement",
33
- }
34
51
  _SUPPORTED_VISUAL_OVERLAY_TYPES = {"top_title", "inline_label_or_callout"}
@@ -1,7 +1,9 @@
1
1
  """Assembly manifest/QC persistence and delivery contract helpers."""
2
2
 
3
3
  import json
4
+ from fractions import Fraction
4
5
  import math
6
+ import subprocess
5
7
  import wave
6
8
  from pathlib import Path
7
9
 
@@ -12,6 +14,7 @@ from assemble_constants import (
12
14
  SEGMENT_AUDIO_SCHEMA_VERSION,
13
15
  )
14
16
  from audio_mix import _loudness_mode
17
+ from subtitles.track_binding import manifest_subtitle_evidence
15
18
  from artifacts import (
16
19
  _load_work_json,
17
20
  _source_video_identity,
@@ -25,44 +28,60 @@ _AUDIO_QC_CODES = frozenset({
25
28
 
26
29
 
27
30
  def _assembly_manifest_payload(input_video, tts_segments, work_dir, output_path,
28
- tts_meta_path=None, final_output=None, *, settings_fingerprint):
31
+ tts_meta_path=None, final_output=None, *, settings_payload,
32
+ audio_mode="narration", audio_stream_index=0,
33
+ narration_input_binding=None, audio_mix_binding=None):
29
34
  """Slim render record. The orchestrator reads `final_output` to report the result;
30
35
  `source_video` stays None unless cut mode explicitly passed --source-video, proving a
31
36
  stale ambient SOURCE_VIDEO never leaked into a full-mode timeline / 剪映 export."""
32
37
  input_video = Path(input_video)
33
38
  output_path = Path(output_path)
34
- source_video, source_video_fingerprint = _source_video_identity()
39
+ source_video_identity = _source_video_identity()
35
40
  qc_path = Path(work_dir) / ASSEMBLY_QC
36
- qc = _load_work_json(work_dir, ASSEMBLY_QC) or {}
41
+ qc = _load_work_json(work_dir, ASSEMBLY_QC) # always written by publish_render first
42
+ settings = settings_payload(
43
+ work_dir, audio_mode=audio_mode, audio_stream_index=audio_stream_index
44
+ )
37
45
  payload = {
38
46
  "schema_version": 2,
39
47
  "input_video": str(input_video.resolve()),
40
- "source_video": source_video,
41
- "source_video_fingerprint": source_video_fingerprint,
48
+ "source_video": source_video_identity["path"] if source_video_identity else None,
49
+ "source_video_identity": source_video_identity,
42
50
  "tts_meta": str(Path(tts_meta_path).resolve()) if tts_meta_path else None,
43
51
  "tts_segments": len(tts_segments),
44
- "assembly_settings": settings_fingerprint(work_dir),
52
+ "audio_mode": audio_mode,
53
+ "selected_audio_stream_index": audio_stream_index,
54
+ "assembly_settings": settings,
45
55
  "output_path": str(output_path.resolve()),
46
56
  "segment_audio_schema_version": SEGMENT_AUDIO_SCHEMA_VERSION,
47
- "qc_path": str(qc_path.resolve()) if qc else None,
48
- "qc_verdict": qc.get("verdict"),
49
- "qc_blocking_codes": qc.get("blocking_codes", []),
50
- # The settings fingerprint records the configured/fallback loudness policy; these QC
57
+ "qc_path": str(qc_path.resolve()),
58
+ "qc_verdict": qc["verdict"],
59
+ "qc_blocking_codes": qc["blocking_codes"],
60
+ # The settings payload records the configured/fallback loudness policy; these QC
51
61
  # fields record what the just-finished render actually used after the loudnorm probe.
52
- "qc_loudness_mode": qc.get("loudness_mode"),
53
- "qc_loudnorm_measurement": qc.get("loudnorm_measurement"),
62
+ "qc_loudness_mode": qc["loudness_mode"],
63
+ "qc_loudnorm_measurement": qc["loudnorm_measurement"],
64
+ "audio_operations": qc["audio_operations"],
65
+ "adopted_audio": qc["adopted_audio"],
66
+ "narration_input_binding": narration_input_binding,
67
+ "audio_mix_binding": audio_mix_binding,
54
68
  "audio_segments": [
55
69
  {
56
70
  "index": seg["index"],
57
- "segment_audio_schema_version": seg.get("segment_audio_schema_version", SEGMENT_AUDIO_SCHEMA_VERSION),
71
+ # Informational manifest fields: a tts_meta without the schema marker is v1,
72
+ # and the loudness measurements are None whenever voiceover skipped
73
+ # normalization (strict adoption fixtures in tests/orchestrator omit both).
74
+ "segment_audio_schema_version": seg.get(
75
+ "segment_audio_schema_version", SEGMENT_AUDIO_SCHEMA_VERSION
76
+ ),
58
77
  "narration": seg["narration"],
59
- "spoken_text": seg.get("spoken_text", seg["narration"]),
60
- "truncated": seg.get("truncated", False),
61
- "truncate_reason": seg.get("truncate_reason", "none"),
62
- "fit_status": seg.get("fit_status"),
63
- "blocking": seg.get("blocking", False),
64
- "audio_duration": seg.get("audio_duration"),
65
- "placed_audio_duration": seg.get("placed_audio_duration"),
78
+ "spoken_text": seg["spoken_text"],
79
+ "truncated": seg["truncated"],
80
+ "truncate_reason": seg["truncate_reason"],
81
+ "fit_status": seg["fit_status"],
82
+ "blocking": seg["blocking"],
83
+ "audio_duration": seg["audio_duration"],
84
+ "placed_audio_duration": seg["placed_audio_duration"],
66
85
  "placed_audio_path": seg.get("placed_audio_path"),
67
86
  "actual_place_start": seg.get("actual_place_start"),
68
87
  "actual_place_end": seg.get("actual_place_end"),
@@ -70,12 +89,15 @@ def _assembly_manifest_payload(input_video, tts_segments, work_dir, output_path,
70
89
  "source_restore_at": seg.get("source_restore_at"),
71
90
  "source_handoff_status": seg.get("source_handoff_status"),
72
91
  "source_entry_status": seg.get("source_entry_status"),
73
- "global_narration_speed": seg.get("global_narration_speed"),
74
- "segment_tempo_factor": seg.get("segment_tempo_factor"),
75
- "effective_tempo": seg.get("effective_tempo"),
92
+ "global_narration_speed": seg["global_narration_speed"],
93
+ "segment_tempo_factor": seg["segment_tempo_factor"],
94
+ "effective_tempo": seg["effective_tempo"],
76
95
  "rms_dbfs_before": seg.get("rms_dbfs_before"),
77
96
  "rms_dbfs_after": seg.get("rms_dbfs_after"),
78
97
  "peak_after": seg.get("peak_after"),
98
+ "output_start_sample": seg.get("output_start_sample"),
99
+ "output_end_sample": seg.get("output_end_sample"),
100
+ "adopted_gain": seg.get("adopted_gain"),
79
101
  }
80
102
  for seg in tts_segments
81
103
  ],
@@ -85,6 +107,9 @@ def _assembly_manifest_payload(input_video, tts_segments, work_dir, output_path,
85
107
  provenance = _timeline_provenance_status(work_dir)
86
108
  if provenance:
87
109
  payload["timeline_provenance"] = provenance
110
+ subtitle_evidence = manifest_subtitle_evidence(work_dir, input_video, output_path)
111
+ if subtitle_evidence is not None:
112
+ payload["subtitle_track"] = subtitle_evidence
88
113
  return payload
89
114
 
90
115
 
@@ -115,8 +140,8 @@ def _visual_qc_rollup(visual_qc):
115
140
  "overlays": {
116
141
  "present": overlays["present"],
117
142
  "rendered": overlays["rendered"],
118
- "unsupported": overlays.get("unsupported", []),
119
- "overflow": overlays.get("overflow", []),
143
+ "unsupported": overlays["unsupported"],
144
+ "overflow": overlays["overflow"],
120
145
  },
121
146
  }
122
147
 
@@ -126,10 +151,27 @@ def _placed_audio_matches_timeline(seg):
126
151
  placed_path = Path(seg["placed_audio_path"])
127
152
  if not placed_path.exists():
128
153
  return False
129
- with wave.open(str(placed_path), "rb") as placed_wav:
130
- placed_duration = placed_wav.getnframes() / placed_wav.getframerate()
131
- tolerance = 1.0 / placed_wav.getframerate()
132
- timeline_start = round(float(seg["actual_place_start"]), 4)
154
+ try:
155
+ with wave.open(str(placed_path), "rb") as placed_wav:
156
+ placed_duration = placed_wav.getnframes() / placed_wav.getframerate()
157
+ tolerance = 1.0 / placed_wav.getframerate()
158
+ except wave.Error:
159
+ result = subprocess.run(
160
+ ["ffprobe", "-v", "error", "-select_streams", "a:0", "-show_entries",
161
+ "stream=sample_rate,time_base,duration_ts", "-of", "json", str(placed_path)],
162
+ capture_output=True, text=True, timeout=600,
163
+ )
164
+ streams = json.loads(result.stdout).get("streams", []) if not result.returncode else []
165
+ if len(streams) != 1:
166
+ return False
167
+ stream = streams[0]
168
+ try:
169
+ rate = int(stream["sample_rate"])
170
+ placed_duration = float(Fraction(stream["duration_ts"]) * Fraction(stream["time_base"]))
171
+ tolerance = 1.0 / rate
172
+ except (KeyError, TypeError, ValueError, ZeroDivisionError):
173
+ return False
174
+ timeline_start = math.floor(float(seg["actual_place_start"]) * 10_000 + 1e-9) / 10_000
133
175
  timeline_end = math.ceil(float(seg["actual_place_end"]) * 10_000 - 1e-9) / 10_000
134
176
  serialized_span = timeline_end - timeline_start
135
177
  return (
@@ -138,9 +180,12 @@ def _placed_audio_matches_timeline(seg):
138
180
  )
139
181
 
140
182
 
141
- def _build_assembly_qc(tts_segments, video_duration, *, output_path=None,
142
- source_has_audio=None, loudness_mode=None, loudnorm_measurement=None,
143
- visual_qc=None, render_delivery=None):
183
+ def _build_assembly_qc(tts_segments, video_duration, *, audio_operations, render_delivery,
184
+ output_path=None, source_has_audio=None, loudness_mode=None,
185
+ loudnorm_measurement=None, visual_qc=None, audio_mode="narration",
186
+ adopted_audio=None,
187
+ narration_input_binding=None, audio_mix_binding=None,
188
+ source_audio_status=None):
144
189
  """Machine-readable assembly release gate.
145
190
 
146
191
  Visual facts are rolled up from visual_qc.json. Delivery/render facts live here
@@ -150,11 +195,11 @@ def _build_assembly_qc(tts_segments, video_duration, *, output_path=None,
150
195
  segments = tts_segments
151
196
  no_safe = [
152
197
  s["index"] for s in segments
153
- if s.get("fit_status") == "no_safe_fit"
154
- or s.get("truncate_reason") in {"no_safe_boundary", "no_room"}
155
- or s.get("blocking", False)
198
+ if s["fit_status"] == "no_safe_fit"
199
+ or s["truncate_reason"] in {"no_safe_boundary", "no_room"}
200
+ or s["blocking"]
156
201
  ]
157
- skipped = [s["index"] for s in segments if s.get("fit_status") == "skipped"]
202
+ skipped = [s["index"] for s in segments if s["fit_status"] == "skipped"]
158
203
  tempo_exceeded = []
159
204
  truncated = []
160
205
  handoff_failed = []
@@ -165,7 +210,7 @@ def _build_assembly_qc(tts_segments, video_duration, *, output_path=None,
165
210
  max_effective = max(max_effective, eff)
166
211
  if eff > hard_max + 1e-6:
167
212
  tempo_exceeded.append(s["index"])
168
- if s.get("truncated", False) or s.get("truncate_reason") == "tail_trim_tolerance":
213
+ if s["truncated"] or s["truncate_reason"] == "tail_trim_tolerance":
169
214
  truncated.append(s["index"])
170
215
  if s.get("source_handoff_blocking", False):
171
216
  handoff_failed.append(s["index"])
@@ -174,7 +219,7 @@ def _build_assembly_qc(tts_segments, video_duration, *, output_path=None,
174
219
 
175
220
  placed = [s["placed_audio_duration"] for s in segments]
176
221
  blocking_codes = []
177
- if not segments:
222
+ if audio_mode == "narration" and not segments:
178
223
  blocking_codes.append("missing_narration")
179
224
  if skipped:
180
225
  blocking_codes.append("skipped_segments")
@@ -197,7 +242,9 @@ def _build_assembly_qc(tts_segments, video_duration, *, output_path=None,
197
242
  )
198
243
  if visual_rollup["blocking"]:
199
244
  blocking_codes.append("visual_qc_failed")
200
- if source_has_audio is False:
245
+ if source_audio_status is not None:
246
+ source_audio = source_audio_status
247
+ elif source_has_audio is False:
201
248
  # Not blocking: assemble can synthesize a silent original track.
202
249
  source_audio = "synthetic_silence"
203
250
  elif source_has_audio is True:
@@ -216,7 +263,6 @@ def _build_assembly_qc(tts_segments, video_duration, *, output_path=None,
216
263
  if output_path.exists() and output["bytes"] <= 0:
217
264
  blocking_codes.append("empty_output")
218
265
 
219
- delivery = render_delivery if render_delivery is not None else {}
220
266
  return {
221
267
  "schema_version": 1,
222
268
  "artifact": ASSEMBLY_QC,
@@ -224,6 +270,11 @@ def _build_assembly_qc(tts_segments, video_duration, *, output_path=None,
224
270
  "blocking": bool(blocking_codes),
225
271
  "blocking_codes": blocking_codes,
226
272
  "duration": round(float(video_duration), 4),
273
+ "audio_mode": audio_mode,
274
+ "audio_operations": audio_operations,
275
+ "adopted_audio": adopted_audio,
276
+ "narration_input_binding": narration_input_binding,
277
+ "audio_mix_binding": audio_mix_binding,
227
278
  "source_audio": source_audio,
228
279
  "loudness_mode": loudness_mode or _loudness_mode(loudnorm_measurement),
229
280
  "loudnorm_measurement": loudnorm_measurement,
@@ -235,10 +286,10 @@ def _build_assembly_qc(tts_segments, video_duration, *, output_path=None,
235
286
  },
236
287
  "visual_qc": visual_rollup,
237
288
  "delivery_qc": {
238
- "video_encode_passes": delivery.get("video_encode_passes"),
239
- "reencode_reason": delivery.get("reencode_reason"),
240
- "audio_sample_rate": delivery.get("audio_sample_rate"),
241
- "final_compat_notes": delivery.get("final_compat_notes", []),
289
+ "video_encode_passes": render_delivery["video_encode_passes"],
290
+ "reencode_reason": render_delivery["reencode_reason"],
291
+ "audio_sample_rate": render_delivery["audio_sample_rate"],
292
+ "final_compat_notes": render_delivery["final_compat_notes"],
242
293
  },
243
294
  "summary": {
244
295
  "segments": len(segments),
@@ -1,31 +1,34 @@
1
- """Render-affecting settings fingerprint for cache/resume decisions."""
1
+ """Render-affecting settings payload recorded in the manifest and compared by resume logic."""
2
2
 
3
3
  from pathlib import Path
4
4
 
5
- from artifacts import _artifact_fingerprint
6
- from assemble_constants import (
7
- SUBTITLE_RENDER_VERSION,
8
- SUBTITLE_TEXT_NORMALIZE_VERSION,
9
- VISUAL_OVERLAYS,
10
- )
5
+ from artifacts import _artifact_identity
6
+ from assemble_constants import VISUAL_OVERLAYS
11
7
  from audio_mix import _loudness_mode, final_loudnorm_filter
12
8
  from lib import CONFIG
13
9
  from source_subtitles import _has_user_subtitles, _source_subtitle_mask_policy
14
- from subtitle_core import _subtitle_style_config
10
+ from subtitles.core import _subtitle_style_config
11
+ from adoption.narration_binding import binding_record
12
+ from adoption.audio_mix_binding import binding_record as audio_mix_binding_record
13
+ from packaging import packaging_settings
15
14
 
16
- def assembly_settings_fingerprint(work_dir=None):
17
- """Settings that affect the rendered video, used by pipeline resume cache. When work_dir is
18
- given, a user_subtitles presence flag is included so dropping in a user-subtitle file rebuilds
19
- the cached subtitles."""
15
+
16
+ def assembly_settings_payload(work_dir=None, *, audio_mode="narration", audio_stream_index=0):
17
+ """Settings that affect the rendered video, as a plain nested dict compared with ``==`` by
18
+ pipeline resume logic. When work_dir is given, a user_subtitles presence flag and the
19
+ ``{size, mtime_ns}`` identity of the overlay/subtitle-track inputs are included so dropping
20
+ in or rewriting one of those files rebuilds the cached subtitles."""
20
21
  burn_subtitles = CONFIG["burn_subtitles"]
21
22
  mask_policy = _source_subtitle_mask_policy(work_dir)
22
23
  mask_source_subtitles = mask_policy["active"]
23
- overlay_fingerprint = (
24
- _artifact_fingerprint(Path(work_dir) / VISUAL_OVERLAYS) if work_dir is not None else None
24
+ overlay_identity = (
25
+ _artifact_identity(Path(work_dir) / VISUAL_OVERLAYS) if work_dir is not None else None
26
+ )
27
+ subtitle_track_identity = (
28
+ _artifact_identity(Path(work_dir) / "subtitle_track.json")
29
+ if work_dir is not None else None
25
30
  )
26
- fingerprint = {
27
- "version": SUBTITLE_RENDER_VERSION,
28
- "subtitle_text_normalize": SUBTITLE_TEXT_NORMALIZE_VERSION,
31
+ settings = {
29
32
  "user_subtitles": _has_user_subtitles(work_dir),
30
33
  "burn_subtitles": burn_subtitles,
31
34
  "force_video_reencode": CONFIG["force_video_reencode"],
@@ -55,19 +58,55 @@ def assembly_settings_fingerprint(work_dir=None):
55
58
  "subtitle_y_bot": CONFIG["subtitle_y_bot"],
56
59
  "visual_overlays": {
57
60
  "artifact": VISUAL_OVERLAYS,
58
- "present": overlay_fingerprint is not None,
59
- "fingerprint": overlay_fingerprint,
61
+ "present": overlay_identity is not None,
62
+ "identity": overlay_identity,
60
63
  },
64
+ "packaging_layers": packaging_settings(work_dir),
61
65
  },
62
- "narration_timing": {
63
- "delay_seconds": CONFIG["narration_delay_seconds"],
64
- "tail_pad_seconds": CONFIG["narration_tail_pad_seconds"],
65
- "fade_ms": CONFIG["fade_ms"],
66
- "narration_speed": CONFIG["narration_speed"],
67
- "narration_cumulative_tempo_max": CONFIG["narration_cumulative_tempo_max"],
68
- "tts_segment_tempo_max": CONFIG["tts_segment_tempo_max"],
66
+ "audio": {
67
+ "mode": audio_mode,
68
+ "selected_stream_index": audio_stream_index,
69
69
  },
70
- "audio_mix": {
70
+ }
71
+ explicit_mix = (
72
+ audio_mix_binding_record(work_dir)
73
+ if work_dir is not None and audio_mode == "narration" else None
74
+ )
75
+ if explicit_mix:
76
+ settings["audio"]["path"] = "explicit_adopted_full_sound"
77
+ settings["audio_mix_binding"] = explicit_mix
78
+ if audio_mode == "narration":
79
+ narration_binding = binding_record(work_dir) if work_dir else None
80
+ settings["narration_input_binding"] = narration_binding
81
+ adopted_tempo = (
82
+ narration_binding.get("tempo_policy") if narration_binding else None
83
+ )
84
+ settings["narration_timing"] = {
85
+ "delay_seconds": 0.0 if explicit_mix else CONFIG["narration_delay_seconds"],
86
+ "tail_pad_seconds": 0.0 if explicit_mix else CONFIG["narration_tail_pad_seconds"],
87
+ "fade_ms": 0 if explicit_mix else CONFIG["fade_ms"],
88
+ "narration_speed": (
89
+ 1.0 if explicit_mix else
90
+ adopted_tempo["global_atempo"] if adopted_tempo else CONFIG["narration_speed"]
91
+ ),
92
+ "tempo_source": (
93
+ "explicit_audio_mix" if explicit_mix else
94
+ "adoption" if adopted_tempo else "configuration"
95
+ ),
96
+ "narration_cumulative_tempo_max": CONFIG["narration_cumulative_tempo_max"],
97
+ "tts_segment_tempo_max": (
98
+ adopted_tempo["segment_tempo_max"]
99
+ if explicit_mix and adopted_tempo else CONFIG["tts_segment_tempo_max"]
100
+ ),
101
+ }
102
+ if explicit_mix and adopted_tempo:
103
+ settings["narration_timing"]["narration_cumulative_tempo_max"] = \
104
+ adopted_tempo["cumulative_tempo_max"]
105
+ settings["narration_timing"]["narration_cumulative_tempo_hard_max"] = \
106
+ adopted_tempo["cumulative_tempo_hard_max"]
107
+ if audio_mode in {"narration", "source-mix"} and not explicit_mix:
108
+ # adopted-packet-copy never decodes or mixes, so mix settings cannot change it.
109
+ settings["audio_mix"] = {
71
110
  "ducking_mode": CONFIG["ducking_mode"],
72
111
  "duck_fade_seconds": CONFIG["duck_fade_seconds"],
73
112
  "duck_bridge_seconds": CONFIG["duck_bridge_seconds"],
@@ -87,9 +126,13 @@ def assembly_settings_fingerprint(work_dir=None):
87
126
  "bgm_path": CONFIG["bgm_path"],
88
127
  "bgm_volume": CONFIG["bgm_volume"],
89
128
  "bgm_ducking_volume": CONFIG["bgm_ducking_volume"],
90
- },
91
- }
129
+ }
92
130
  if burn_subtitles:
93
- fingerprint["subtitle_renderer"] = "ass"
94
- fingerprint["subtitle_style"] = _subtitle_style_config()
95
- return fingerprint
131
+ settings["subtitle_renderer"] = "ass"
132
+ settings["subtitle_style"] = _subtitle_style_config()
133
+ if subtitle_track_identity is not None:
134
+ settings["subtitle_track"] = {
135
+ "artifact": "subtitle_track.json",
136
+ "identity": subtitle_track_identity,
137
+ }
138
+ return settings
@@ -4,13 +4,13 @@ import json
4
4
  import re
5
5
  from pathlib import Path
6
6
 
7
- from artifacts import _load_work_json, _value_fingerprint
7
+ from artifacts import _load_work_json
8
8
  from audio_automation import (
9
9
  coalesce_duck_windows,
10
10
  ducking_expression,
11
11
  release_ducking_expression,
12
12
  )
13
- from lib import CONFIG, log, run_cmd
13
+ from lib import CONFIG, filter_file_args, log, run_cmd
14
14
 
15
15
  def _limiter_filter():
16
16
  return f"alimiter=limit={CONFIG['final_limiter_peak']:.2f}:level=false"
@@ -92,7 +92,7 @@ def _run_loudnorm_first_pass(input_video, narration_wav, original_audio_input,
92
92
  "-i", str(narration_wav),
93
93
  *original_audio_input,
94
94
  *bgm_input,
95
- "-filter_complex_script", str(probe_script),
95
+ *filter_file_args("filter_complex", probe_script),
96
96
  "-map", "[lnprobe]",
97
97
  "-f", "null", "-",
98
98
  ]
@@ -126,13 +126,13 @@ def _load_sentence_handoff_anchors(work_dir):
126
126
  if payload is None:
127
127
  return [], None, {"require_measured": cut_mode}
128
128
  if cut_mode:
129
- # Output-clock anchors are only trusted when they were derived from the current cut plan.
130
- plan = _load_work_json(work_dir, "clip_plan_validated.json")
129
+ # Output-clock anchors are only trusted when they are at least as new as the cut plan.
130
+ plan_path = work_dir / "clip_plan_validated.json"
131
131
  fresh = (
132
132
  payload.get("schema_version") == 2
133
133
  and payload.get("timeline") == "cut_output"
134
- and plan is not None
135
- and payload.get("clip_plan_fingerprint") == _value_fingerprint(plan)
134
+ and plan_path.exists()
135
+ and (work_dir / artifact).stat().st_mtime_ns >= plan_path.stat().st_mtime_ns
136
136
  )
137
137
  if not fresh:
138
138
  return [], None, {"require_measured": True}
@@ -172,7 +172,7 @@ def _handoff_speech_evidence(work_dir, payload):
172
172
  speech = _timed_rows(_asr_segments(work_dir))
173
173
  if not quiet:
174
174
  silence = _load_work_json(work_dir, "silence_periods.json") or []
175
- quiet = _timed_rows(row for row in silence if not row.get("has_speech", False))
175
+ quiet = _timed_rows(row for row in silence if not row["has_speech"])
176
176
  return speech, quiet
177
177
 
178
178
 
@@ -278,7 +278,7 @@ def _apply_source_sentence_handoffs(tts_segments, work_dir, video_duration):
278
278
  speech_spans,
279
279
  quiet_windows,
280
280
  anchors,
281
- seg.get("overlaps_speech", True),
281
+ seg["overlaps_speech"],
282
282
  require_measured=require_measured,
283
283
  )
284
284
  seg["overlaps_speech"] = measured
@@ -374,7 +374,7 @@ def _duck_envelope(tts_segments, idle, speech_vol, quiet_vol, fade, bridge):
374
374
  continue
375
375
  hold_end = max(narration_end, seg.get("source_duck_end", narration_end))
376
376
  restore_at = max(hold_end, seg.get("source_restore_at", hold_end + fade))
377
- level = speech_vol if seg.get("overlaps_speech", True) else quiet_vol
377
+ level = speech_vol if seg["overlaps_speech"] else quiet_vol
378
378
  windows.append((start, hold_end, level, restore_at))
379
379
  return release_ducking_expression(windows, idle, fade, bridge=bridge)
380
380
 
@@ -455,7 +455,7 @@ def _build_audio_filter_complex(
455
455
  quiet_vol = CONFIG["zone_ducking_volume"]
456
456
  expr = _duck_envelope(tts_segments, idle, speech_vol, quiet_vol, fade, bridge)
457
457
  if expr:
458
- n_overlap = sum(1 for s in tts_segments if s.get("overlaps_speech", True))
458
+ n_overlap = sum(1 for s in tts_segments if s["overlaps_speech"])
459
459
  n_quiet = len(tts_segments) - n_overlap
460
460
  log(f"gap-fill ducking: 间隙原声={idle}, 对白段={speech_vol}({n_overlap}), 安静段={quiet_vol}({n_quiet}), 桥接间隙<{bridge}s")
461
461
  orig = f"{original_in}volume='{expr}':eval=frame,aresample=48000[orig];"