@oh-story/dsh 0.1.11 → 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
@@ -1,318 +0,0 @@
1
- """Sample frames and build aggregate MiMo QC reports."""
2
-
3
- from __future__ import annotations
4
-
5
- import base64
6
- import json
7
- import math
8
- import os
9
- import subprocess
10
- import tempfile
11
- from pathlib import Path
12
- from typing import Any
13
- from collections.abc import Callable, Mapping, Sequence
14
-
15
- import qc_contract
16
- from qc.mimo_client import mimo_qc_api_call
17
- from qc.mimo_evidence import (
18
- _cache_evidence,
19
- _effective_config,
20
- _existing_final_output,
21
- collect_evidence,
22
- safe_mimo_config,
23
- )
24
- from qc.mimo_observations import normalize_observations
25
- from qc.mimo_payload import _request_payload, _validated_live_output, build_payload
26
- from qc.mimo_contract import (
27
- ARTIFACT_NAME,
28
- DEFAULT_STAGE,
29
- FRAME_SAMPLER_VERSION,
30
- MAX_FRAME_DIMENSION,
31
- MAX_FRAMES,
32
- )
33
-
34
- JudgeCallable = Callable[
35
- [Mapping[str, Any]], Mapping[str, Any] | Sequence[Mapping[str, Any]]
36
- ]
37
-
38
- FrameSampler = Callable[..., Sequence[Mapping[str, Any]]]
39
-
40
-
41
- def _probe_duration(path: Path) -> float:
42
- """Media duration via ffprobe; 0.0 when ffprobe cannot read it (frame sampling is advisory)."""
43
- result = subprocess.run(
44
- [
45
- "ffprobe",
46
- "-v",
47
- "error",
48
- "-show_entries",
49
- "format=duration",
50
- "-of",
51
- "json",
52
- str(path),
53
- ],
54
- capture_output=True,
55
- text=True,
56
- timeout=20,
57
- check=False,
58
- )
59
- if result.returncode != 0:
60
- return 0.0
61
- try:
62
- return max(0.0, float(json.loads(result.stdout)["format"]["duration"]))
63
- except (KeyError, ValueError):
64
- return 0.0
65
-
66
-
67
- def sample_video_frames(
68
- video_path: str | Path,
69
- *,
70
- max_frames: int = MAX_FRAMES,
71
- max_dimension: int = MAX_FRAME_DIMENSION,
72
- ) -> list[dict[str, Any]]:
73
- """Extract at most six bounded JPEGs; returned base64 is request-only."""
74
- path = Path(video_path)
75
- if not path.is_file() or max_frames <= 0:
76
- return []
77
- try:
78
- duration = _probe_duration(path)
79
- except (OSError, subprocess.SubprocessError):
80
- return []
81
- if duration <= 0:
82
- return []
83
- count = min(max_frames, max(1, math.ceil(duration / 15.0)))
84
- samples = []
85
- with tempfile.TemporaryDirectory(prefix="video-recap-mimo-qc-") as tmp:
86
- for index in range(count):
87
- timestamp = duration * (index + 0.5) / count
88
- destination = Path(tmp) / f"frame-{index:02d}.jpg"
89
- try:
90
- result = subprocess.run(
91
- [
92
- "ffmpeg",
93
- "-hide_banner",
94
- "-loglevel",
95
- "error",
96
- "-y",
97
- "-ss",
98
- f"{timestamp:.3f}",
99
- "-i",
100
- str(path),
101
- "-frames:v",
102
- "1",
103
- "-vf",
104
- f"scale={max_dimension}:{max_dimension}:force_original_aspect_ratio=decrease",
105
- "-q:v",
106
- "4",
107
- str(destination),
108
- ],
109
- capture_output=True,
110
- timeout=30,
111
- check=False,
112
- )
113
- except (OSError, subprocess.SubprocessError):
114
- continue
115
- if result.returncode != 0 or not destination.is_file():
116
- continue
117
- raw = destination.read_bytes()
118
- samples.append(
119
- {
120
- "data_url": "data:image/jpeg;base64,"
121
- + base64.b64encode(raw).decode("ascii"),
122
- "bytes": len(raw),
123
- "timestamp": round(timestamp, 3),
124
- }
125
- )
126
- return samples
127
-
128
-
129
- def _frame_metadata(samples: Sequence[Mapping[str, Any]]) -> dict[str, Any]:
130
- return {
131
- "count": len(samples),
132
- "max_frames": MAX_FRAMES,
133
- "max_dimension": MAX_FRAME_DIMENSION,
134
- "sampler_version": FRAME_SAMPLER_VERSION,
135
- "samples": [
136
- {"bytes": sample["bytes"], "timestamp": sample["timestamp"]}
137
- for sample in samples
138
- ],
139
- }
140
-
141
-
142
- def _cache_input(
143
- stage: str,
144
- payload: Mapping[str, Any],
145
- evidence: Mapping[str, Any],
146
- frames: Mapping[str, Any],
147
- ) -> dict[str, Any]:
148
- """Everything a stage report depends on; an existing report whose stored cache_input
149
- equals this dict is reused instead of a new request."""
150
- return {
151
- "stage": stage,
152
- "model": payload["model"],
153
- "config": payload["config"],
154
- "instructions": payload["instructions"],
155
- "evidence": _cache_evidence(evidence),
156
- "frames": frames,
157
- "contract": qc_contract.SCHEMA_VERSION,
158
- }
159
-
160
-
161
- def _stage_reports(path: Path) -> dict[str, dict[str, Any]]:
162
- """Per-stage reports from an existing aggregate mimo_qc.json, or {} before the first stage.
163
-
164
- The aggregate is this module's own atomically written, contract-validated report, so a
165
- corrupt one raises (recap_stage_qc keeps the pipeline fail-open around the whole stage)."""
166
- if not path.is_file():
167
- return {}
168
- return dict(json.loads(path.read_text(encoding="utf-8"))["metadata"]["stages"])
169
-
170
-
171
- def _error_name(exc: Exception) -> str:
172
- return (str(exc).strip() or type(exc).__name__)[:120]
173
-
174
-
175
- def build_report(
176
- work_dir: str | Path,
177
- *,
178
- stage: str = DEFAULT_STAGE,
179
- fixture: Any | None = None,
180
- dry_run: bool = False,
181
- judge: JudgeCallable | None = None,
182
- config: Mapping[str, Any] | None = None,
183
- final_output: str | Path | None = None,
184
- live: bool = False,
185
- refresh: bool = False,
186
- frame_sampler: FrameSampler | None = None,
187
- existing: Mapping[str, Any] | None = None,
188
- api_call: Callable[..., Any] | None = None,
189
- ) -> dict[str, Any]:
190
- """Build one validated stage report; all live failures remain successful QC."""
191
- root = Path(work_dir)
192
- evidence = collect_evidence(root, final_output=final_output)
193
- payload = build_payload(evidence, stage=stage, config=config)
194
- cfg = safe_mimo_config(config)
195
- samples: Sequence[Mapping[str, Any]] = []
196
- if stage == "post_render" and (fixture is not None or judge is not None or live):
197
- output = _existing_final_output(root, final_output)
198
- if output is not None:
199
- sampler = frame_sampler or sample_video_frames
200
- samples = list(
201
- sampler(output, max_frames=MAX_FRAMES, max_dimension=MAX_FRAME_DIMENSION)
202
- )[:MAX_FRAMES]
203
- frame_meta = _frame_metadata(samples)
204
- cache_input = _cache_input(stage, payload, evidence, frame_meta)
205
-
206
- if (
207
- not refresh
208
- and existing is not None
209
- and existing["metadata"]["cache_input"] == cache_input
210
- ):
211
- cached = json.loads(json.dumps(existing))
212
- cached["metadata"].update(status="cached", mode="live_cache", request_count=0)
213
- return cached
214
-
215
- status = "completed"
216
- error = None
217
- if fixture is not None:
218
- model_output = fixture
219
- mode = "fixture"
220
- elif judge is not None and not dry_run:
221
- mode = "injected_judge"
222
- try:
223
- model_output = judge(payload)
224
- except Exception as exc:
225
- model_output = {"observations": []}
226
- status, error = "failed", _error_name(exc)
227
- elif live and not dry_run:
228
- mode = "live"
229
- if not cfg["key_present"]:
230
- model_output = {"observations": []}
231
- status, error = "unavailable", "missing_key"
232
- else:
233
- # Fail-open boundary for the third-party request: transport errors arrive as
234
- # MiMoQCRequestError (a RuntimeError), malformed responses as ValueError.
235
- try:
236
- response = (api_call or mimo_qc_api_call)(
237
- _request_payload(payload, samples),
238
- config=_effective_config(config),
239
- timeout=60,
240
- )
241
- model_output = _validated_live_output(response)
242
- except (RuntimeError, ValueError) as exc:
243
- model_output = {"observations": []}
244
- status, error = "failed", _error_name(exc)
245
- else:
246
- model_output = {"observations": []}
247
- mode = "dry_run"
248
- status = "dry_run"
249
-
250
- findings = normalize_observations(
251
- model_output, stage=stage, payload=payload, model_config=config
252
- )
253
- metadata = {
254
- "mode": mode,
255
- "status": status,
256
- "error": error,
257
- "report_only": True,
258
- "auto_repair": False,
259
- "pipeline_blocking": False,
260
- "deterministic": False,
261
- "request_count": 1
262
- if mode == "live" and status in {"completed", "failed"}
263
- else 0,
264
- "cache_input": cache_input,
265
- "frame_samples": frame_meta,
266
- "evidence": evidence,
267
- "payload": payload,
268
- "config": cfg,
269
- }
270
- return qc_contract.build_report(
271
- artifact=ARTIFACT_NAME,
272
- stage=stage,
273
- findings=findings,
274
- metadata=metadata,
275
- )
276
-
277
-
278
- def _aggregate_reports(
279
- stage_reports: Mapping[str, Mapping[str, Any]], current_stage: str
280
- ) -> dict[str, Any]:
281
- findings = [
282
- finding
283
- for stage_report in stage_reports.values()
284
- for finding in stage_report["findings"]
285
- ]
286
- metadata = dict(stage_reports[current_stage]["metadata"])
287
- metadata["stages"] = {
288
- stage: dict(report) for stage, report in stage_reports.items()
289
- }
290
- return qc_contract.build_report(
291
- artifact=ARTIFACT_NAME,
292
- stage=current_stage,
293
- findings=findings,
294
- metadata=metadata,
295
- )
296
-
297
-
298
- def write_report(
299
- work_dir: str | Path, report: Mapping[str, Any], *, output: str | Path | None = None
300
- ) -> tuple[Path, dict[str, Any]]:
301
- """Atomically merge one stage into mimo_qc.json and return the aggregate."""
302
- path = Path(output) if output else Path(work_dir) / ARTIFACT_NAME
303
- path.parent.mkdir(parents=True, exist_ok=True)
304
- stages = _stage_reports(path)
305
- stages[report["stage"]] = dict(report)
306
- aggregate = _aggregate_reports(stages, report["stage"])
307
- serialized = json.dumps(aggregate, ensure_ascii=False, indent=2) + "\n"
308
- descriptor, temp_name = tempfile.mkstemp(prefix=f".{path.name}.", dir=path.parent)
309
- try:
310
- with os.fdopen(descriptor, "w", encoding="utf-8") as handle:
311
- handle.write(serialized)
312
- handle.flush()
313
- os.fsync(handle.fileno())
314
- os.replace(temp_name, path)
315
- except BaseException:
316
- Path(temp_name).unlink(missing_ok=True)
317
- raise
318
- return path, aggregate
@@ -1,130 +0,0 @@
1
- """Run and persist advisory MiMo QC stages."""
2
-
3
- from __future__ import annotations
4
-
5
- import argparse
6
-
7
-
8
- import json
9
-
10
-
11
- from pathlib import Path
12
-
13
- from typing import Any
14
- from collections.abc import Callable, Mapping, Sequence
15
-
16
- import qc_contract
17
-
18
- from qc.mimo_report import _stage_reports, build_report, write_report
19
- from qc.mimo_contract import ARTIFACT_NAME, DEFAULT_STAGE
20
-
21
- JudgeCallable = Callable[
22
- [Mapping[str, Any]], Mapping[str, Any] | Sequence[Mapping[str, Any]]
23
- ]
24
-
25
- FrameSampler = Callable[..., Sequence[Mapping[str, Any]]]
26
-
27
-
28
- def run(
29
- work_dir: str | Path,
30
- *,
31
- stage: str = DEFAULT_STAGE,
32
- fixture: Any | None = None,
33
- dry_run: bool = False,
34
- judge: JudgeCallable | None = None,
35
- config: Mapping[str, Any] | None = None,
36
- final_output: str | Path | None = None,
37
- output: str | Path | None = None,
38
- live: bool = False,
39
- refresh: bool = False,
40
- frame_sampler: FrameSampler | None = None,
41
- api_call: Callable[..., Any] | None = None,
42
- ) -> dict[str, Any]:
43
- path = Path(output) if output else Path(work_dir) / ARTIFACT_NAME
44
- existing = _stage_reports(path).get(stage)
45
- report = build_report(
46
- work_dir,
47
- stage=stage,
48
- fixture=fixture,
49
- dry_run=dry_run,
50
- judge=judge,
51
- config=config,
52
- final_output=final_output,
53
- live=live,
54
- refresh=refresh,
55
- frame_sampler=frame_sampler,
56
- existing=existing,
57
- api_call=api_call,
58
- )
59
- written_path, aggregate = write_report(work_dir, report, output=output)
60
- return {"path": str(written_path), "report": aggregate}
61
-
62
-
63
- def clear_report(work_dir: str | Path, *, output: str | Path | None = None) -> bool:
64
- path = Path(output) if output else Path(work_dir) / ARTIFACT_NAME
65
- try:
66
- path.unlink()
67
- return True
68
- except FileNotFoundError:
69
- return False
70
-
71
-
72
- def _load_fixture(path: str | Path | None) -> Any | None:
73
- if not path:
74
- return None
75
- with Path(path).open("r", encoding="utf-8") as handle:
76
- return json.load(handle)
77
-
78
-
79
- def main(
80
- argv: Sequence[str] | None = None,
81
- *,
82
- run_callable: Callable[..., dict[str, Any]] | None = None,
83
- ) -> int:
84
- parser = argparse.ArgumentParser(
85
- description="Write advisory MiMo QC from local recap evidence."
86
- )
87
- parser.add_argument("--work-dir", required=True)
88
- parser.add_argument(
89
- "--stage", default=DEFAULT_STAGE, choices=sorted(qc_contract.STAGES)
90
- )
91
- parser.add_argument("--fixture", help="offline model-response fixture JSON")
92
- parser.add_argument(
93
- "--live", action="store_true", help="make one live MiMo request for this stage"
94
- )
95
- parser.add_argument(
96
- "--refresh", action="store_true", help="ignore a matching stage cache"
97
- )
98
- parser.add_argument(
99
- "--dry-run",
100
- action="store_true",
101
- help="write evidence only; never access the network",
102
- )
103
- parser.add_argument("--model", help="override MIMO_QC_MODEL for this call")
104
- parser.add_argument("--final-output")
105
- parser.add_argument("--output")
106
- args = parser.parse_args(argv)
107
- fixture = _load_fixture(args.fixture)
108
- config = {"mimo_qc_model": args.model} if args.model else None
109
- result = (run_callable or run)(
110
- args.work_dir,
111
- stage=args.stage,
112
- fixture=fixture,
113
- live=args.live and fixture is None,
114
- refresh=args.refresh,
115
- dry_run=args.dry_run or (not args.live and fixture is None),
116
- config=config,
117
- final_output=args.final_output,
118
- output=args.output,
119
- )
120
- print(
121
- json.dumps(
122
- {
123
- "ok": result["report"]["ok"],
124
- "status": result["report"]["metadata"]["status"],
125
- "path": result["path"],
126
- },
127
- ensure_ascii=False,
128
- )
129
- )
130
- return 0