@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
@@ -1,175 +0,0 @@
1
- """Normalize MiMo QC observations into the local contract."""
2
-
3
- from __future__ import annotations
4
-
5
- import json
6
- import re
7
- from typing import Any
8
- from collections.abc import Mapping
9
-
10
- import qc_contract
11
- from qc.mimo_evidence import _summarize, safe_mimo_config
12
- from qc.mimo_payload import _strip_json_fence
13
- from qc.mimo_contract import (
14
- ARTIFACT_NAME,
15
- DEFAULT_STAGE,
16
- MAX_MESSAGE_CHARS,
17
- MAX_OBSERVATIONS,
18
- )
19
-
20
-
21
- def _extract_observations(model_output: Any) -> list[Mapping[str, Any]]:
22
- """Bound whatever shape the model (or an offline fixture) returned to observation dicts."""
23
- if isinstance(model_output, str):
24
- try:
25
- model_output = json.loads(_strip_json_fence(model_output))
26
- except ValueError:
27
- return [
28
- {
29
- "code": "freeform_observation",
30
- "message": model_output,
31
- "confidence": "low",
32
- }
33
- ]
34
- if isinstance(model_output, list):
35
- return [item for item in model_output if isinstance(item, Mapping)][
36
- :MAX_OBSERVATIONS
37
- ]
38
- if not isinstance(model_output, Mapping):
39
- return []
40
- for key in ("observations", "findings"):
41
- if isinstance(model_output.get(key), list):
42
- return [item for item in model_output[key] if isinstance(item, Mapping)][
43
- :MAX_OBSERVATIONS
44
- ]
45
- if "choices" in model_output: # a raw chat-completion response saved as a fixture
46
- return _extract_observations(model_output["choices"][0]["message"]["content"])
47
- return [model_output] if model_output else []
48
-
49
-
50
- def _norm_choice(raw: Any, allowed: set[str], default: str) -> str:
51
- value = str(raw or "").strip().lower()
52
- return value if value in allowed else default
53
-
54
-
55
- def _caption_match_text(value: Any) -> str:
56
- return re.sub(r"[^0-9A-Za-z㐀-鿿]+", "", str(value or "")).lower()
57
-
58
-
59
- def _generated_subtitle_corpus(payload: Mapping[str, Any]) -> str:
60
- strings = []
61
-
62
- def visit(value: Any) -> None:
63
- if isinstance(value, str):
64
- strings.append(value)
65
- elif isinstance(value, Mapping):
66
- for child in value.values():
67
- visit(child)
68
- elif isinstance(value, list):
69
- for child in value:
70
- visit(child)
71
-
72
- visit(payload["evidence"]["generated_subtitles"])
73
- return _caption_match_text(" ".join(strings))
74
-
75
-
76
- def _misclassified_generated_caption(
77
- observation: Mapping[str, Any], payload: Mapping[str, Any], stage: str
78
- ) -> bool:
79
- """Drop an objectively contradicted 'source subtitle visible' observation.
80
-
81
- MiMo occasionally calls the intended recap cue on an opaque mask band a leftover source
82
- subtitle even when its own ``visible_text`` exactly matches generated_subtitles. This is not
83
- a subjective disagreement: the artifact provides direct provenance for that text.
84
- """
85
- if stage != "post_render":
86
- return False
87
- code_and_message = (
88
- f"{observation.get('code', '')} {observation.get('message', '')}".lower()
89
- )
90
- if not any(
91
- token in code_and_message
92
- for token in ("source_subtitle", "source subtitle", "源字幕")
93
- ):
94
- return False
95
- if any(
96
- token in code_and_message for token in ("overlap", "double", "两层", "重叠")
97
- ):
98
- return False
99
- evidence = observation.get("evidence")
100
- visible = evidence.get("visible_text") if isinstance(evidence, Mapping) else None
101
- visible = _caption_match_text(visible)
102
- corpus = _generated_subtitle_corpus(payload)
103
- return len(visible) >= 4 and bool(corpus) and visible in corpus
104
-
105
-
106
- def normalize_observations(
107
- model_output: Any,
108
- *,
109
- payload: Mapping[str, Any],
110
- stage: str = DEFAULT_STAGE,
111
- model_config: Mapping[str, Any] | None = None,
112
- ) -> list[dict[str, Any]]:
113
- """Normalize bounded model output into permanently non-blocking findings."""
114
- cfg = safe_mimo_config(model_config)
115
- model_used = cfg["model"]
116
- findings = []
117
- for index, observation in enumerate(_extract_observations(model_output), start=1):
118
- if _misclassified_generated_caption(observation, payload, stage):
119
- continue
120
- obs = _summarize(observation, max_items=10, max_string=MAX_MESSAGE_CHARS)
121
- category_hint = str(
122
- obs.get("category") or obs.get("type") or "semantic"
123
- ).lower()
124
- category = "mimo_aesthetic" if "aesthetic" in category_hint else "mimo_semantic"
125
- code = str(
126
- obs.get("code") or obs.get("rule_id") or f"mimo_observation_{index}"
127
- ).strip()
128
- code = (code or f"mimo_observation_{index}")[:96]
129
- message = str(
130
- obs.get("message")
131
- or obs.get("summary")
132
- or obs.get("text")
133
- or "MiMo QC observation"
134
- )
135
- message = message.strip()[:MAX_MESSAGE_CHARS] or "MiMo QC observation"
136
- confidence = _norm_choice(
137
- obs.get("confidence"), {"low", "medium", "high"}, "low"
138
- )
139
- sample_type = _norm_choice(
140
- obs.get("sample_policy") or obs.get("sample_policy_type"),
141
- {"semantic", "aesthetic", "sampled"},
142
- "aesthetic" if category == "mimo_aesthetic" else "semantic",
143
- )
144
- raw_evidence = obs.get("evidence")
145
- evidence = {
146
- **(raw_evidence if isinstance(raw_evidence, Mapping) else {}),
147
- "model": model_used,
148
- "config": cfg,
149
- }
150
- location = obs.get("location")
151
- findings.append(
152
- qc_contract.build_finding(
153
- finding_id=str(
154
- obs.get("finding_id")
155
- or obs.get("id")
156
- or f"mimo-{stage}-{index:03d}"
157
- )[:160],
158
- stage=stage,
159
- severity="advisory",
160
- confidence=confidence,
161
- sample_policy={"type": sample_type},
162
- category=category,
163
- code=code,
164
- message=message,
165
- deterministic=False,
166
- blocking=False,
167
- source={"artifact": ARTIFACT_NAME, "adapter": "mimo_qc.py"},
168
- location=location if isinstance(location, Mapping) else {},
169
- evidence=evidence,
170
- model_used=model_used,
171
- next_action="human_review",
172
- decision_reason=message,
173
- )
174
- )
175
- return findings
@@ -1,206 +0,0 @@
1
- """Build bounded semantic and multimodal MiMo QC payloads."""
2
-
3
- from __future__ import annotations
4
-
5
- import json
6
- from typing import Any
7
- from collections.abc import Mapping, Sequence
8
-
9
- from qc.mimo_evidence import safe_mimo_config
10
- from qc.mimo_contract import ARTIFACT_NAME, DEFAULT_STAGE
11
-
12
-
13
- def _semantic_evidence(evidence: Mapping[str, Any], *, stage: str) -> dict[str, Any]:
14
- """Keep real artifact values visible to MiMo without sending irrelevant paths.
15
-
16
- ``collect_evidence`` already bounds every artifact independently. The additional
17
- summarization here therefore needs enough depth to retain the narration/ASR/TTS
18
- scalars nested inside those summaries. A shallow second pass used to replace the
19
- values with opaque placeholders, which made live QC invent missing-script and failed-TTS
20
- findings from evidence it could no longer read.
21
- """
22
- semantic = dict(evidence)
23
- semantic.pop("work_dir", None)
24
- semantic["evidence_roles"] = {
25
- "narration.json": (
26
- "Planned recap voiceover on the OUTPUT timeline; judge its factual and temporal "
27
- "fit rather than expecting verbatim source dialogue."
28
- ),
29
- "tts_meta.json": (
30
- "Synthesis and placement metadata for narration.json; an empty failures list means "
31
- "all requested narration segments synthesized successfully."
32
- ),
33
- "source_asr": (
34
- "Transcript of the SOURCE media audio, not a transcript of generated TTS. "
35
- "Wording differences from narration are expected; use this evidence for factual "
36
- "support and original-dialogue timing, not verbatim equality."
37
- ),
38
- "generated_subtitles": (
39
- "Recap subtitles derived from generated narration/TTS during assembly. They are not "
40
- "source ASR and must never be used as independent factual support. Visible text that "
41
- "matches one of these cues is the intended generated caption, including when it sits "
42
- "on the black source-subtitle mask band."
43
- ),
44
- "visual_metadata": (
45
- "Diagnostic storyboard/frame metadata. labels_burned refers only to diagnostic "
46
- "timestamp labels and does not mean labels were burned into the final video."
47
- ),
48
- "final_output": (
49
- "Only present for post_render and limited to candidates that actually exist."
50
- ),
51
- "post_render_frame_limits": (
52
- "Sampled final-output frames are silent still images: they contain no audible audio. "
53
- "Do not infer which audio track is playing from visible source captions. Narration/TTS "
54
- "timings are segment-level, not word-level, so an internal phrase cannot be assigned to "
55
- "an exact sampled-frame timestamp."
56
- ),
57
- "narration_and_subtitle_gaps": (
58
- "Generated narration/subtitle gaps may be deliberate original-audio blocks. In those "
59
- "gaps the source audio and its existing captions can intentionally return; do not call "
60
- "the absence of generated subtitles a defect without evidence of accidental silence."
61
- ),
62
- "actual_audio_timing": (
63
- "narration.json start/end values are authoring slots, not proof that speech fills the "
64
- "whole slot. For post-render timing, assembly_manifest audio_segments "
65
- "actual_place_start/actual_place_end are authoritative. A sampled frame after "
66
- "actual_place_end is in an original-audio gap."
67
- ),
68
- }
69
- if stage != "post_render":
70
- semantic.pop("final_output", None)
71
- # These files can be leftovers from an earlier assembly when narration is being revised.
72
- # Pre-assemble QC must evaluate current narration/TTS, not stale rendered captions.
73
- semantic.pop("generated_subtitles", None)
74
- semantic["evidence_roles"].pop("generated_subtitles", None)
75
- semantic["evidence_roles"].pop("post_render_frame_limits", None)
76
- semantic["evidence_roles"].pop("actual_audio_timing", None)
77
- else:
78
- semantic["final_output"] = {
79
- "candidates": [
80
- dict(candidate)
81
- for candidate in semantic["final_output"]["candidates"]
82
- if candidate["exists"]
83
- ]
84
- }
85
- # Artifact summaries are already bounded at collection time; summarizing them again
86
- # would wrap their ``items`` arrays in another summary layer and obscure the values.
87
- return semantic
88
-
89
-
90
- def build_payload(
91
- evidence: Mapping[str, Any],
92
- *,
93
- stage: str = DEFAULT_STAGE,
94
- config: Mapping[str, Any] | None = None,
95
- ) -> dict[str, Any]:
96
- """Build the report-safe semantic payload (never contains image base64)."""
97
- cfg = safe_mimo_config(config)
98
- payload = {
99
- "stage": stage,
100
- "artifact": ARTIFACT_NAME,
101
- "model": cfg["model"],
102
- "config": cfg,
103
- "instructions": (
104
- "你是 video-recap 的建议性质量审阅器。结合解说、剪辑计划、字幕/ASR、TTS、"
105
- "组装元数据和抽样画面,指出语义或审美问题。只返回主观观察,不做自动修复,"
106
- "只根据可见的实际字段判断,不得从文件字节数、截断或省略标记推断内容缺失;"
107
- "source_asr 是源素材原声证据,不是生成后 TTS 的逐字转录;generated_subtitles 是本轮生成字幕,"
108
- "二者角色不可混淆,解说改写与源台词不一致本身不是问题;"
109
- "空 failures 表示没有失败,storyboard 的 labels_burned 仅表示诊断图上的时间标签,"
110
- "不代表标签烧进最终视频;pre_assemble 阶段尚无 final_output 属于正常状态;"
111
- "成片抽样画面是带时间戳的稀疏点样本,只能判断该具体时刻,不得用单帧否定整段内其他时刻的画面。"
112
- "这些抽样帧是无声静帧,不能从画面中的源字幕推断当时实际播放哪条音轨;"
113
- "源字幕遮罩策略为 off 时,保留的源字幕无需与解说逐字一致;解说/TTS 只有段级时间,"
114
- "没有词级对齐,不得把句中某个短语强行对应到抽样帧的精确秒数。"
115
- "生成解说和字幕之间的空档可能是刻意保留的原声块,此时原声和源字幕回归是正常设计,"
116
- "没有意外静音证据时不得把生成字幕空档当成缺失。只输出可采取行动的疑似问题,"
117
- "不要把正常、一致、相符或通过项作为 observation。不得提出阻断决定。"
118
- "narration.json 的 start/end 是创作槽位,不表示旁白铺满整段;成片阶段必须以 "
119
- "assembly_manifest.audio_segments 的 actual_place_start/actual_place_end 判断实际旁白。"
120
- "抽样点超过 actual_place_end 时属于原声块;一个字幕时间段含多个分句时,静帧匹配其中任一分句都不算错位。"
121
- "只有同一编号静帧中同时清晰可读两层不同字幕,才能报告字幕重叠;不同静帧分别出现解说字幕和源字幕不算重叠。"
122
- "遮罩 opacity=1.0 表示源字幕像素已被不透明覆盖,不得臆测遮罩下仍有可见文字。"
123
- "黑色遮罩带上与 generated_subtitles cue 一致的白字是本轮预期生成字幕,不是残留源字幕;"
124
- "必须先逐字对照 generated_subtitles,再判断是否另有第二层源字幕。"
125
- "每张抽样图都由同编号 BEGIN/END 文本包围,必须按编号和时间戳独立判断,"
126
- "不得把一张图里的文字或人物归到另一张图。"
127
- '返回 JSON:{"observations":[{"code":...,'
128
- '"message":...,"category":"semantic|aesthetic",'
129
- '"confidence":"low|medium|high","sample_policy":'
130
- '"semantic|aesthetic|sampled","evidence":{...}}]}。最多 12 条。'
131
- ),
132
- "evidence": _semantic_evidence(evidence, stage=stage),
133
- }
134
- return payload
135
-
136
-
137
- def _request_payload(
138
- payload: Mapping[str, Any], frame_samples: Sequence[Mapping[str, Any]]
139
- ) -> dict[str, Any]:
140
- request_evidence = {
141
- "stage": payload["stage"],
142
- "instructions": payload["instructions"],
143
- "evidence": payload["evidence"],
144
- }
145
- content: list[dict[str, Any]] = [
146
- {
147
- "type": "text",
148
- "text": json.dumps(
149
- request_evidence, ensure_ascii=False, separators=(",", ":")
150
- ),
151
- }
152
- ]
153
- for index, sample in enumerate(frame_samples, start=1):
154
- timestamp_label = f"{sample['timestamp']:.3f}s"
155
- content.append(
156
- {
157
- "type": "text",
158
- "text": (
159
- f"BEGIN QC_FRAME_{index}: final-output timestamp {timestamp_label}. "
160
- f"Judge only this image and do not transfer its content to another frame."
161
- ),
162
- }
163
- )
164
- content.append({"type": "image_url", "image_url": {"url": sample["data_url"]}})
165
- content.append(
166
- {
167
- "type": "text",
168
- "text": f"END QC_FRAME_{index}: timestamp {timestamp_label}.",
169
- }
170
- )
171
- return {
172
- "model": payload["model"],
173
- "messages": [{"role": "user", "content": content}],
174
- "max_completion_tokens": 1600,
175
- "thinking": {"type": "disabled"},
176
- }
177
-
178
-
179
- def _strip_json_fence(text: str) -> str:
180
- value = text.strip()
181
- if value.startswith("```") and value.endswith("```"):
182
- lines = value.splitlines()
183
- if len(lines) >= 3:
184
- value = "\n".join(lines[1:-1]).strip()
185
- return value
186
-
187
-
188
- def _validated_live_output(response: Any) -> Any:
189
- """The one shape check on the third-party model response: a list of observations, or an
190
- object carrying `observations`/`findings`; anything else is a failed (fail-open) request."""
191
- try:
192
- content = response["choices"][0]["message"]["content"]
193
- except (KeyError, IndexError, TypeError):
194
- raise ValueError("malformed_response") from None
195
- if isinstance(content, str):
196
- try:
197
- content = json.loads(_strip_json_fence(content))
198
- except (TypeError, ValueError):
199
- raise ValueError("malformed_json_content") from None
200
- if isinstance(content, list):
201
- return content
202
- if isinstance(content, Mapping) and isinstance(content.get("observations"), list):
203
- return content
204
- if isinstance(content, Mapping) and isinstance(content.get("findings"), list):
205
- return content
206
- raise ValueError("malformed_observations")