@oh-story/dsh 0.1.12 → 0.1.13

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 (264) hide show
  1. package/README.md +17 -11
  2. package/lib/client.js +85 -35
  3. package/lib/client.js.map +3 -3
  4. package/lib/drama/manifest.json +41 -36
  5. package/lib/drama/skills/short-drama/scripts/creator_markdown_check.py +340 -13
  6. package/lib/drama/skills/short-drama-assets/references/voice-direction.md +4 -2
  7. package/lib/drama/skills/short-drama-edit/SKILL.md +32 -12
  8. package/lib/drama/skills/short-drama-edit/references/still-cuts.md +90 -0
  9. package/lib/drama/skills/short-drama-edit/scripts/edit_tool.py +1035 -145
  10. package/lib/drama/skills/short-drama-edit/scripts/selftest.py +48 -0
  11. package/lib/drama/skills/short-drama-produce/SKILL.md +6 -1
  12. package/lib/drama/skills/short-drama-produce/references/adapter-contract.md +12 -0
  13. package/lib/drama/skills/short-drama-produce/scripts/production_tool.py +138 -9
  14. package/lib/drama/skills/short-drama-produce/scripts/provider_adapters.py +6 -0
  15. package/lib/drama/skills/short-drama-storyboard/SKILL.md +2 -0
  16. package/lib/drama/skills/short-drama-storyboard/references/production-shot-grammar.md +3 -1
  17. package/lib/drama/skills/short-drama-video-prompts/SKILL.md +7 -0
  18. package/lib/drama/skills/short-drama-video-prompts/references/performance-action-timing.md +14 -0
  19. package/lib/drama/skills/short-drama-video-prompts/references/seedance-2.5.md +19 -6
  20. package/lib/drama/skills/short-drama-video-prompts/references/stage-contract.md +1 -0
  21. package/lib/drama/skills/short-drama-video-prompts/references/target-model-profile.md +5 -0
  22. package/lib/drama/skills/short-drama-video-prompts/references/wan-3.0.md +4 -1
  23. package/lib/index.js +16 -7
  24. package/lib/index.js.map +2 -2
  25. package/lib/novel-to-game/README.md +98 -44
  26. package/lib/novel-to-game/README_ZH.md +100 -46
  27. package/lib/novel-to-game/VERSION +1 -1
  28. package/lib/novel-to-game/examples/jin-ping-mei/build/BUILD_BRIEF.md +27 -202
  29. package/lib/novel-to-game/examples/jin-ping-mei/build/app/RUN.md +23 -115
  30. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/bg/chamber.webp +0 -0
  31. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/bg/court_night.webp +0 -0
  32. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/bg/court_rain.webp +0 -0
  33. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/bg/garden.webp +0 -0
  34. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/bg/hall.webp +0 -0
  35. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/bg/kitchen.webp +0 -0
  36. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/bg/street.webp +0 -0
  37. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/bg/street_dawn.webp +0 -0
  38. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/bg/temple.webp +0 -0
  39. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/yue/moon.webp +0 -0
  40. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/meng/angry.webp +0 -0
  41. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/meng/blush.webp +0 -0
  42. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/meng/neutral.webp +0 -0
  43. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/meng/sad.webp +0 -0
  44. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/meng/smile.webp +0 -0
  45. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/meng/surprised.webp +0 -0
  46. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/pan/angry.webp +0 -0
  47. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/pan/blush.webp +0 -0
  48. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/pan/neutral.webp +0 -0
  49. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/pan/sad.webp +0 -0
  50. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/pan/smile.webp +0 -0
  51. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/pan/surprised.webp +0 -0
  52. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/pinger/angry.webp +0 -0
  53. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/pinger/blush.webp +0 -0
  54. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/pinger/neutral.webp +0 -0
  55. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/pinger/sad.webp +0 -0
  56. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/pinger/smile.webp +0 -0
  57. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/pinger/surprised.webp +0 -0
  58. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/xuee/angry.webp +0 -0
  59. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/xuee/blush.webp +0 -0
  60. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/xuee/neutral.webp +0 -0
  61. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/xuee/sad.webp +0 -0
  62. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/xuee/smile.webp +0 -0
  63. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/xuee/surprised.webp +0 -0
  64. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/yue/angry.webp +0 -0
  65. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/yue/blush.webp +0 -0
  66. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/yue/neutral.webp +0 -0
  67. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/yue/sad.webp +0 -0
  68. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/yue/smile.webp +0 -0
  69. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/yue/surprised.webp +0 -0
  70. package/lib/novel-to-game/examples/jin-ping-mei/build/app/css/style.css +326 -2503
  71. package/lib/novel-to-game/examples/jin-ping-mei/build/app/index.html +71 -4
  72. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/adv.js +221 -0
  73. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/audio.js +113 -257
  74. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/main.js +511 -3026
  75. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/menus.js +277 -0
  76. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/petals.js +67 -0
  77. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/stage.js +180 -0
  78. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/store.js +70 -0
  79. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/story/common.js +198 -0
  80. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/story/common2.js +204 -0
  81. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/story/common3.js +198 -0
  82. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/story/dates.js +186 -0
  83. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/story/index.js +122 -0
  84. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/story/meng.js +190 -0
  85. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/story/pan.js +212 -0
  86. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/story/pinger.js +198 -0
  87. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/story/xuee.js +199 -0
  88. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/story/yue.js +214 -0
  89. package/lib/novel-to-game/examples/jin-ping-mei/build/app/package.json +2 -2
  90. package/lib/novel-to-game/examples/jin-ping-mei/build/art/generated-art.json +182 -45
  91. package/lib/novel-to-game/examples/jin-ping-mei/qa/README.md +7 -17
  92. package/lib/novel-to-game/examples/jin-ping-mei/qa/verification.json +9 -9
  93. package/lib/novel-to-game/manifest.json +303 -78
  94. package/lib/novel-to-game/skills/game-art-direction/references/art-direction-method.md +7 -2
  95. package/lib/novel-to-game/skills/game-concept/references/concept-method.md +2 -0
  96. package/lib/novel-to-game/skills/game-world-design/references/narrative-design-method.md +2 -0
  97. package/lib/novel-to-game/skills/game-world-design/references/world-design-method.md +9 -0
  98. package/lib/types/client/drama-production-view.d.ts +11 -0
  99. package/lib/types/client/drama-production-view.d.ts.map +1 -1
  100. package/lib/types/client/file-activity.d.ts +1 -1
  101. package/lib/types/client/file-activity.d.ts.map +1 -1
  102. package/lib/types/client/production-prompts.d.ts +7 -1
  103. package/lib/types/client/production-prompts.d.ts.map +1 -1
  104. package/lib/types/client/production-runtime.d.ts +7 -2
  105. package/lib/types/client/production-runtime.d.ts.map +1 -1
  106. package/lib/types/drama-adapters.d.ts.map +1 -1
  107. package/lib/types/skill-provider.d.ts.map +1 -1
  108. package/lib/types/video-project.d.ts.map +1 -1
  109. package/lib/video-recap/CHANGELOG.md +108 -1
  110. package/lib/video-recap/README.en.md +18 -14
  111. package/lib/video-recap/README.md +18 -14
  112. package/lib/video-recap/manifest.json +297 -321
  113. package/lib/video-recap/skills/video-assemble/SKILL.md +18 -16
  114. package/lib/video-recap/skills/video-assemble/references/explicit-audio-mix.md +3 -1
  115. package/lib/video-recap/skills/video-assemble/references/jianying/SOURCE.md +5 -5
  116. package/lib/video-recap/skills/video-assemble/references/packaging.md +4 -4
  117. package/lib/video-recap/skills/video-assemble/references/subtitle-track.md +10 -10
  118. package/lib/video-recap/skills/video-assemble/scripts/adoption/audio_mix_binding.py +13 -2
  119. package/lib/video-recap/skills/video-assemble/scripts/adoption/av_clock.py +109 -0
  120. package/lib/video-recap/skills/video-assemble/scripts/adoption/strict_publish.py +4 -1
  121. package/lib/video-recap/skills/video-assemble/scripts/artifacts.py +2 -9
  122. package/lib/video-recap/skills/video-assemble/scripts/assemble.py +188 -51
  123. package/lib/video-recap/skills/video-assemble/scripts/assemble_constants.py +0 -1
  124. package/lib/video-recap/skills/video-assemble/scripts/assembly_contract.py +32 -75
  125. package/lib/video-recap/skills/video-assemble/scripts/assembly_settings.py +8 -15
  126. package/lib/video-recap/skills/video-assemble/scripts/audio_mix.py +202 -161
  127. package/lib/video-recap/skills/video-assemble/scripts/codec_peak.py +112 -0
  128. package/lib/video-recap/skills/video-assemble/scripts/export_jianying.py +10 -101
  129. package/lib/video-recap/skills/video-assemble/scripts/jianying/builders.py +44 -475
  130. package/lib/video-recap/skills/video-assemble/scripts/jianying/model.py +1 -22
  131. package/lib/video-recap/skills/video-assemble/scripts/jianying/templates.py +0 -3
  132. package/lib/video-recap/skills/video-assemble/scripts/jianying/timeline_contract.py +36 -103
  133. package/lib/video-recap/skills/video-assemble/scripts/jianying/tracks.py +0 -8
  134. package/lib/video-recap/skills/video-assemble/scripts/jianying/writer.py +28 -203
  135. package/lib/video-recap/skills/video-assemble/scripts/lib.py +20 -34
  136. package/lib/video-recap/skills/video-assemble/scripts/loudness.py +332 -0
  137. package/lib/video-recap/skills/video-assemble/scripts/media.py +158 -38
  138. package/lib/video-recap/skills/video-assemble/scripts/narration_audio.py +30 -16
  139. package/lib/video-recap/skills/video-assemble/scripts/render_preflight.py +49 -8
  140. package/lib/video-recap/skills/video-assemble/scripts/source_subtitles.py +17 -6
  141. package/lib/video-recap/skills/video-assemble/scripts/subtitles/core.py +24 -2
  142. package/lib/video-recap/skills/video-assemble/scripts/subtitles/track.py +2 -5
  143. package/lib/video-recap/skills/video-assemble/scripts/subtitles/track_binding.py +2 -9
  144. package/lib/video-recap/skills/video-assemble/scripts/timeline.py +12 -34
  145. package/lib/video-recap/skills/video-assemble/scripts/timeline_emit.py +3 -4
  146. package/lib/video-recap/skills/video-assemble/scripts/visual_render.py +28 -2
  147. package/lib/video-recap/skills/video-cut/SKILL.md +37 -11
  148. package/lib/video-recap/skills/video-cut/references/shot-review.md +1 -1
  149. package/lib/video-recap/skills/video-cut/scripts/cut.py +2 -34
  150. package/lib/video-recap/skills/video-cut/scripts/cut_cli.py +96 -124
  151. package/lib/video-recap/skills/video-cut/scripts/cut_contract.py +97 -182
  152. package/lib/video-recap/skills/video-cut/scripts/cut_render.py +177 -166
  153. package/lib/video-recap/skills/video-cut/scripts/frame_grid.py +249 -0
  154. package/lib/video-recap/skills/video-cut/scripts/lib.py +0 -3
  155. package/lib/video-recap/skills/video-cut/scripts/media_geometry.py +173 -7
  156. package/lib/video-recap/skills/video-cut/scripts/sentence_boundaries.py +234 -151
  157. package/lib/video-recap/skills/video-cut/scripts/sentence_gate.py +189 -0
  158. package/lib/video-recap/skills/video-cut/scripts/shot_review.py +2 -6
  159. package/lib/video-recap/skills/video-recap/SKILL.md +24 -27
  160. package/lib/video-recap/skills/video-recap/references/audio-routing.md +8 -6
  161. package/lib/video-recap/skills/video-recap/references/config-playbook.md +8 -11
  162. package/lib/video-recap/skills/video-recap/references/data-schema.md +131 -74
  163. package/lib/video-recap/skills/video-recap/references/resource-library.md +17 -1
  164. package/lib/video-recap/skills/video-recap/scripts/doctor.py +51 -268
  165. package/lib/video-recap/skills/video-recap/scripts/final_qc.py +89 -254
  166. package/lib/video-recap/skills/video-recap/scripts/lib.py +81 -70
  167. package/lib/video-recap/skills/video-recap/scripts/library.py +40 -4
  168. package/lib/video-recap/skills/video-recap/scripts/materials.py +13 -80
  169. package/lib/video-recap/skills/video-recap/scripts/recap_cli.py +49 -17
  170. package/lib/video-recap/skills/video-recap/scripts/recap_inspect.py +1 -3
  171. package/lib/video-recap/skills/video-recap/scripts/recap_runner.py +86 -116
  172. package/lib/video-recap/skills/video-recap/scripts/recap_runtime.py +87 -23
  173. package/lib/video-recap/skills/video-recap/scripts/recap_source.py +0 -39
  174. package/lib/video-recap/skills/video-recap/scripts/recap_stage_qc.py +37 -135
  175. package/lib/video-recap/skills/video-recap/scripts/recap_timeline.py +118 -127
  176. package/lib/video-recap/skills/video-recap/scripts/resources/__init__.py +4 -0
  177. package/lib/video-recap/skills/video-recap/scripts/{resource_lock.py → resources/lock.py} +1 -1
  178. package/lib/video-recap/skills/video-recap/scripts/{project_binding.py → resources/project_binding.py} +78 -17
  179. package/lib/video-recap/skills/video-reference/SKILL.md +108 -0
  180. package/lib/video-recap/skills/video-reference/references/reference-schema.md +139 -0
  181. package/lib/video-recap/skills/video-reference/scripts/lib.py +85 -0
  182. package/lib/video-recap/skills/video-reference/scripts/reference.py +96 -0
  183. package/lib/video-recap/skills/video-reference/scripts/reference_check.py +553 -0
  184. package/lib/video-recap/skills/video-reference/scripts/reference_frames.py +122 -0
  185. package/lib/video-recap/skills/video-reference/scripts/reference_measure.py +253 -0
  186. package/lib/video-recap/skills/video-reference/scripts/reference_profile.py +271 -0
  187. package/lib/video-recap/skills/video-script/SKILL.md +9 -7
  188. package/lib/video-recap/skills/video-script/scripts/agent_text.py +18 -267
  189. package/lib/video-recap/skills/video-script/scripts/deslop_qc.py +12 -30
  190. package/lib/video-recap/skills/video-script/scripts/entry_suggestion.py +125 -0
  191. package/lib/video-recap/skills/video-script/scripts/evidence_bundle.py +2 -26
  192. package/lib/video-recap/skills/video-script/scripts/lib.py +5 -15
  193. package/lib/video-recap/skills/video-script/scripts/lint_summary.py +184 -0
  194. package/lib/video-recap/skills/video-script/scripts/narration_lint.py +120 -100
  195. package/lib/video-recap/skills/video-script/scripts/review.py +3 -34
  196. package/lib/video-recap/skills/video-script/scripts/review_grounding.py +20 -46
  197. package/lib/video-recap/skills/video-script/scripts/review_response.py +120 -258
  198. package/lib/video-recap/skills/video-script/scripts/review_runner.py +2 -10
  199. package/lib/video-recap/skills/video-script/scripts/speech_ownership.py +89 -5
  200. package/lib/video-recap/skills/video-script/scripts/validate.py +26 -34
  201. package/lib/video-recap/skills/video-understanding/SKILL.md +29 -4
  202. package/lib/video-recap/skills/video-understanding/references/data-schema.md +5 -22
  203. package/lib/video-recap/skills/video-understanding/scripts/agent_text.py +24 -121
  204. package/lib/video-recap/skills/video-understanding/scripts/asr.py +11 -5
  205. package/lib/video-recap/skills/video-understanding/scripts/asr_timing_evidence.py +15 -24
  206. package/lib/video-recap/skills/video-understanding/scripts/briefing/builder.py +59 -25
  207. package/lib/video-recap/skills/video-understanding/scripts/briefing/context.py +39 -3
  208. package/lib/video-recap/skills/video-understanding/scripts/briefing/inputs.py +15 -27
  209. package/lib/video-recap/skills/video-understanding/scripts/briefing/timeline.py +136 -37
  210. package/lib/video-recap/skills/video-understanding/scripts/consolidate.py +121 -37
  211. package/lib/video-recap/skills/video-understanding/scripts/detect.py +118 -16
  212. package/lib/video-recap/skills/video-understanding/scripts/index_normalize.py +359 -0
  213. package/lib/video-recap/skills/video-understanding/scripts/lib.py +62 -21
  214. package/lib/video-recap/skills/video-understanding/scripts/storyboard.py +181 -51
  215. package/lib/video-recap/skills/video-understanding/scripts/timeline_fusion.py +0 -26
  216. package/lib/video-recap/skills/video-understanding/scripts/understanding_brief.py +37 -14
  217. package/lib/video-recap/skills/video-understanding/scripts/understanding_cache.py +40 -32
  218. package/lib/video-recap/skills/video-understanding/scripts/understanding_runner.py +100 -127
  219. package/lib/video-recap/skills/video-understanding/scripts/understanding_storyboard.py +182 -30
  220. package/lib/video-recap/skills/video-understanding/scripts/vlm.py +30 -27
  221. package/lib/video-recap/skills/video-voiceover/SKILL.md +19 -12
  222. package/lib/video-recap/skills/video-voiceover/references/index-tts.md +3 -3
  223. package/lib/video-recap/skills/video-voiceover/scripts/approved_text_policy.py +3 -1
  224. package/lib/video-recap/skills/video-voiceover/scripts/dub.py +60 -135
  225. package/lib/video-recap/skills/video-voiceover/scripts/lib.py +9 -34
  226. package/lib/video-recap/skills/video-voiceover/scripts/providers/fish_audio.py +6 -1
  227. package/lib/video-recap/skills/video-voiceover/scripts/tts_audio.py +64 -1
  228. package/lib/video-recap/skills/video-voiceover/scripts/tts_cache.py +99 -0
  229. package/lib/video-recap/skills/video-voiceover/scripts/voiceover.py +177 -200
  230. package/package.json +45 -45
  231. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/group/inner_court_accord_five.webp +0 -0
  232. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/group/public_day15.webp +0 -0
  233. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/household/li_jiaoer.webp +0 -0
  234. package/lib/novel-to-game/examples/jin-ping-mei/build/app/css/new-guofeng.css +0 -1494
  235. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/assets.js +0 -147
  236. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/data.js +0 -7470
  237. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/engine.js +0 -13066
  238. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/text.js +0 -32
  239. package/lib/novel-to-game/examples/jin-ping-mei/qa/playable-model.json +0 -99
  240. package/lib/novel-to-game/examples/jin-ping-mei/qa/verify_playable_model.mjs +0 -152
  241. package/lib/video-recap/skills/video-assemble/references/foreground-compose.md +0 -118
  242. package/lib/video-recap/skills/video-assemble/references/jianying/empty_jy_combination_segment.json +0 -84
  243. package/lib/video-recap/skills/video-assemble/references/jianying/empty_jy_combination_video_material.json +0 -98
  244. package/lib/video-recap/skills/video-assemble/references/jianying/empty_jy_draft.json +0 -204
  245. package/lib/video-recap/skills/video-assemble/references/pair-media.md +0 -74
  246. package/lib/video-recap/skills/video-assemble/scripts/compose_foreground.py +0 -293
  247. package/lib/video-recap/skills/video-assemble/scripts/pair_media.py +0 -216
  248. package/lib/video-recap/skills/video-recap/references/shift-left-qc-schema.md +0 -23
  249. package/lib/video-recap/skills/video-recap/scripts/mimo_qc.py +0 -33
  250. package/lib/video-recap/skills/video-recap/scripts/qc/__init__.py +0 -1
  251. package/lib/video-recap/skills/video-recap/scripts/qc/mimo_client.py +0 -13
  252. package/lib/video-recap/skills/video-recap/scripts/qc/mimo_contract.py +0 -9
  253. package/lib/video-recap/skills/video-recap/scripts/qc/mimo_evidence.py +0 -287
  254. package/lib/video-recap/skills/video-recap/scripts/qc/mimo_observations.py +0 -175
  255. package/lib/video-recap/skills/video-recap/scripts/qc/mimo_payload.py +0 -206
  256. package/lib/video-recap/skills/video-recap/scripts/qc/mimo_report.py +0 -318
  257. package/lib/video-recap/skills/video-recap/scripts/qc/mimo_runner.py +0 -130
  258. package/lib/video-recap/skills/video-recap/scripts/qc_contract.py +0 -320
  259. package/lib/video-recap/skills/video-script/scripts/timeline_fusion.py +0 -108
  260. package/lib/video-recap/skills/video-understanding/scripts/brief.py +0 -9
  261. package/lib/video-recap/skills/video-understanding/scripts/deslop_qc.py +0 -211
  262. package/lib/video-recap/skills/video-understanding/scripts/narration_lint.py +0 -707
  263. package/lib/video-recap/skills/video-understanding/scripts/speech_ownership.py +0 -143
  264. /package/lib/video-recap/skills/video-cut/scripts/{narration_mapping.py → cut_qc.py} +0 -0
@@ -10,57 +10,25 @@ import argparse
10
10
  import json
11
11
  import os
12
12
  import shutil
13
- import subprocess
14
13
  import sys
15
14
  import urllib.parse
16
15
  from pathlib import Path
17
16
 
18
- from lib import CONFIG
17
+ from lib import CONFIG, TTS_PROVIDERS, env_bool, ffmpeg_filters
19
18
 
20
19
 
21
20
  SCRIPT_DIR = Path(__file__).resolve().parent
22
- DEGRADED_GROUP = "warnings/degraded"
23
- TTS_PROVIDERS = ("auto", "mimo-tts", "fish-audio", "index-tts")
21
+ # index-tts is checked as hard failures below; these providers only need a key.
22
+ TTS_KEY_HINTS = {
23
+ "mimo-tts": "MIMO_TTS_API_KEY or MIMO_API_KEY",
24
+ "fish-audio": "FISH_API_KEY",
25
+ }
24
26
 
25
27
 
26
28
  def _command_path(name: str) -> str | None:
27
29
  return shutil.which(name)
28
30
 
29
31
 
30
- def _ffmpeg_filters() -> set[str]:
31
- """Filters the installed ffmpeg lists; empty when ffmpeg is absent.
32
-
33
- A present ffmpeg whose `-filters` fails or hangs is an environment fault and raises,
34
- so it is never misreported downstream as "filter absent"."""
35
- ffmpeg = _command_path("ffmpeg")
36
- if not ffmpeg:
37
- return set()
38
- try:
39
- result = subprocess.run(
40
- [ffmpeg, "-hide_banner", "-filters"], text=True, capture_output=True, timeout=20
41
- )
42
- except (OSError, subprocess.SubprocessError) as exc:
43
- raise RuntimeError(f"`ffmpeg -filters` failed or hung: {exc}") from exc
44
- if result.returncode != 0:
45
- detail = (result.stderr or result.stdout or "").strip()[:300]
46
- raise RuntimeError(f"`ffmpeg -filters` failed (exit {result.returncode}): {detail}")
47
- filters = set()
48
- for line in result.stdout.splitlines():
49
- parts = line.split()
50
- if len(parts) >= 2 and parts[0] and parts[0][0] in ".TSCAPN|":
51
- filters.add(parts[1])
52
- return filters
53
-
54
-
55
- def ffmpeg_has_subtitles_filter() -> bool:
56
- """True when this ffmpeg can burn subtitles — its filter list includes the libass
57
- `subtitles` filter. The render burns even the .ass file through `subtitles=` (see
58
- video-assemble assemble.py:_subtitle_burn_filter), so this — not the `ass` filter — is
59
- the exact capability `--burn-subtitles` needs. Reused by the orchestrator preflight
60
- (recap.py) to fail fast before any API spend."""
61
- return "subtitles" in _ffmpeg_filters()
62
-
63
-
64
32
  def _asr_status() -> dict[str, object]:
65
33
  configured = bool(CONFIG["mimo_asr_api_key"])
66
34
  return {
@@ -108,218 +76,8 @@ def _index_tts_status() -> dict[str, object]:
108
76
  }
109
77
 
110
78
 
111
- def _capability(name: str, summary: str, *, detail: str = "", action: str = "") -> dict[str, str]:
112
- item = {"name": name, "summary": summary}
113
- if detail:
114
- item["detail"] = detail
115
- if action:
116
- item["action"] = action
117
- return item
118
-
119
-
120
- def _build_capability_menu(checks: dict) -> dict[str, list[dict[str, str]]]:
121
- """Human-ready preflight summary grouped by what can run, what blocks, and what degrades.
122
-
123
- This is intentionally a small rollup over the existing `checks` tree. It does not replace
124
- the raw machine checks, install anything, or introduce provider ranking.
125
- """
126
- system = checks["system_tools"]
127
- api = checks["api_config"]
128
- asr = checks["asr"]
129
- tts = checks["tts"]
130
-
131
- menu: dict[str, list[dict[str, str]]] = {
132
- "ready": [],
133
- "blocked": [],
134
- DEGRADED_GROUP: [],
135
- "optional_upgrades": [],
136
- }
137
-
138
- ffmpeg_ready = system["ffmpeg"]
139
- ffprobe_ready = system["ffprobe"]
140
- subtitles_ready = system["burn_subtitles_ready"]
141
- api_key_set = api["api_key_set"]
142
- asr_ready = asr["available"]
143
- tts_ready = tts["available"]
144
- vlm_ready = api["mimo_video_configured"]
145
- normal_core_ready = ffmpeg_ready and ffprobe_ready and api_key_set and vlm_ready and tts_ready
146
-
147
- if ffmpeg_ready and ffprobe_ready:
148
- menu["ready"].append(
149
- _capability(
150
- "core_media_tools",
151
- "ffmpeg and ffprobe are available",
152
- detail="Local probing, cutting, rendering, and duration checks can run.",
153
- )
154
- )
155
- else:
156
- if not ffmpeg_ready:
157
- menu["blocked"].append(
158
- _capability("ffmpeg", "Missing ffmpeg", action="Install ffmpeg before running the recap pipeline.")
159
- )
160
- if not ffprobe_ready:
161
- menu["blocked"].append(
162
- _capability("ffprobe", "Missing ffprobe", action="Install ffprobe before running media probing/export.")
163
- )
164
-
165
- if api_key_set:
166
- menu["ready"].append(
167
- _capability(
168
- "mimo_credentials",
169
- "MiMo API key is configured",
170
- detail=f"Source: {api['api_env_var']}",
171
- )
172
- )
173
- else:
174
- menu["blocked"].append(
175
- _capability(
176
- "mimo_credentials",
177
- "Missing MIMO_API_KEY",
178
- action="Set MIMO_API_KEY; the default ASR / VLM / TTS path depends on it.",
179
- )
180
- )
181
-
182
- if vlm_ready:
183
- menu["ready"].append(
184
- _capability(
185
- "mimo_vlm",
186
- "MiMo VLM/video understanding is configured",
187
- detail=f"Model: {api['vlm_model']}",
188
- )
189
- )
190
- elif api_key_set:
191
- menu["blocked"].append(
192
- _capability(
193
- "mimo_vlm",
194
- "MiMo VLM/video understanding is not configured",
195
- action="Set MIMO_VIDEO_API_KEY or the shared MIMO_API_KEY before video understanding.",
196
- )
197
- )
198
-
199
- tts_provider = tts["provider"]
200
- if tts_provider == "index-tts":
201
- capability_name = "index_tts_configuration"
202
- action = "Set valid INDEX_TTS_ENDPOINT and INDEX_TTS_VOICE values before voiceover."
203
- elif tts_provider == "fish-audio":
204
- capability_name = "fish_audio_tts"
205
- action = "Set FISH_API_KEY before voiceover."
206
- else:
207
- capability_name = "mimo_tts"
208
- action = "Set MIMO_TTS_API_KEY or the shared MIMO_API_KEY before voiceover."
209
- if tts_ready:
210
- menu["ready"].append(
211
- _capability(
212
- capability_name,
213
- (
214
- "index-tts configuration is present"
215
- if tts_provider == "index-tts"
216
- else f"{tts_provider} is configured"
217
- ),
218
- detail=(
219
- tts["validation_scope"]
220
- if tts_provider == "index-tts"
221
- else f"Model: {tts['model']}"
222
- ),
223
- )
224
- )
225
- elif api_key_set:
226
- menu["blocked"].append(
227
- _capability(
228
- capability_name,
229
- f"{tts_provider} is not configured",
230
- action=action,
231
- )
232
- )
233
-
234
- if asr_ready:
235
- menu["ready"].append(
236
- _capability(
237
- "mimo_asr",
238
- "MiMo ASR is configured",
239
- detail=f"Language: {asr['mimo_asr_language']}; model: {asr['mimo_asr_model']}",
240
- )
241
- )
242
- else:
243
- menu[DEGRADED_GROUP].append(
244
- _capability(
245
- "mimo_asr",
246
- "ASR is unavailable; run only with --skip-asr",
247
- action=asr["note"],
248
- )
249
- )
250
-
251
- if subtitles_ready:
252
- menu["ready"].append(
253
- _capability("subtitle_burn", "Subtitle burn-in is available", detail="ffmpeg has the subtitles/libass filter.")
254
- )
255
- elif ffmpeg_ready:
256
- menu[DEGRADED_GROUP].append(
257
- _capability(
258
- "subtitle_burn",
259
- "Subtitle burn-in is unavailable",
260
- action="Use --no-burn-subtitles or install an ffmpeg build with the subtitles/libass filter.",
261
- )
262
- )
263
-
264
- if not normal_core_ready:
265
- menu["blocked"].append(
266
- _capability(
267
- "default_recap_pipeline",
268
- "Default recap run is blocked",
269
- detail="Resolve the blocking items above before a normal run.",
270
- )
271
- )
272
- elif asr_ready and subtitles_ready:
273
- menu["ready"].append(
274
- _capability(
275
- "default_recap_pipeline",
276
- (
277
- "Recap prerequisites are configured"
278
- if tts_provider == "index-tts"
279
- else "Default recap run is ready"
280
- ),
281
- detail=(
282
- "ASR, VLM, and media tools are configured; Index TTS passed offline configuration checks only."
283
- if tts_provider == "index-tts"
284
- else "ASR, VLM, TTS, and media tools are configured."
285
- ),
286
- )
287
- )
288
- else:
289
- actions = []
290
- if not asr_ready:
291
- actions.append("run with --skip-asr")
292
- if not subtitles_ready:
293
- actions.append("run with --no-burn-subtitles")
294
- menu[DEGRADED_GROUP].append(
295
- _capability(
296
- "recap_degraded_mode",
297
- "Recap can run only in an explicit degraded mode",
298
- detail="; ".join(actions),
299
- )
300
- )
301
-
302
- menu["optional_upgrades"].append(
303
- _capability(
304
- "jianying_export",
305
- "Editable JianYing draft export can be requested with --export-jianying",
306
- detail="No JianYing install is required to write the draft; ffprobe improves media metadata.",
307
- )
308
- )
309
- if subtitles_ready:
310
- menu["optional_upgrades"].append(
311
- _capability(
312
- "burned_subtitles",
313
- "Burned subtitles are available and enabled by default",
314
- action="Use --no-burn-subtitles if you prefer external subtitle files.",
315
- )
316
- )
317
-
318
- return menu
319
-
320
-
321
79
  def build_report(*, tts_provider: str | None = None) -> dict[str, object]:
322
- filters = _ffmpeg_filters()
80
+ filters = ffmpeg_filters()
323
81
  ffmpeg_path = _command_path("ffmpeg") or ""
324
82
  ffprobe_path = _command_path("ffprobe") or ""
325
83
  mimo_video_configured = bool(CONFIG["mimo_video_api_key"])
@@ -345,6 +103,15 @@ def build_report(*, tts_provider: str | None = None) -> dict[str, object]:
345
103
  else:
346
104
  tts_model = CONFIG["mimo_tts_model"]
347
105
  subtitle_filter = "subtitles" in filters
106
+ drawtext_filter = "drawtext" in filters
107
+ burn_explicit = "BURN_SUBTITLES" in os.environ and env_bool("BURN_SUBTITLES", True)
108
+ if not ffmpeg_path:
109
+ subtitle_delivery = "unavailable"
110
+ elif subtitle_filter:
111
+ subtitle_delivery = "burned"
112
+ else:
113
+ # The default burn degrades to the .srt sidecar; an explicit request fails fast.
114
+ subtitle_delivery = "fails_explicit_burn" if burn_explicit else "sidecar_srt"
348
115
  checks = {
349
116
  "system_tools": {
350
117
  "ffmpeg": bool(ffmpeg_path),
@@ -354,6 +121,9 @@ def build_report(*, tts_provider: str | None = None) -> dict[str, object]:
354
121
  "ffmpeg_subtitles_filter": subtitle_filter,
355
122
  "ffmpeg_ass_filter": "ass" in filters,
356
123
  "burn_subtitles_ready": bool(ffmpeg_path and subtitle_filter),
124
+ "subtitle_delivery": subtitle_delivery,
125
+ "ffmpeg_drawtext_filter": drawtext_filter,
126
+ "visual_overlays_ready": bool(ffmpeg_path and drawtext_filter),
357
127
  },
358
128
  "tts": {
359
129
  "provider": effective_tts_provider,
@@ -414,17 +184,40 @@ def build_report(*, tts_provider: str | None = None) -> dict[str, object]:
414
184
  )
415
185
  if not index_tts["index_tts_voice_set"]:
416
186
  failures.append("INDEX_TTS_VOICE is not set")
417
- if tools["ffmpeg"] and not tools["ffmpeg_subtitles_filter"]:
418
- warnings.append("ffmpeg lacks subtitles/libass filter; --burn-subtitles will fail")
187
+ if subtitle_delivery == "sidecar_srt":
188
+ warnings.append(
189
+ "ffmpeg lacks subtitles/libass filter; the default run will not burn subtitles and "
190
+ "delivers a .srt sidecar instead (explicit --burn-subtitles fails): "
191
+ "install an ffmpeg build with libass to burn them"
192
+ )
193
+ elif subtitle_delivery == "fails_explicit_burn":
194
+ warnings.append(
195
+ "ffmpeg lacks subtitles/libass filter and BURN_SUBTITLES asks for burn-in, so runs "
196
+ "will stop at preflight: unset BURN_SUBTITLES to get a .srt sidecar, pass "
197
+ "--no-burn-subtitles, or install an ffmpeg build with libass"
198
+ )
199
+ if tools["ffmpeg"] and not drawtext_filter:
200
+ warnings.append(
201
+ "ffmpeg lacks the drawtext filter (libfreetype); narration visual_overlays will stop "
202
+ "the run before TTS: remove them or install an ffmpeg build with drawtext"
203
+ )
419
204
  if not checks["api_config"]["api_key_set"]:
420
205
  failures.append("MIMO_API_KEY is not set; the default ASR / VLM path requires MiMo")
206
+ if not mimo_video_configured:
207
+ warnings.append(
208
+ "MiMo VLM not configured: set MIMO_VIDEO_API_KEY or MIMO_API_KEY before video understanding"
209
+ )
210
+ if not tts_configured and effective_tts_provider in TTS_KEY_HINTS:
211
+ warnings.append(
212
+ f"TTS provider {effective_tts_provider} not configured: "
213
+ f"set {TTS_KEY_HINTS[effective_tts_provider]} before voiceover"
214
+ )
421
215
  if not checks["asr"]["available"]:
422
216
  warnings.append("ASR not configured (MIMO_API_KEY); pipeline can run with --skip-asr")
423
217
  return {
424
218
  "ok": not failures,
425
219
  "repo_root": str(SCRIPT_DIR.parents[2]),
426
220
  "checks": checks,
427
- "capability_menu": _build_capability_menu(checks),
428
221
  "failures": failures,
429
222
  "warnings": warnings,
430
223
  }
@@ -449,6 +242,12 @@ def _print_human(report: dict) -> None:
449
242
  f"{_status_icon(system['ffmpeg_subtitles_filter'], warning=True)} "
450
243
  f"ffmpeg subtitles/libass filter: "
451
244
  f"{'available' if system['ffmpeg_subtitles_filter'] else 'missing'}"
245
+ f" (subtitles: {system['subtitle_delivery']})"
246
+ )
247
+ print(
248
+ f"{_status_icon(system['ffmpeg_drawtext_filter'], warning=True)} "
249
+ f"ffmpeg drawtext filter (visual_overlays): "
250
+ f"{'available' if system['ffmpeg_drawtext_filter'] else 'missing'}"
452
251
  )
453
252
 
454
253
  api = checks["api_config"]
@@ -504,22 +303,6 @@ def _print_human(report: dict) -> None:
504
303
  print(f"✓ TTS voice: {tts['mimo_tts_voice']} (source: {tts['mimo_tts_voice_source']})")
505
304
  print(f"✓ TTS API URL: {tts['mimo_tts_api_url']} (source: {tts['mimo_tts_api_url_source']})")
506
305
 
507
- menu = report["capability_menu"]
508
- print("\n[capability menu]")
509
- for group in ("ready", "blocked", DEGRADED_GROUP, "optional_upgrades"):
510
- print(f"{group}:")
511
- items = menu[group]
512
- if not items:
513
- print(" - none")
514
- continue
515
- for item in items:
516
- line = f" - {item['name']}: {item['summary']}"
517
- if "detail" in item:
518
- line += f" ({item['detail']})"
519
- print(line)
520
- if "action" in item:
521
- print(f" action: {item['action']}")
522
-
523
306
  if report["warnings"]:
524
307
  print("\nWarnings:")
525
308
  for warning in report["warnings"]: