@oh-story/dsh 0.1.12 → 0.1.14
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.
- package/README.md +18 -12
- package/lib/client.js +1003 -652
- package/lib/client.js.map +4 -4
- package/lib/drama/manifest.json +41 -36
- package/lib/drama/skills/short-drama/scripts/creator_markdown_check.py +340 -13
- package/lib/drama/skills/short-drama-assets/references/voice-direction.md +4 -2
- package/lib/drama/skills/short-drama-edit/SKILL.md +32 -12
- package/lib/drama/skills/short-drama-edit/references/still-cuts.md +90 -0
- package/lib/drama/skills/short-drama-edit/scripts/edit_tool.py +1035 -145
- package/lib/drama/skills/short-drama-edit/scripts/selftest.py +48 -0
- package/lib/drama/skills/short-drama-produce/SKILL.md +6 -1
- package/lib/drama/skills/short-drama-produce/references/adapter-contract.md +12 -0
- package/lib/drama/skills/short-drama-produce/scripts/production_tool.py +138 -9
- package/lib/drama/skills/short-drama-produce/scripts/provider_adapters.py +6 -0
- package/lib/drama/skills/short-drama-storyboard/SKILL.md +2 -0
- package/lib/drama/skills/short-drama-storyboard/references/production-shot-grammar.md +3 -1
- package/lib/drama/skills/short-drama-video-prompts/SKILL.md +7 -0
- package/lib/drama/skills/short-drama-video-prompts/references/performance-action-timing.md +14 -0
- package/lib/drama/skills/short-drama-video-prompts/references/seedance-2.5.md +19 -6
- package/lib/drama/skills/short-drama-video-prompts/references/stage-contract.md +1 -0
- package/lib/drama/skills/short-drama-video-prompts/references/target-model-profile.md +5 -0
- package/lib/drama/skills/short-drama-video-prompts/references/wan-3.0.md +4 -1
- package/lib/index.js +17 -7
- package/lib/index.js.map +2 -2
- package/lib/novel-to-game/README.md +98 -44
- package/lib/novel-to-game/README_ZH.md +100 -46
- package/lib/novel-to-game/VERSION +1 -1
- package/lib/novel-to-game/examples/jin-ping-mei/build/BUILD_BRIEF.md +27 -202
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/RUN.md +23 -115
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/bg/chamber.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/bg/court_night.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/bg/court_rain.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/bg/garden.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/bg/hall.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/bg/kitchen.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/bg/street.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/bg/street_dawn.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/bg/temple.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/yue/moon.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/meng/angry.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/meng/blush.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/meng/neutral.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/meng/sad.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/meng/smile.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/meng/surprised.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/pan/angry.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/pan/blush.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/pan/neutral.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/pan/sad.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/pan/smile.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/pan/surprised.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/pinger/angry.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/pinger/blush.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/pinger/neutral.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/pinger/sad.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/pinger/smile.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/pinger/surprised.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/xuee/angry.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/xuee/blush.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/xuee/neutral.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/xuee/sad.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/xuee/smile.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/xuee/surprised.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/yue/angry.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/yue/blush.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/yue/neutral.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/yue/sad.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/yue/smile.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/yue/surprised.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/css/style.css +326 -2503
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/index.html +71 -4
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/adv.js +221 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/audio.js +113 -257
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/main.js +511 -3026
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/menus.js +277 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/petals.js +67 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/stage.js +180 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/store.js +70 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/story/common.js +198 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/story/common2.js +204 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/story/common3.js +198 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/story/dates.js +186 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/story/index.js +122 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/story/meng.js +190 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/story/pan.js +212 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/story/pinger.js +198 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/story/xuee.js +199 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/story/yue.js +214 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/package.json +2 -2
- package/lib/novel-to-game/examples/jin-ping-mei/build/art/generated-art.json +182 -45
- package/lib/novel-to-game/examples/jin-ping-mei/qa/README.md +7 -17
- package/lib/novel-to-game/examples/jin-ping-mei/qa/verification.json +9 -9
- package/lib/novel-to-game/manifest.json +303 -78
- package/lib/novel-to-game/skills/game-art-direction/references/art-direction-method.md +7 -2
- package/lib/novel-to-game/skills/game-concept/references/concept-method.md +2 -0
- package/lib/novel-to-game/skills/game-world-design/references/narrative-design-method.md +2 -0
- package/lib/novel-to-game/skills/game-world-design/references/world-design-method.md +9 -0
- package/lib/types/client/canvas-viewport.d.ts +31 -0
- package/lib/types/client/canvas-viewport.d.ts.map +1 -0
- package/lib/types/client/drama-production-view.d.ts +14 -2
- package/lib/types/client/drama-production-view.d.ts.map +1 -1
- package/lib/types/client/file-activity.d.ts +1 -1
- package/lib/types/client/file-activity.d.ts.map +1 -1
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/production-canvas.d.ts +24 -0
- package/lib/types/client/production-canvas.d.ts.map +1 -0
- package/lib/types/client/production-prompts.d.ts +7 -1
- package/lib/types/client/production-prompts.d.ts.map +1 -1
- package/lib/types/client/production-runtime.d.ts +7 -2
- package/lib/types/client/production-runtime.d.ts.map +1 -1
- package/lib/types/drama-adapters.d.ts.map +1 -1
- package/lib/types/skill-provider.d.ts.map +1 -1
- package/lib/types/video-project.d.ts.map +1 -1
- package/lib/video-recap/CHANGELOG.md +123 -1
- package/lib/video-recap/README.en.md +20 -16
- package/lib/video-recap/README.md +20 -16
- package/lib/video-recap/manifest.json +297 -321
- package/lib/video-recap/skills/video-assemble/SKILL.md +18 -16
- package/lib/video-recap/skills/video-assemble/references/explicit-audio-mix.md +3 -1
- package/lib/video-recap/skills/video-assemble/references/jianying/SOURCE.md +5 -5
- package/lib/video-recap/skills/video-assemble/references/packaging.md +4 -4
- package/lib/video-recap/skills/video-assemble/references/subtitle-track.md +10 -10
- package/lib/video-recap/skills/video-assemble/scripts/adoption/audio_mix_binding.py +13 -2
- package/lib/video-recap/skills/video-assemble/scripts/adoption/av_clock.py +109 -0
- package/lib/video-recap/skills/video-assemble/scripts/adoption/strict_publish.py +4 -1
- package/lib/video-recap/skills/video-assemble/scripts/artifacts.py +2 -9
- package/lib/video-recap/skills/video-assemble/scripts/assemble.py +188 -51
- package/lib/video-recap/skills/video-assemble/scripts/assemble_constants.py +0 -1
- package/lib/video-recap/skills/video-assemble/scripts/assembly_contract.py +32 -75
- package/lib/video-recap/skills/video-assemble/scripts/assembly_settings.py +8 -15
- package/lib/video-recap/skills/video-assemble/scripts/audio_mix.py +202 -161
- package/lib/video-recap/skills/video-assemble/scripts/codec_peak.py +112 -0
- package/lib/video-recap/skills/video-assemble/scripts/export_jianying.py +10 -101
- package/lib/video-recap/skills/video-assemble/scripts/jianying/builders.py +44 -475
- package/lib/video-recap/skills/video-assemble/scripts/jianying/model.py +1 -22
- package/lib/video-recap/skills/video-assemble/scripts/jianying/templates.py +0 -3
- package/lib/video-recap/skills/video-assemble/scripts/jianying/timeline_contract.py +36 -103
- package/lib/video-recap/skills/video-assemble/scripts/jianying/tracks.py +0 -8
- package/lib/video-recap/skills/video-assemble/scripts/jianying/writer.py +28 -203
- package/lib/video-recap/skills/video-assemble/scripts/lib.py +20 -34
- package/lib/video-recap/skills/video-assemble/scripts/loudness.py +332 -0
- package/lib/video-recap/skills/video-assemble/scripts/media.py +158 -38
- package/lib/video-recap/skills/video-assemble/scripts/narration_audio.py +30 -16
- package/lib/video-recap/skills/video-assemble/scripts/render_preflight.py +49 -8
- package/lib/video-recap/skills/video-assemble/scripts/source_subtitles.py +17 -6
- package/lib/video-recap/skills/video-assemble/scripts/subtitles/core.py +24 -2
- package/lib/video-recap/skills/video-assemble/scripts/subtitles/track.py +2 -5
- package/lib/video-recap/skills/video-assemble/scripts/subtitles/track_binding.py +2 -9
- package/lib/video-recap/skills/video-assemble/scripts/timeline.py +12 -34
- package/lib/video-recap/skills/video-assemble/scripts/timeline_emit.py +3 -4
- package/lib/video-recap/skills/video-assemble/scripts/visual_render.py +28 -2
- package/lib/video-recap/skills/video-cut/SKILL.md +37 -11
- package/lib/video-recap/skills/video-cut/references/shot-review.md +1 -1
- package/lib/video-recap/skills/video-cut/scripts/cut.py +2 -34
- package/lib/video-recap/skills/video-cut/scripts/cut_cli.py +96 -124
- package/lib/video-recap/skills/video-cut/scripts/cut_contract.py +97 -182
- package/lib/video-recap/skills/video-cut/scripts/cut_render.py +177 -166
- package/lib/video-recap/skills/video-cut/scripts/frame_grid.py +249 -0
- package/lib/video-recap/skills/video-cut/scripts/lib.py +0 -3
- package/lib/video-recap/skills/video-cut/scripts/media_geometry.py +173 -7
- package/lib/video-recap/skills/video-cut/scripts/sentence_boundaries.py +234 -151
- package/lib/video-recap/skills/video-cut/scripts/sentence_gate.py +189 -0
- package/lib/video-recap/skills/video-cut/scripts/shot_review.py +2 -6
- package/lib/video-recap/skills/video-recap/SKILL.md +28 -29
- package/lib/video-recap/skills/video-recap/references/audio-routing.md +8 -6
- package/lib/video-recap/skills/video-recap/references/config-playbook.md +10 -13
- package/lib/video-recap/skills/video-recap/references/data-schema.md +131 -74
- package/lib/video-recap/skills/video-recap/references/resource-library.md +17 -1
- package/lib/video-recap/skills/video-recap/scripts/doctor.py +51 -268
- package/lib/video-recap/skills/video-recap/scripts/final_qc.py +89 -254
- package/lib/video-recap/skills/video-recap/scripts/lib.py +81 -70
- package/lib/video-recap/skills/video-recap/scripts/library.py +40 -4
- package/lib/video-recap/skills/video-recap/scripts/materials.py +13 -80
- package/lib/video-recap/skills/video-recap/scripts/recap_cli.py +55 -17
- package/lib/video-recap/skills/video-recap/scripts/recap_inspect.py +1 -3
- package/lib/video-recap/skills/video-recap/scripts/recap_runner.py +96 -117
- package/lib/video-recap/skills/video-recap/scripts/recap_runtime.py +87 -23
- package/lib/video-recap/skills/video-recap/scripts/recap_source.py +0 -39
- package/lib/video-recap/skills/video-recap/scripts/recap_stage_qc.py +37 -135
- package/lib/video-recap/skills/video-recap/scripts/recap_timeline.py +118 -127
- package/lib/video-recap/skills/video-recap/scripts/resources/__init__.py +4 -0
- package/lib/video-recap/skills/video-recap/scripts/{resource_lock.py → resources/lock.py} +1 -1
- package/lib/video-recap/skills/video-recap/scripts/{project_binding.py → resources/project_binding.py} +78 -17
- package/lib/video-recap/skills/video-reference/SKILL.md +108 -0
- package/lib/video-recap/skills/video-reference/references/reference-schema.md +139 -0
- package/lib/video-recap/skills/video-reference/scripts/lib.py +85 -0
- package/lib/video-recap/skills/video-reference/scripts/reference.py +96 -0
- package/lib/video-recap/skills/video-reference/scripts/reference_check.py +553 -0
- package/lib/video-recap/skills/video-reference/scripts/reference_frames.py +122 -0
- package/lib/video-recap/skills/video-reference/scripts/reference_measure.py +253 -0
- package/lib/video-recap/skills/video-reference/scripts/reference_profile.py +271 -0
- package/lib/video-recap/skills/video-script/SKILL.md +53 -24
- package/lib/video-recap/skills/video-script/scripts/agent_text.py +18 -267
- package/lib/video-recap/skills/video-script/scripts/deslop_qc.py +12 -30
- package/lib/video-recap/skills/video-script/scripts/entry_suggestion.py +125 -0
- package/lib/video-recap/skills/video-script/scripts/evidence_bundle.py +2 -26
- package/lib/video-recap/skills/video-script/scripts/lib.py +5 -15
- package/lib/video-recap/skills/video-script/scripts/lint_summary.py +184 -0
- package/lib/video-recap/skills/video-script/scripts/narration_lint.py +120 -100
- package/lib/video-recap/skills/video-script/scripts/review.py +3 -34
- package/lib/video-recap/skills/video-script/scripts/review_grounding.py +20 -46
- package/lib/video-recap/skills/video-script/scripts/review_response.py +120 -258
- package/lib/video-recap/skills/video-script/scripts/review_runner.py +12 -11
- package/lib/video-recap/skills/video-script/scripts/speech_ownership.py +89 -5
- package/lib/video-recap/skills/video-script/scripts/validate.py +26 -34
- package/lib/video-recap/skills/video-understanding/SKILL.md +29 -4
- package/lib/video-recap/skills/video-understanding/references/data-schema.md +5 -22
- package/lib/video-recap/skills/video-understanding/scripts/agent_text.py +24 -121
- package/lib/video-recap/skills/video-understanding/scripts/asr.py +11 -5
- package/lib/video-recap/skills/video-understanding/scripts/asr_timing_evidence.py +15 -24
- package/lib/video-recap/skills/video-understanding/scripts/briefing/builder.py +59 -25
- package/lib/video-recap/skills/video-understanding/scripts/briefing/context.py +39 -3
- package/lib/video-recap/skills/video-understanding/scripts/briefing/inputs.py +15 -27
- package/lib/video-recap/skills/video-understanding/scripts/briefing/timeline.py +136 -37
- package/lib/video-recap/skills/video-understanding/scripts/consolidate.py +121 -37
- package/lib/video-recap/skills/video-understanding/scripts/detect.py +118 -16
- package/lib/video-recap/skills/video-understanding/scripts/index_normalize.py +359 -0
- package/lib/video-recap/skills/video-understanding/scripts/lib.py +62 -21
- package/lib/video-recap/skills/video-understanding/scripts/storyboard.py +181 -51
- package/lib/video-recap/skills/video-understanding/scripts/timeline_fusion.py +0 -26
- package/lib/video-recap/skills/video-understanding/scripts/understanding_brief.py +37 -14
- package/lib/video-recap/skills/video-understanding/scripts/understanding_cache.py +40 -32
- package/lib/video-recap/skills/video-understanding/scripts/understanding_runner.py +100 -127
- package/lib/video-recap/skills/video-understanding/scripts/understanding_storyboard.py +182 -30
- package/lib/video-recap/skills/video-understanding/scripts/vlm.py +30 -27
- package/lib/video-recap/skills/video-voiceover/SKILL.md +51 -18
- package/lib/video-recap/skills/video-voiceover/references/index-tts.md +3 -3
- package/lib/video-recap/skills/video-voiceover/scripts/approved_text_policy.py +3 -1
- package/lib/video-recap/skills/video-voiceover/scripts/dub.py +81 -135
- package/lib/video-recap/skills/video-voiceover/scripts/lib.py +9 -34
- package/lib/video-recap/skills/video-voiceover/scripts/providers/fish_audio.py +6 -1
- package/lib/video-recap/skills/video-voiceover/scripts/tts_audio.py +64 -1
- package/lib/video-recap/skills/video-voiceover/scripts/tts_cache.py +99 -0
- package/lib/video-recap/skills/video-voiceover/scripts/voiceover.py +177 -200
- package/package.json +45 -45
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/group/inner_court_accord_five.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/group/public_day15.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/household/li_jiaoer.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/css/new-guofeng.css +0 -1494
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/assets.js +0 -147
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/data.js +0 -7470
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/engine.js +0 -13066
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/text.js +0 -32
- package/lib/novel-to-game/examples/jin-ping-mei/qa/playable-model.json +0 -99
- package/lib/novel-to-game/examples/jin-ping-mei/qa/verify_playable_model.mjs +0 -152
- package/lib/video-recap/skills/video-assemble/references/foreground-compose.md +0 -118
- package/lib/video-recap/skills/video-assemble/references/jianying/empty_jy_combination_segment.json +0 -84
- package/lib/video-recap/skills/video-assemble/references/jianying/empty_jy_combination_video_material.json +0 -98
- package/lib/video-recap/skills/video-assemble/references/jianying/empty_jy_draft.json +0 -204
- package/lib/video-recap/skills/video-assemble/references/pair-media.md +0 -74
- package/lib/video-recap/skills/video-assemble/scripts/compose_foreground.py +0 -293
- package/lib/video-recap/skills/video-assemble/scripts/pair_media.py +0 -216
- package/lib/video-recap/skills/video-recap/references/shift-left-qc-schema.md +0 -23
- package/lib/video-recap/skills/video-recap/scripts/mimo_qc.py +0 -33
- package/lib/video-recap/skills/video-recap/scripts/qc/__init__.py +0 -1
- package/lib/video-recap/skills/video-recap/scripts/qc/mimo_client.py +0 -13
- package/lib/video-recap/skills/video-recap/scripts/qc/mimo_contract.py +0 -9
- package/lib/video-recap/skills/video-recap/scripts/qc/mimo_evidence.py +0 -287
- package/lib/video-recap/skills/video-recap/scripts/qc/mimo_observations.py +0 -175
- package/lib/video-recap/skills/video-recap/scripts/qc/mimo_payload.py +0 -206
- package/lib/video-recap/skills/video-recap/scripts/qc/mimo_report.py +0 -318
- package/lib/video-recap/skills/video-recap/scripts/qc/mimo_runner.py +0 -130
- package/lib/video-recap/skills/video-recap/scripts/qc_contract.py +0 -320
- package/lib/video-recap/skills/video-script/scripts/timeline_fusion.py +0 -108
- package/lib/video-recap/skills/video-understanding/scripts/brief.py +0 -9
- package/lib/video-recap/skills/video-understanding/scripts/deslop_qc.py +0 -211
- package/lib/video-recap/skills/video-understanding/scripts/narration_lint.py +0 -707
- package/lib/video-recap/skills/video-understanding/scripts/speech_ownership.py +0 -143
- /package/lib/video-recap/skills/video-cut/scripts/{narration_mapping.py → cut_qc.py} +0 -0
|
@@ -5,21 +5,26 @@ timing and text checks live here, and every later consumer trusts its result.
|
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
import json
|
|
8
|
-
import math
|
|
9
8
|
from pathlib import Path
|
|
10
9
|
|
|
11
10
|
from lib import CONFIG, log
|
|
12
11
|
from agent_text import (
|
|
13
|
-
|
|
12
|
+
TTS_UTTERANCE_OVERHEAD_SECONDS,
|
|
14
13
|
_find_scene_for_midpoint,
|
|
15
|
-
|
|
16
|
-
_post_dedup_narration,
|
|
17
|
-
_recommended_char_budget,
|
|
14
|
+
_lint_char_budget,
|
|
18
15
|
_scene_available_seconds,
|
|
19
16
|
_text_char_count,
|
|
20
|
-
_truncate_at_sentence,
|
|
21
17
|
)
|
|
22
18
|
from deslop_qc import analyze_deslop_qc
|
|
19
|
+
from entry_suggestion import (
|
|
20
|
+
SUGGESTED_ANCHOR_MAX_SHIFT_SECONDS,
|
|
21
|
+
_has_connected_predecessor,
|
|
22
|
+
_is_number,
|
|
23
|
+
_nearest_safe_anchor,
|
|
24
|
+
_other_block_spans,
|
|
25
|
+
_suggestion_window,
|
|
26
|
+
)
|
|
27
|
+
from lint_summary import format_lint_failure, format_lint_warnings
|
|
23
28
|
from speech_ownership import (
|
|
24
29
|
entry_overlaps_source_speech,
|
|
25
30
|
load_source_sentence_evidence,
|
|
@@ -32,14 +37,6 @@ def _lint_issue(level, index, code, message, **extra):
|
|
|
32
37
|
return issue
|
|
33
38
|
|
|
34
39
|
|
|
35
|
-
def _is_number(value):
|
|
36
|
-
return (
|
|
37
|
-
isinstance(value, (int, float))
|
|
38
|
-
and not isinstance(value, bool)
|
|
39
|
-
and math.isfinite(value)
|
|
40
|
-
)
|
|
41
|
-
|
|
42
|
-
|
|
43
40
|
def _visual_overlay_issues(index, seg):
|
|
44
41
|
"""Shape-check the renderer handoff recap reads verbatim from a validated segment.
|
|
45
42
|
|
|
@@ -134,11 +131,16 @@ def _clip_matches_for_segment(seg, clip_plan, requested_clip_id):
|
|
|
134
131
|
]
|
|
135
132
|
|
|
136
133
|
|
|
137
|
-
def _source_sentence_entry_issue(
|
|
134
|
+
def _source_sentence_entry_issue(
|
|
135
|
+
index, start, end, anchors, speech_owned, other_spans=(), window=(None, None),
|
|
136
|
+
):
|
|
138
137
|
"""Return a blocking issue when narration enters midway through source speech.
|
|
139
138
|
|
|
140
139
|
The validator reports a correction instead of silently moving audio. Source
|
|
141
140
|
sentence integrity is invariant; an editorial policy string cannot bypass it.
|
|
141
|
+
`other_spans` are the other blocks' (start, end) and `window` the open range between
|
|
142
|
+
the neighbours (see _nearest_safe_anchor): the suggested entry never moves this block
|
|
143
|
+
onto, flush against, or past one of them.
|
|
142
144
|
"""
|
|
143
145
|
if not speech_owned:
|
|
144
146
|
return None
|
|
@@ -165,45 +167,81 @@ def _source_sentence_entry_issue(index, start, anchors, speech_owned):
|
|
|
165
167
|
if pause_start - 0.05 <= start <= when + 0.08:
|
|
166
168
|
return None
|
|
167
169
|
|
|
168
|
-
suggested =
|
|
169
|
-
(anchor for anchor in anchors if anchor["time"] > start + 0.08), None
|
|
170
|
-
)
|
|
170
|
+
suggested = _nearest_safe_anchor(start, end, anchors, other_spans, window)
|
|
171
171
|
return _lint_issue(
|
|
172
172
|
"error",
|
|
173
173
|
index,
|
|
174
174
|
"interrupts_source_sentence",
|
|
175
|
-
"Narration enters before the source sentence finishes. Move
|
|
176
|
-
"sentence-end anchor, or shorten/move/remove it when
|
|
177
|
-
"
|
|
175
|
+
"Narration enters before the source sentence finishes. Move the whole block (same "
|
|
176
|
+
"duration) to the suggested sentence-end anchor, or shorten/move/remove it when no "
|
|
177
|
+
f"anchor within {SUGGESTED_ANCHOR_MAX_SHIFT_SECONDS:g}s keeps it between its neighbours "
|
|
178
|
+
"and clear of the other blocks, then rerun lint before TTS. The window stays inside the "
|
|
179
|
+
"block's clip (cut) or the output duration (cut_output); full mode does not know the "
|
|
180
|
+
"video's end, so keep it inside the video. Source sentence interruption has no override.",
|
|
178
181
|
entry_time=round(start, 3),
|
|
179
182
|
suggested_start=round(suggested["time"], 3) if suggested else None,
|
|
183
|
+
suggested_end=round(suggested["time"] + end - start, 3) if suggested else None,
|
|
184
|
+
max_shift_seconds=SUGGESTED_ANCHOR_MAX_SHIFT_SECONDS,
|
|
180
185
|
source_text_tail=str(suggested.get("text_tail", "")).strip() if suggested else "",
|
|
181
186
|
anchor_confidence=suggested["confidence"] if suggested else None,
|
|
187
|
+
anchor_boundary_use=(
|
|
188
|
+
suggested.get("boundary_use")
|
|
189
|
+
or ("unverified" if suggested["confidence"] in {"high", "medium"} else "none")
|
|
190
|
+
)
|
|
191
|
+
if suggested
|
|
192
|
+
else None,
|
|
182
193
|
)
|
|
183
194
|
|
|
184
195
|
|
|
185
|
-
def
|
|
186
|
-
"""
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
196
|
+
def _suggestion_limits(seg, clip_plan, mode, output_duration):
|
|
197
|
+
"""Closed range a suggested move must stay in: the block's one clip (cut mode, source
|
|
198
|
+
time), or [0, output_duration] when that is known (cut_output)."""
|
|
199
|
+
if mode == "cut" and clip_plan:
|
|
200
|
+
requested = seg.get("source_clip_id")
|
|
201
|
+
try:
|
|
202
|
+
requested = None if requested is None else int(requested)
|
|
203
|
+
except (TypeError, ValueError):
|
|
204
|
+
return None, None
|
|
205
|
+
matches = _clip_matches_for_segment(seg, clip_plan, requested)
|
|
206
|
+
return _clip_span(matches[0]) if len(matches) == 1 else (None, None)
|
|
207
|
+
usable = _is_number(output_duration) and output_duration > 0
|
|
208
|
+
return 0.0, output_duration if usable else None
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
# Full-mode hard text budget: a segment longer than this multiple of its recommended
|
|
212
|
+
# character budget cannot be spoken in its window, so lint returns it to the author
|
|
213
|
+
# instead of anything downstream shortening it.
|
|
214
|
+
OVER_BUDGET_ERROR_RATIO = 1.25
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def _over_budget_error(index, start, end, char_count, budget):
|
|
218
|
+
limit = int(budget * OVER_BUDGET_ERROR_RATIO)
|
|
219
|
+
return _lint_issue(
|
|
220
|
+
"error",
|
|
221
|
+
index,
|
|
222
|
+
"over_budget",
|
|
223
|
+
f"段 {index + 1} [{start:.2f}-{end:.2f}s] has {char_count} chars; the window holds "
|
|
224
|
+
f"about {budget} (hard limit {limit}), {char_count - limit} over the limit. "
|
|
225
|
+
"Shorten the text or widen/move the window, then rerun validate; "
|
|
226
|
+
"validation never shortens approved text.",
|
|
227
|
+
start=start,
|
|
228
|
+
end=end,
|
|
229
|
+
budget_chars=budget,
|
|
230
|
+
limit_chars=limit,
|
|
231
|
+
actual_chars=char_count,
|
|
232
|
+
over_chars=char_count - limit,
|
|
233
|
+
tts_overhead_seconds=TTS_UTTERANCE_OVERHEAD_SECONDS,
|
|
234
|
+
)
|
|
197
235
|
|
|
198
236
|
|
|
199
237
|
def lint_narration(
|
|
200
238
|
narration, scenes_analysis=None, *, clip_plan=None, mode="full", work_dir=None,
|
|
201
|
-
|
|
239
|
+
output_duration=None,
|
|
202
240
|
):
|
|
203
241
|
"""Preflight-check agent narration before TTS; write narration_lint.json when work_dir is set.
|
|
204
242
|
|
|
205
|
-
Segments
|
|
206
|
-
|
|
243
|
+
Segments must already be in chronological order; validation never reorders them.
|
|
244
|
+
`output_duration` (cut_output) bounds where interrupt suggestions may move a block."""
|
|
207
245
|
scenes_analysis = scenes_analysis or []
|
|
208
246
|
errors = []
|
|
209
247
|
warnings = []
|
|
@@ -221,6 +259,8 @@ def lint_narration(
|
|
|
221
259
|
)
|
|
222
260
|
else:
|
|
223
261
|
previous_start = None
|
|
262
|
+
# Suggested (start, end) of blocks already told to move; later blocks keep clear.
|
|
263
|
+
suggested_spans = {}
|
|
224
264
|
for idx, seg in enumerate(narration):
|
|
225
265
|
if not isinstance(seg, dict):
|
|
226
266
|
errors.append(
|
|
@@ -240,7 +280,7 @@ def lint_narration(
|
|
|
240
280
|
)
|
|
241
281
|
)
|
|
242
282
|
continue
|
|
243
|
-
if
|
|
283
|
+
if previous_start is not None and start < previous_start:
|
|
244
284
|
errors.append(
|
|
245
285
|
_lint_issue(
|
|
246
286
|
"error",
|
|
@@ -304,12 +344,18 @@ def lint_narration(
|
|
|
304
344
|
continue
|
|
305
345
|
|
|
306
346
|
char_count = _text_char_count(text)
|
|
307
|
-
budget =
|
|
347
|
+
budget = _lint_char_budget(start, end)
|
|
308
348
|
# estimate at the REAL playback rate (after the narration_speed atempo); otherwise a
|
|
309
|
-
# beat sized to its 1.3x-sped slot looks "over budget" when it actually fits.
|
|
349
|
+
# beat sized to its 1.3x-sped slot looks "over budget" when it actually fits. The
|
|
350
|
+
# utterance's TTS edge silence takes room too.
|
|
310
351
|
play_rate = CONFIG["speech_rate"] * CONFIG["narration_speed"]
|
|
311
|
-
estimated_tts_seconds = char_count / play_rate
|
|
352
|
+
estimated_tts_seconds = char_count / play_rate + TTS_UTTERANCE_OVERHEAD_SECONDS
|
|
312
353
|
slot_seconds = _scene_available_seconds(start, end)
|
|
354
|
+
over_limit = (
|
|
355
|
+
mode == "full" and char_count > budget * OVER_BUDGET_ERROR_RATIO
|
|
356
|
+
)
|
|
357
|
+
if over_limit:
|
|
358
|
+
errors.append(_over_budget_error(idx, start, end, char_count, budget))
|
|
313
359
|
if budget < 5:
|
|
314
360
|
warnings.append(
|
|
315
361
|
_lint_issue(
|
|
@@ -322,7 +368,9 @@ def lint_narration(
|
|
|
322
368
|
budget_chars=budget,
|
|
323
369
|
)
|
|
324
370
|
)
|
|
325
|
-
|
|
371
|
+
# Independent of slot_too_short: a short slot can also be overfilled, and the
|
|
372
|
+
# author needs both numbers.
|
|
373
|
+
if not over_limit and estimated_tts_seconds > slot_seconds:
|
|
326
374
|
warnings.append(
|
|
327
375
|
_lint_issue(
|
|
328
376
|
"warning",
|
|
@@ -352,15 +400,25 @@ def lint_narration(
|
|
|
352
400
|
entry_issue = _source_sentence_entry_issue(
|
|
353
401
|
idx,
|
|
354
402
|
start,
|
|
403
|
+
end,
|
|
355
404
|
source_sentence_anchors,
|
|
356
405
|
entry_overlaps_source_speech(
|
|
357
406
|
start,
|
|
358
407
|
source_evidence,
|
|
359
408
|
authored_overlap=seg.get("overlaps_speech", True),
|
|
360
409
|
),
|
|
410
|
+
_other_block_spans(narration, idx),
|
|
411
|
+
_suggestion_window(
|
|
412
|
+
narration, idx, suggested_spans,
|
|
413
|
+
_suggestion_limits(seg, clip_plan, mode, output_duration),
|
|
414
|
+
),
|
|
361
415
|
)
|
|
362
416
|
if entry_issue:
|
|
363
417
|
errors.append(entry_issue)
|
|
418
|
+
if entry_issue.get("suggested_end") is not None:
|
|
419
|
+
suggested_spans[idx] = (
|
|
420
|
+
entry_issue["suggested_start"], entry_issue["suggested_end"],
|
|
421
|
+
)
|
|
364
422
|
|
|
365
423
|
scene_bounds = _scene_bounds_for_midpoint(scenes_analysis, start, end)
|
|
366
424
|
if scenes_analysis and not scene_bounds:
|
|
@@ -404,6 +462,7 @@ def lint_narration(
|
|
|
404
462
|
end=end,
|
|
405
463
|
duration=round(end - start, 2),
|
|
406
464
|
frame_fact_times=[round(ts, 2) for ts in frame_fact_times[:8]],
|
|
465
|
+
frame_fact_count=len(frame_fact_times),
|
|
407
466
|
)
|
|
408
467
|
)
|
|
409
468
|
|
|
@@ -603,11 +662,9 @@ def lint_narration(
|
|
|
603
662
|
)
|
|
604
663
|
)
|
|
605
664
|
|
|
665
|
+
# Unfiltered, so narration-sourced blocker indices are narration.json positions.
|
|
606
666
|
deslop_qc = analyze_deslop_qc(
|
|
607
|
-
|
|
608
|
-
if isinstance(narration, list)
|
|
609
|
-
else [],
|
|
610
|
-
work_dir=work_dir,
|
|
667
|
+
narration if isinstance(narration, list) else [], work_dir=work_dir,
|
|
611
668
|
)
|
|
612
669
|
for blocker in deslop_qc["blockers"]:
|
|
613
670
|
errors.append(
|
|
@@ -632,76 +689,39 @@ def lint_narration(
|
|
|
632
689
|
"warnings": warnings,
|
|
633
690
|
}
|
|
634
691
|
if work_dir is not None:
|
|
635
|
-
Path(work_dir, "deslop_qc.json").write_text(
|
|
636
|
-
json.dumps(deslop_qc, ensure_ascii=False, indent=2), encoding="utf-8"
|
|
637
|
-
)
|
|
638
692
|
Path(work_dir, "narration_lint.json").write_text(
|
|
639
693
|
json.dumps(report, ensure_ascii=False, indent=2), encoding="utf-8"
|
|
640
694
|
)
|
|
641
695
|
return report
|
|
642
696
|
|
|
643
697
|
|
|
698
|
+
class NarrationLintError(ValueError):
|
|
699
|
+
"""Lint found blocking errors: an ordinary result for the author to fix, not a crash.
|
|
700
|
+
|
|
701
|
+
str() is the compact per-block summary; `report` is the full lint report."""
|
|
702
|
+
|
|
703
|
+
def __init__(self, report, lint_path=None):
|
|
704
|
+
self.report = report
|
|
705
|
+
super().__init__(format_lint_failure(report, lint_path))
|
|
706
|
+
|
|
707
|
+
|
|
644
708
|
def validate_narration_or_raise(
|
|
645
709
|
narration, scenes_analysis=None, *, clip_plan=None, mode="full", work_dir=None,
|
|
646
|
-
|
|
710
|
+
output_duration=None,
|
|
647
711
|
):
|
|
648
712
|
report = lint_narration(
|
|
649
713
|
narration, scenes_analysis, clip_plan=clip_plan, mode=mode, work_dir=work_dir,
|
|
650
|
-
|
|
714
|
+
output_duration=output_duration,
|
|
651
715
|
)
|
|
652
716
|
if report["errors"]:
|
|
653
|
-
|
|
654
|
-
|
|
717
|
+
raise NarrationLintError(
|
|
718
|
+
report, None if work_dir is None else Path(work_dir, "narration_lint.json")
|
|
655
719
|
)
|
|
656
|
-
raise ValueError(f"narration.json 预检失败: {sample}; 详见 narration_lint.json")
|
|
657
720
|
if report["warnings"]:
|
|
658
|
-
log(
|
|
659
|
-
|
|
660
|
-
)
|
|
721
|
+
log(format_lint_warnings(
|
|
722
|
+
report, None if work_dir is None else Path(work_dir, "narration_lint.json")
|
|
723
|
+
))
|
|
661
724
|
else:
|
|
662
|
-
log("narration lint
|
|
725
|
+
log("narration lint:通过")
|
|
663
726
|
return report
|
|
664
727
|
|
|
665
|
-
|
|
666
|
-
def _validate_narration_budget(narration, scenes_analysis):
|
|
667
|
-
"""Trim lint-validated narration to its timing budgets; drop what cannot be spoken."""
|
|
668
|
-
del scenes_analysis # scene boundaries are advisory (lint warns); authored timing is kept
|
|
669
|
-
cleaned = []
|
|
670
|
-
for raw in narration:
|
|
671
|
-
item = _normalise_narration_segment(raw)
|
|
672
|
-
max_chars = _recommended_char_budget(item["start"], item["end"])
|
|
673
|
-
if max_chars < 5:
|
|
674
|
-
log(f" 丢弃过短解说段 {item['start']:.1f}-{item['end']:.1f}s")
|
|
675
|
-
continue
|
|
676
|
-
if _text_char_count(item["narration"]) > max_chars * 1.25:
|
|
677
|
-
truncated = _truncate_at_sentence(item["narration"], max_chars)
|
|
678
|
-
if truncated and _text_char_count(truncated) >= 5:
|
|
679
|
-
log(f" 解说超预算,已截短: {item['start']:.1f}-{item['end']:.1f}s")
|
|
680
|
-
item["narration"] = truncated
|
|
681
|
-
else:
|
|
682
|
-
log(
|
|
683
|
-
f" 解说超预算且无法安全截断,已丢弃: {item['start']:.1f}-{item['end']:.1f}s"
|
|
684
|
-
)
|
|
685
|
-
continue
|
|
686
|
-
item["narration"] = _clean_narration_punctuation(item["narration"])
|
|
687
|
-
stripped = item["narration"].strip()
|
|
688
|
-
if stripped and stripped[-1] in ",:、;,—":
|
|
689
|
-
item["narration"] = stripped.rstrip(",:、;,—") + "。"
|
|
690
|
-
cleaned.append(item)
|
|
691
|
-
|
|
692
|
-
cleaned.sort(key=lambda n: n["start"])
|
|
693
|
-
deduped = []
|
|
694
|
-
for item in cleaned:
|
|
695
|
-
if deduped and item["start"] < deduped[-1]["end"]:
|
|
696
|
-
prev = deduped[-1]
|
|
697
|
-
log(
|
|
698
|
-
f" 解说时间重叠: {item['start']:.1f}-{item['end']:.1f}s vs "
|
|
699
|
-
f"{prev['start']:.1f}-{prev['end']:.1f}s"
|
|
700
|
-
)
|
|
701
|
-
if _text_char_count(item["narration"]) > _text_char_count(
|
|
702
|
-
prev["narration"]
|
|
703
|
-
):
|
|
704
|
-
deduped[-1] = item
|
|
705
|
-
else:
|
|
706
|
-
deduped.append(item)
|
|
707
|
-
return _post_dedup_narration(deduped)
|
|
@@ -1,40 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
2
|
+
"""CLI entrypoint for narration review."""
|
|
3
3
|
|
|
4
|
-
from
|
|
5
|
-
build_evidence_bundle,
|
|
6
|
-
build_review_coverage_metadata,
|
|
7
|
-
coverage_policy_v1,
|
|
8
|
-
filter_evidence_by_ranges,
|
|
9
|
-
render_evidence_bundle,
|
|
10
|
-
)
|
|
11
|
-
from review_grounding import remap_grounding_to_output_timeline
|
|
12
|
-
from review_response import (
|
|
13
|
-
build_grounding_qc,
|
|
14
|
-
build_review_messages,
|
|
15
|
-
format_review_md,
|
|
16
|
-
merge_review_findings,
|
|
17
|
-
parse_review_response,
|
|
18
|
-
write_grounding_qc,
|
|
19
|
-
)
|
|
20
|
-
from review_runner import main, review_narration
|
|
4
|
+
from review_runner import main
|
|
21
5
|
|
|
22
|
-
__all__ = [
|
|
23
|
-
"build_evidence_bundle",
|
|
24
|
-
"build_grounding_qc",
|
|
25
|
-
"build_review_coverage_metadata",
|
|
26
|
-
"build_review_messages",
|
|
27
|
-
"coverage_policy_v1",
|
|
28
|
-
"filter_evidence_by_ranges",
|
|
29
|
-
"format_review_md",
|
|
30
|
-
"main",
|
|
31
|
-
"merge_review_findings",
|
|
32
|
-
"parse_review_response",
|
|
33
|
-
"remap_grounding_to_output_timeline",
|
|
34
|
-
"render_evidence_bundle",
|
|
35
|
-
"review_narration",
|
|
36
|
-
"write_grounding_qc",
|
|
37
|
-
]
|
|
6
|
+
__all__ = ["main"]
|
|
38
7
|
|
|
39
8
|
if __name__ == "__main__":
|
|
40
9
|
main()
|
|
@@ -50,44 +50,30 @@ def _load_review_grounding(work_dir):
|
|
|
50
50
|
"""
|
|
51
51
|
work_dir = Path(work_dir)
|
|
52
52
|
manifest = _load(work_dir, "multi_source_manifest.json")
|
|
53
|
-
|
|
54
|
-
if isinstance(sources, list):
|
|
53
|
+
if manifest is not None:
|
|
55
54
|
combined_vlm = []
|
|
56
55
|
combined_asr = []
|
|
57
|
-
for source in sources:
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
source_id = str(source.get("source_id") or "").strip()
|
|
61
|
-
relative_dir = source.get("source_work_dir")
|
|
62
|
-
if not source_id or not relative_dir:
|
|
63
|
-
continue
|
|
64
|
-
source_dir = (work_dir / str(relative_dir)).resolve(strict=False)
|
|
56
|
+
for source in manifest["sources"]:
|
|
57
|
+
source_id = str(source["source_id"])
|
|
58
|
+
source_dir = (work_dir / source["source_work_dir"]).resolve(strict=False)
|
|
65
59
|
try:
|
|
66
60
|
source_dir.relative_to(work_dir.resolve(strict=False))
|
|
67
61
|
except ValueError:
|
|
68
62
|
continue
|
|
69
63
|
for scene in _load(source_dir, "vlm_analysis.json") or []:
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
combined_vlm.append(item)
|
|
74
|
-
clean_asr = _load(source_dir, "asr_clean.json")
|
|
75
|
-
raw_asr = (
|
|
76
|
-
clean_asr
|
|
77
|
-
if clean_asr is not None
|
|
78
|
-
else _load(source_dir, "asr_result.json")
|
|
79
|
-
)
|
|
80
|
-
for segment in _asr_segments(raw_asr):
|
|
81
|
-
if isinstance(segment, dict):
|
|
82
|
-
item = dict(segment)
|
|
83
|
-
item["source_id"] = source_id
|
|
84
|
-
combined_asr.append(item)
|
|
64
|
+
combined_vlm.append({**scene, "source_id": source_id})
|
|
65
|
+
for segment in _preferred_asr(source_dir):
|
|
66
|
+
combined_asr.append({**segment, "source_id": source_id})
|
|
85
67
|
if combined_vlm or combined_asr:
|
|
86
68
|
return combined_vlm, combined_asr
|
|
87
69
|
|
|
88
|
-
return _load(work_dir, "vlm_analysis.json") or [],
|
|
89
|
-
|
|
90
|
-
|
|
70
|
+
return _load(work_dir, "vlm_analysis.json") or [], _preferred_asr(work_dir)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _preferred_asr(work_dir):
|
|
74
|
+
"""asr_clean.json wins over asr_result.json, as in lint, cut and assemble."""
|
|
75
|
+
clean = _load(work_dir, "asr_clean.json")
|
|
76
|
+
return _asr_segments(clean if clean is not None else _load(work_dir, "asr_result.json"))
|
|
91
77
|
|
|
92
78
|
|
|
93
79
|
def _load_cut_clip_spans(work_dir):
|
|
@@ -176,13 +162,8 @@ def remap_grounding_to_output_timeline(vlm_analysis, asr_result, clip_spans):
|
|
|
176
162
|
|
|
177
163
|
remapped_scenes = []
|
|
178
164
|
for scene in vlm_analysis or []:
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
try:
|
|
182
|
-
start = float(scene.get("start", 0))
|
|
183
|
-
end = float(scene.get("end", start))
|
|
184
|
-
except (TypeError, ValueError):
|
|
185
|
-
continue
|
|
165
|
+
start = float(scene["start"])
|
|
166
|
+
end = float(scene["end"])
|
|
186
167
|
overlaps = _source_output_overlaps(
|
|
187
168
|
start,
|
|
188
169
|
end,
|
|
@@ -205,13 +186,8 @@ def remap_grounding_to_output_timeline(vlm_analysis, asr_result, clip_spans):
|
|
|
205
186
|
|
|
206
187
|
remapped_asr = []
|
|
207
188
|
for seg in asr_result or []:
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
try:
|
|
211
|
-
start = float(seg.get("start", 0))
|
|
212
|
-
end = float(seg.get("end", start))
|
|
213
|
-
except (TypeError, ValueError):
|
|
214
|
-
continue
|
|
189
|
+
start = float(seg["start"])
|
|
190
|
+
end = float(seg["end"])
|
|
215
191
|
text = str(seg.get("text", "")).strip()
|
|
216
192
|
if not text:
|
|
217
193
|
continue
|
|
@@ -231,8 +207,6 @@ def remap_grounding_to_output_timeline(vlm_analysis, asr_result, clip_spans):
|
|
|
231
207
|
item["output_segment_index"] = overlap.get("output_segment_index")
|
|
232
208
|
remapped_asr.append(item)
|
|
233
209
|
|
|
234
|
-
remapped_scenes.sort(
|
|
235
|
-
|
|
236
|
-
)
|
|
237
|
-
remapped_asr.sort(key=lambda x: (float(x.get("start", 0)), float(x.get("end", 0))))
|
|
210
|
+
remapped_scenes.sort(key=lambda x: (x["start"], x["end"]))
|
|
211
|
+
remapped_asr.sort(key=lambda x: (x["start"], x["end"]))
|
|
238
212
|
return remapped_scenes, remapped_asr
|