@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.
- package/README.md +17 -11
- package/lib/client.js +85 -35
- package/lib/client.js.map +3 -3
- 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 +16 -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/drama-production-view.d.ts +11 -0
- 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/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 +108 -1
- package/lib/video-recap/README.en.md +18 -14
- package/lib/video-recap/README.md +18 -14
- 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 +24 -27
- package/lib/video-recap/skills/video-recap/references/audio-routing.md +8 -6
- package/lib/video-recap/skills/video-recap/references/config-playbook.md +8 -11
- 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 +49 -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 +86 -116
- 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 +9 -7
- 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 +2 -10
- 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 +19 -12
- 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 +60 -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
|
@@ -80,11 +80,15 @@
|
|
|
80
80
|
|
|
81
81
|
```json
|
|
82
82
|
{
|
|
83
|
-
"schema_version":
|
|
83
|
+
"schema_version": 2,
|
|
84
84
|
"sentence_anchors": [
|
|
85
85
|
{
|
|
86
86
|
"time": 5.809,
|
|
87
|
-
"confidence": "
|
|
87
|
+
"confidence": "low",
|
|
88
|
+
"boundary_use": "unverified",
|
|
89
|
+
"timing_bound_seconds": 9.778,
|
|
90
|
+
"timing_basis": "asr_window",
|
|
91
|
+
"alignment_error": 0.012,
|
|
88
92
|
"text_tail": "带你重走詹姆斯的二十一年。",
|
|
89
93
|
"pause_start": 5.222,
|
|
90
94
|
"pause_end": 5.809
|
|
@@ -93,26 +97,43 @@
|
|
|
93
97
|
}
|
|
94
98
|
```
|
|
95
99
|
|
|
100
|
+
ASR 时间只到窗口级,句末位置只能在窗口内估计。`timing_bound_seconds` 是停顿到窗口两端的较远距离,
|
|
101
|
+
即句末真实位置的最坏误差;`confidence` 取它与 `alignment_error` 的较大值(≤0.6 high,≤1.2 medium,否则 low)。
|
|
102
|
+
`boundary_use` 决定下游用不用:`verified`(high/medium)、`unverified`(窗口太粗但吸附误差 ≤1.2s,仍作门禁锚点,
|
|
103
|
+
brief 里标 `unverified ±N s`)、`none`(不用)。缺 `boundary_use` 的锚点来自 schema 1 的旧估计器:high/medium 视为 `unverified`、其余为 `none`;
|
|
104
|
+
理解阶段发现 `schema_version` 不是 2 时:有 `audio.wav` 就重新生成本文件;没有(素材库恢复)就按 `asr_result.json`
|
|
105
|
+
原地重标(锚点时间不变,记 `upgraded_from_schema: 1`),重标不了就保持原样。剪后输出时钟的
|
|
106
|
+
`speech_boundary_anchors_output.json` 里 `time` / `pause_start` / `pause_end` / `expected_time` 都是输出时钟,原片值在 `source_*` 字段。
|
|
107
|
+
|
|
96
108
|
当 `overlaps_speech=true` 且旁白不是从 0 秒冷开场时,`narration` lint 要求 `start`
|
|
97
|
-
贴近 `
|
|
98
|
-
`suggested_start` 与 `source_text_tail` 给 Agent
|
|
99
|
-
`
|
|
100
|
-
|
|
109
|
+
贴近 `boundary_use` 不为 `none` 的锚点。入口是否落在原声讲话里,与 cut 门禁用同一条语气词规则:只有语气词的 ASR 窗口("啊!"、"Hi.")不算讲话,只在紧挨真实对白的一侧保留 1 秒;文本为空或只有空白的 ASR 行只是时间证据,不算讲话也不留保护;有 `asr_clean.json` 时三处都以它为准,否则读 `asr_result.json`;assemble 的入口检查同样如此。否则在 TTS 前用 `interrupts_source_sentence` 阻断,并返回
|
|
110
|
+
`suggested_start` / `suggested_end` 与 `source_text_tail` 给 Agent 调整:建议的是入点前后
|
|
111
|
+
`max_shift_seconds`(10 秒)内离入点最近的锚点,整块按原时长平移过去后仍在前后两块之间(不越过相邻块),
|
|
112
|
+
既不与其他块重叠、也不与它们相接(间隔须大于 0.15 秒,否则就成了首尾相连的交接);前一块也收到建议时,
|
|
113
|
+
同时避开它原来和建议的时间窗。cut 模式下平移后仍在该块所属片段内,cut_output 下不超过 `--output-duration`
|
|
114
|
+
(full 模式不检查视频结尾)。距离相同时取后面的。常规块使用
|
|
115
|
+
`source_entry_policy: "sentence_boundary"`;原声语句完整性没有抢断 override。范围内没有这样的锚点时
|
|
116
|
+
`suggested_start` 为 `null`,Agent 必须移动、缩短或删除该旁白块。
|
|
101
117
|
|
|
102
118
|
剪辑模式第二阶段会另外生成 `speech_boundary_anchors_output.json`,把锚点、ASR 语音区间和
|
|
103
|
-
安静窗口映射到剪后 OUTPUT
|
|
119
|
+
安静窗口映射到剪后 OUTPUT 时间轴,避免拿原片时间检查剪后旁白。输出时钟上的值不越出所属片段:比片段入/出点早/晚不超过
|
|
120
|
+
0.05 秒、又没有别的片段播放的锚点钉在该段的入点或出点上(换算后落在 `[0, 成片时长]` 之外的直接丢掉),`source_time` 仍是实测的原片时间:
|
|
104
121
|
|
|
105
122
|
```json
|
|
106
123
|
{
|
|
107
124
|
"schema_version": 2,
|
|
108
125
|
"timeline": "cut_output",
|
|
126
|
+
"clip_plan_identity": {"size": 4970, "mtime_ns": 1790949817370765842},
|
|
109
127
|
"sentence_anchors": [{"time": 4.0, "pause_start": 3.8, "confidence": "high"}],
|
|
110
128
|
"speech_spans": [{"start": 0.0, "end": 3.8}],
|
|
111
129
|
"quiet_windows": [{"start": 3.8, "end": 4.1}]
|
|
112
130
|
}
|
|
113
131
|
```
|
|
114
132
|
|
|
115
|
-
|
|
133
|
+
`clip_plan_identity` 是写入时 `clip_plan_validated.json` 的 `{size, mtime_ns}`:video-script
|
|
134
|
+
校验要求它与当前文件相等,assemble 要求本文件不早于 `clip_plan_validated.json`。video-cut
|
|
135
|
+
对同一剪辑计划重跑(续跑时复用 `edited_source.mp4`)不会重写内容未变的 `clip_plan_validated.json`,
|
|
136
|
+
所以第二遍写下的证据在第三遍续跑时仍然有效;`clip_plan.json` 被重新保存后则必然重写。缺失、过期或畸形的
|
|
116
137
|
output 证据一律 fail closed,不能回退到原片时钟或信任 Agent 写入的
|
|
117
138
|
`overlaps_speech=false`。多来源剪辑的每条映射记录还保留 `source_id` 和原片起止时间。
|
|
118
139
|
|
|
@@ -140,6 +161,8 @@ output 证据一律 fail closed,不能回退到原片时钟或信任 Agent 写
|
|
|
140
161
|
]
|
|
141
162
|
```
|
|
142
163
|
|
|
164
|
+
cut 第二轮(已有 `edited_source.mp4`)时本文件与 `asr_writing_chunks.json` 都在 OUTPUT 时间轴上:被拆到多个片段的场景 `scene_id` 写作 `"3.1"`(0 起的原场景号加片段序号);VLM 文字里落在场景原片区间内的时间改写成输出时间,落在被剪掉部分的写 `[cut-away moment]`;只被剪进一部分的 ASR 窗口文字换成 `[partial ASR window: only part of it is in the cut, text withheld]`。`scene_id` 一律 0 起,brief 里给人看的场景号从 1 数。
|
|
165
|
+
|
|
143
166
|
## narration.json
|
|
144
167
|
|
|
145
168
|
Agent 撰写的解说词。full 模式下使用原视频时间;**orchestrated cut 模式(`video-recap --edit-mode cut`)下,第二次暂停时已经先剪出 `edited_source.mp4`,因此 `narration.json` 必须直接使用剪后成片的 OUTPUT 时间轴(0..成片总时长);不存在原视频时间→输出时间的旁白映射产物:
|
|
@@ -152,7 +175,7 @@ Agent 撰写的解说词。full 模式下使用原视频时间;**orchestrated
|
|
|
152
175
|
|
|
153
176
|
## narration_lint.json
|
|
154
177
|
|
|
155
|
-
|
|
178
|
+
续跑校验 `narration.json` 时生成的预检结果。它检查写稿、时间安全和解说覆盖。`metrics` 为 full 模式下的诊断指标(cut 模式为空对象),不是要求命中某个旁白比例的创作配额;低覆盖 warning 应回到 `visual_audio_board.json` 检查是否为有意的原声/沉默选择。
|
|
156
179
|
|
|
157
180
|
```json
|
|
158
181
|
{
|
|
@@ -168,7 +191,8 @@ Agent 撰写的解说词。full 模式下使用原视频时间;**orchestrated
|
|
|
168
191
|
"original_block_count": 4
|
|
169
192
|
},
|
|
170
193
|
"errors": [
|
|
171
|
-
{"level": "error", "index": 2, "code": "time_overlap", "message": "Segment overlaps the previous narration segment"}
|
|
194
|
+
{"level": "error", "index": 2, "code": "time_overlap", "message": "Segment overlaps the previous narration segment"},
|
|
195
|
+
{"level": "error", "index": 5, "code": "over_budget", "budget_chars": 28, "limit_chars": 35, "actual_chars": 42, "over_chars": 7, "tts_overhead_seconds": 0.45}
|
|
172
196
|
],
|
|
173
197
|
"warnings": [
|
|
174
198
|
{"level": "warning", "index": 0, "code": "over_budget", "budget_chars": 28, "actual_chars": 42}
|
|
@@ -176,7 +200,9 @@ Agent 撰写的解说词。full 模式下使用原视频时间;**orchestrated
|
|
|
176
200
|
}
|
|
177
201
|
```
|
|
178
202
|
|
|
179
|
-
|
|
203
|
+
推荐字数 `budget_chars` 先从时间窗扣掉 `tts_overhead_seconds`(约 0.45 秒,每块一次 TTS 合成自带的首尾静音)再折算,所以比 brief 里每个窗口标的字数少一两个字。full 模式下字数超过推荐字数 1.25 倍(`limit_chars`)的段是 `over_budget` error(校验不会替 Agent 截短);略超实际朗读时长但未过硬上限、以及 cut_output 的超预算仍是 warning。段落必须按 `start` 排序,否则报 `out_of_order` error。
|
|
204
|
+
|
|
205
|
+
常见 code:`invalid_time`、`out_of_order`、`empty_narration`、`time_overlap`、`outside_clip_plan`、`over_budget`、`incomplete_sentence`、`slot_too_short`、`under_narrated`、`over_narrated`、`fragmented_beats`、`no_original_blocks`。
|
|
180
206
|
|
|
181
207
|
## recap_story_plan.json / visual_audio_board.json(Agent 创作工作产物)
|
|
182
208
|
|
|
@@ -217,42 +243,24 @@ CLI 不以它们作为渲染硬门禁,也不新增解析服务;建议型解
|
|
|
217
243
|
}
|
|
218
244
|
```
|
|
219
245
|
|
|
220
|
-
##
|
|
221
|
-
|
|
222
|
-
`deslop_qc_requirements.json` 是 tool/brief generated run contract:工具或 brief 生成本次运行的 QC 要求,供 `deslop_qc` 读取,不由 Agent 手写。字段为 `schema_version` 与 `style_card_required`。
|
|
246
|
+
## narration_lint.json 的 deslop_qc(CLI 生成,报告型 QC)
|
|
223
247
|
|
|
224
|
-
`
|
|
225
|
-
|
|
226
|
-
该契约不改变 `--style`:`--style` 仍是 freeform verbatim guidance,不增加固定风格档位。它也不改变 `deslop_qc` 边界:仍然是 report-only,不是 AIGC detector,不自动改写。
|
|
227
|
-
|
|
228
|
-
最小示例:
|
|
229
|
-
|
|
230
|
-
```json
|
|
231
|
-
{
|
|
232
|
-
"schema_version": 1,
|
|
233
|
-
"style_card_required": false
|
|
234
|
-
}
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
## deslop_qc.json(CLI 生成,报告型 QC)
|
|
238
|
-
|
|
239
|
-
`deslop_qc.json` 由本地 deterministic scanner 生成,Agent 不手写。它只是 report-only QC:不是 AIGC detector,不判断文本是不是 AI 写的,不会自动改写。修改仍由 Agent/人工根据报告回到 `narration.json`、`style_card.json` 或字幕源里处理。
|
|
248
|
+
`narration_lint.json` 的 `deslop_qc` 字段由本地 deterministic scanner 生成,Agent 不手写;不再单独写 `deslop_qc.json`。它只是 report-only QC:不是 AIGC detector,不判断文本是不是 AI 写的,不会自动改写。修改仍由 Agent/人工根据报告回到 `narration.json`、`style_card.json` 或字幕源里处理。
|
|
240
249
|
|
|
241
250
|
报告分两层:
|
|
242
251
|
|
|
243
|
-
- `blockers`:客观阻断项,会并入 `narration_lint.json` 的 error
|
|
244
|
-
- `advisories
|
|
252
|
+
- `blockers`:客观阻断项,会并入 `narration_lint.json` 的 error,例如破折号、占位符泄漏。
|
|
253
|
+
- `advisories`:建议项,只提示可读性/口语化风险,例如缺少或空的 `style_card.json`、模板化“不是……而是……”转折、套话密度、抽象总结词、解释链、比喻标记、过长段落;它们不自动阻断,也不自动改写。
|
|
245
254
|
|
|
246
255
|
```json
|
|
247
256
|
{
|
|
248
257
|
"ok": false,
|
|
249
258
|
"contract": "Local readability/QC report only: this is not an AIGC detector, does not claim AI-generation accuracy, and never rewrites text. Corrections remain human/agent rewrite work.",
|
|
250
259
|
"scanner": "deslop_qc.py",
|
|
251
|
-
"style_card_required": true,
|
|
252
260
|
"blocker_count": 1,
|
|
253
261
|
"advisory_count": 1,
|
|
254
262
|
"blockers": [
|
|
255
|
-
{"severity": "blocker", "code": "
|
|
263
|
+
{"severity": "blocker", "code": "em_dash", "source": "narration", "index": 3, "message": "破折号(—/——)不得出现在 narration/original_subtitles 中"}
|
|
256
264
|
],
|
|
257
265
|
"advisories": [
|
|
258
266
|
{"severity": "advisory", "code": "cliche_density", "source": "narration", "index": null, "message": "套话/高频抽象词偏密,建议换成具体行动、选择和后果"}
|
|
@@ -326,6 +334,7 @@ CLI 校验 `clip_plan.json` 后写出,额外包含输出时间轴:
|
|
|
326
334
|
"output_start": 0.0,
|
|
327
335
|
"output_end": 26.0,
|
|
328
336
|
"duration": 26.0,
|
|
337
|
+
"frame_count": 650,
|
|
329
338
|
"reason": "b01 | hook | knowledge: unknown→threat | POV=主角 | 保留倾听反应 | 入点=问题已问出 | 出点=沉默落地"
|
|
330
339
|
}
|
|
331
340
|
],
|
|
@@ -335,9 +344,30 @@ CLI 校验 `clip_plan.json` 后写出,额外包含输出时间轴:
|
|
|
335
344
|
```
|
|
336
345
|
|
|
337
346
|
`qc.boundary_status.sentence_checks` 逐项记录每个片段 start/end 是 `safe`、`unchecked`
|
|
338
|
-
还是 `blocking
|
|
339
|
-
|
|
340
|
-
|
|
347
|
+
还是 `blocking`(落在 `unverified` 句末锚点上的边界为 `safe`,`reason` 记 `unverified_sentence_boundary`)。理解阶段已有 ASR 讲话时间时,任何未落到源头/源尾、句末锚点/静音窗,且
|
|
348
|
+
不是同源无损连续连接(计划里相邻、`clip_id` 连续的同源片段)的边界都会写入 `qc.blocking[].code=unsafe_clip_sentence_boundary`。
|
|
349
|
+
被阻断的边界额外带 `nearest_safe`,给出前后 5 秒内最近的安全边界(原片秒,已是帧对齐后的落点、复核后仍安全;重跑时切镜吸附不会再把它拉回讲话内,见下文),没有则为 `null`:
|
|
350
|
+
|
|
351
|
+
```json
|
|
352
|
+
{"clip_id": 0, "edge": "start", "time": 3.0, "status": "blocking", "reason": "inside_detected_speech",
|
|
353
|
+
"nearest_safe": {"before": {"time": 0.0, "reason": "source_start", "delta": -3.0},
|
|
354
|
+
"after": {"time": 4.8, "reason": "sentence_or_quiet_boundary", "delta": 1.8}}}
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
切镜吸附先执行,句末吸附随后执行,保证视觉边界不会覆盖声音安全边界;附近没有停顿窗、句末吸附修不回来时,被切镜吸附移进讲话的边界若原位置能过门禁,就撤回原位置(不与其他片段重叠时),`qc.boundary_status.shot_snaps` 对应项记 `end_action`/`start_action: "reverted_unsafe"`、`rejected_end`/`rejected_start`(被撤回的切点)与 `end_revert_reason`/`start_revert_reason`;最后把入点对齐到源帧网格、
|
|
358
|
+
时长对齐到整数输出帧(只移动不到一帧,优先选门禁仍判为安全、仍在停顿内的一侧,也不会因此丢掉必保证据的边缘;两侧同样安全时选不跨过切镜扫描到的原片硬切的一侧,入点落在切点上或之后、出点落在切点上或之前,不闪一帧另一个镜头),
|
|
359
|
+
门禁检查对齐后的边界。每段的 `frame_count` 是它渲染的帧数,`edited_source.mp4` 因此是恒定帧率;
|
|
360
|
+
对齐前后的时间写在 `qc.boundary_status.frame_snaps`,汇总写在 `qc.frame_grid`:
|
|
361
|
+
|
|
362
|
+
```json
|
|
363
|
+
{"output_frame_rate": "25", "frame_count": 650, "duration": 26.0,
|
|
364
|
+
"sources": [{"source_id": null, "path": "/abs/source.mp4", "frame_rate": "25/1",
|
|
365
|
+
"video_start_offset": 0.0, "start_snapped": true}]}
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
单源时 `output_frame_rate` 沿用源的 `r_frame_rate`(隔行素材报场频、或可变帧率素材的标称帧率高出 `avg_frame_rate` 1.5 倍以上时,按实际帧率取),多源时是画布帧率(NTSC 写成 `30000/1001`)。
|
|
369
|
+
`output_start/end` 与 `total_duration` 按累计 `frame_count` 计算,与渲染的帧时间一致。
|
|
370
|
+
`start_snapped: false` 表示该源帧率未知(`0/0` 或大于 120),入点不动,时长仍对齐到整数输出帧。
|
|
341
371
|
|
|
342
372
|
多视频 validated clip 会额外保留来源字段,供 pass2 brief、timeline 和剪映导出追溯原素材:
|
|
343
373
|
|
|
@@ -353,6 +383,7 @@ CLI 校验 `clip_plan.json` 后写出,额外包含输出时间轴:
|
|
|
353
383
|
"output_start": 0.0,
|
|
354
384
|
"output_end": 26.0,
|
|
355
385
|
"duration": 26.0,
|
|
386
|
+
"frame_count": 780,
|
|
356
387
|
"reason": "b01 | hook | knowledge: unknown→threat | POV=主角 | 保留倾听反应 | 入点=问题已问出 | 出点=沉默落地"
|
|
357
388
|
}
|
|
358
389
|
]
|
|
@@ -374,8 +405,12 @@ CLI 校验 `clip_plan.json` 后写出,额外包含输出时间轴:
|
|
|
374
405
|
artifacts/asr_clean.json # 启用 --consolidate-asr 时保留,恢复后 brief/review 优先使用
|
|
375
406
|
artifacts/vlm_analysis.json
|
|
376
407
|
artifacts/understanding_index.json
|
|
408
|
+
artifacts/consolidation.status.json # 恢复后 brief-only 仍能提示 consolidate 失败/缺索引
|
|
377
409
|
```
|
|
378
410
|
|
|
411
|
+
`asr_timing_evidence.json` 不入库:它按 `{size, mtime_ns}` 绑定 `asr_result.json` 与 `audio.wav`,
|
|
412
|
+
恢复时前者被脱敏重写、后者不复制,恢复出的副本只会被判为 `MISSING_OR_STALE`。
|
|
413
|
+
|
|
379
414
|
`materials_index.jsonl` 每次保存追加一行,字段包括 `schema_version`, `event`, `material_id`, `source_name`, `source_path`, `source_video_identity`, `summary`, `tags`, `material_dir`, `updated_at`;`material.json` 另外记录分析 `settings` 字典与每个产物的 `bytes`。当前权威状态始终以 `materials/<material_id>/material.json` 为准。MVP 只承诺 `grep -R "关键词" <library>` 这类文件检索;没有 DB、embedding 或语义搜索。
|
|
380
415
|
|
|
381
416
|
保存时会对凭证形态(`tp-`/`sk-`/`gh*_`/`AKIA`/JWT 与 `KEY=VALUE` 赋值)和凭证命名的 JSON key 做脱敏,但这只是**尽力而为**的兜底,不是保证:陌生格式的密钥仍可能漏过。请从源头避免把密钥写进分析产物——key 从环境变量/`.env` 读取,不需要落进 scenes/ASR/VLM/summary 等 JSON。
|
|
@@ -427,28 +462,20 @@ CLI 校验 `clip_plan.json` 后写出,额外包含输出时间轴:
|
|
|
427
462
|
|
|
428
463
|
## narration_review.json
|
|
429
464
|
|
|
430
|
-
解说评审阶段输出 LLM-as-judge
|
|
465
|
+
解说评审阶段输出 LLM-as-judge 结果,只有 `verdict` / `summary` / `findings` 三项内容字段,外加运行记录 `evidence_contract`(时间轴、时钟、选中的证据区间、分块数、警告);有警告时另有顶层 `warnings`,分块评审时另有 `chunked_review` 计数,评审输出无法解析时另有 `parse_error` / `raw`。硬门禁是 `findings` 里的 error(事实矛盾/残句)经 `--require-narration-review` 严格模式拦截;`verdict` 只是建议信号,词表为 `PASS|REVISE|FAIL`,模型偶尔返回的 `OK` 按 `PASS` 记录。
|
|
431
466
|
|
|
432
467
|
```json
|
|
433
468
|
{
|
|
434
|
-
"verdict": "PASS|REVISE|FAIL
|
|
469
|
+
"verdict": "PASS|REVISE|FAIL",
|
|
435
470
|
"summary": "总体判断",
|
|
436
|
-
"
|
|
437
|
-
|
|
438
|
-
"stakes_escalation": 4, "information_gain": 4, "spoken_language": 4, "sentence_brevity": 4,
|
|
439
|
-
"tts_pacing": 4, "grounding": 4, "original_audio_use": 4, "subtitle_readability": 4
|
|
440
|
-
},
|
|
441
|
-
"hook_candidates_review": [{"candidate": "首句", "type": "suspense", "score": 4, "keep": true}],
|
|
442
|
-
"retention_risk_points": [{"time": "00:28", "risk": "信息重复可能掉人", "fix": "删掉复述画面的句子"}],
|
|
443
|
-
"highest_return_edits": ["最值得先改的一件事"],
|
|
444
|
-
"information_gain_notes": [{"segment": 0, "label": "motive|...|visual_restatement", "note": "证据/改法"}],
|
|
445
|
-
"spoken_language_rewrites": [{"segment": 0, "original": "原句", "rewrite": "口语改写", "why": "为什么更适合听"}],
|
|
446
|
-
"grounding_assertions": [{"segment": 0, "assertion": "人物/关系/因果断言", "source": "visual|asr|research|user_context|unsupported", "risk": "谨慎说明"}],
|
|
447
|
-
"findings": [{"segment": 0, "severity": "warning", "category": "weak_hook", "issue": "问题", "fix": "改法"}]
|
|
471
|
+
"findings": [{"segment": 0, "severity": "warning", "category": "weak_hook", "issue": "问题", "fix": "改法"}],
|
|
472
|
+
"evidence_contract": {"schema_version": 1, "timeline": "source", "clock": "source", "selected_ranges": [], "chunk_count": 1, "warnings": []}
|
|
448
473
|
}
|
|
449
474
|
```
|
|
450
475
|
|
|
451
|
-
> 若 `work_dir` 提供了 `packaging_plan.json` / `recap_story_plan.json` / `visual_audio_board.json`(可选、Agent 撰写),review
|
|
476
|
+
> 若 `work_dir` 提供了 `packaging_plan.json` / `recap_story_plan.json` / `visual_audio_board.json` / `style_card.json`(可选、Agent 撰写),review 会把它们并入评审上下文;缺失时 review 仅基于解说与画面/对白证据评审,行为不受影响。
|
|
477
|
+
|
|
478
|
+
`cut_output` 解说评审按 `source_id` 映射 `multi_source_manifest.json` 指向的逐源 VLM/ASR,避免项目根目录没有单一 ASR 文件时产生空证据。
|
|
452
479
|
|
|
453
480
|
## tts_meta.json(partial 失败可见性)
|
|
454
481
|
|
|
@@ -497,17 +524,29 @@ full / cut 流程(含本地采用路径)合成完成后,video-recap 在 `w
|
|
|
497
524
|
|
|
498
525
|
组装后,`assembly_manifest.json.audio_segments[]` 另外记录 `fit_status`、`truncated`、
|
|
499
526
|
`truncate_reason`、`placed_audio_duration`、`placed_audio_path`、`source_duck_end`、
|
|
500
|
-
`source_restore_at` 与 `
|
|
527
|
+
`source_restore_at`、`source_handoff_status`、`source_entry_status` 与 `written_start`。组装阶段从不按时间裁旁白尾音:放不下时用
|
|
501
528
|
`no_safe_fit` 阻断。`placed_audio_path` 是实际写入 canonical `narration.wav` 的完整逐段 PCM;
|
|
502
529
|
`timeline.json`/剪映必须引用它而不是更长的加速前文件。素材时长与序列化后的时间线段长不一致时,
|
|
503
|
-
`assembly_qc.json` 用 `timeline_audio_mismatch`
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
530
|
+
`assembly_qc.json` 用 `timeline_audio_mismatch` 阻断。旁白结束后原声最多再压低 3 秒,等这段时间内的
|
|
531
|
+
第一个句末锚点:压到它的 `pause_start`(`source_duck_end`),只在实测停顿内渐强,并于 `source_restore_at`
|
|
532
|
+
完成,避免渐强提前泄露上一句尾音。`source_handoff_status` 取值:`sentence_boundary`(已验证锚点)、
|
|
533
|
+
`sentence_boundary_unverified`(锚点为 `unverified`,含 schema 1 旧锚点)、`held_to_timeline_end`(3 秒内无锚点且离片尾
|
|
534
|
+
不足 3 秒,压到片尾)、`bounded_release`(3 秒内无锚点,在旁白结束处以 `duck_fade_seconds` 渐强回满,不阻断)、
|
|
535
|
+
`anchors_unavailable`(原声有讲话但没有可用锚点,阻断)、`no_source_speech`;入口状态在 `source_entry_status`
|
|
536
|
+
(`sentence_boundary` / `sentence_boundary_unverified` / `quiet_source` / `non_dialogue_source` / `unverified` /
|
|
537
|
+
`unsafe_entry` / `anchors_unavailable` / `paragraph_tightened`)。每个压低段只给首块记入口状态:`quiet_source` 是入口落在
|
|
538
|
+
实测安静或没有讲话的地方,`non_dialogue_source` 是入口落在只有语气词(尖叫、"Hi.")的讲话窗口里,两者都不阻断。
|
|
539
|
+
段落内与上一块作者留白不超过 1.6 秒的后续块会紧接上一块实际结尾 0.35 秒后播放,最多比写的 `start` 提前 1.2 秒;
|
|
540
|
+
旁白校验只检查过写的 `start`,所以提前的这一段不进入原声对白(ASR 对白区间减去实测安静窗口),有对白时最多提前到
|
|
541
|
+
最后一段对白结束处。真的提前了的块记 `paragraph_tightened`,`written_start` 是写的 `start`(未提前的块为 `null`);
|
|
542
|
+
这样的块若因间隔超过 `duck_bridge_seconds` 成为新压低段的首块,入口状态改记该段在实际起点上的入口判定,`written_start` 保留。
|
|
543
|
+
每次压低延续 = `source_restore_at - actual_place_end`,最大值写在 `assembly_qc.json` 的 `summary.max_source_duck_hold_seconds`,只作信息、不阻断。
|
|
544
|
+
|
|
545
|
+
## dub_lint.json
|
|
546
|
+
|
|
547
|
+
Dub 模式下,`dub_script.json` 在 voiceclone **之前**先经过 deterministic lint,把明显不可发布的脚本挡在昂贵的克隆 TTS 之前。空译文、相邻行重叠、时间越界、`room < 0.4s` 等 **error** 会 `verdict=FAIL` 并阻断 render;`fast_speech`、`trim_risk`(有效字数 ≥ 7 字/秒;`dub_brief.md` 要求的目标约 5 字/秒)等是 warning,不阻断。
|
|
548
|
+
|
|
549
|
+
每行 voiceclone 原始 WAV 会把中文台词、模型/提示等合成设置和参考音频信息写入相邻的 `*.wav.meta.json`。台词与设置完全相等且 WAV 可读取时,dub render 直接复用并在 `dub_manifest.json.lines[].tts_cache` 记录 `hit`;台词、参考音频、模型或提示变化都会重新合成。时长远超读完台词所需(`TTS_MIN_SPEECH_RATE`)的克隆音频视为幻读:按 `TTS_RETRIES` 重试、不写 `.meta.json`,用尽则 render 失败,最后一次被拒的音频留在 `dub_tts/line_NNN_raw.rejected.wav`;旧缓存里的这类 WAV 会重新合成。
|
|
511
550
|
|
|
512
551
|
```json
|
|
513
552
|
{
|
|
@@ -520,26 +559,44 @@ Dub 模式下,`dub_script.json` 在 voiceclone **之前**先经过 determinist
|
|
|
520
559
|
}
|
|
521
560
|
```
|
|
522
561
|
|
|
523
|
-
`
|
|
562
|
+
> dub 渲染阶段在克隆前写 `dub_lint.json`,lint 非 PASS 即中止;dub 只有 `--edit-mode dub` 驱动的准备 / 渲染两个阶段,没有单独的手动 lint / review 入口。
|
|
563
|
+
> 最终 `dub_<name>.mp4` 显式输出 48 kHz AAC;不能沿用 `loudnorm` 内部的 96 kHz 分析采样率。
|
|
564
|
+
## final_qc.json
|
|
565
|
+
|
|
566
|
+
渲染后 `recap.py` 写 `final_qc.json`;`scripts/final_qc.py --work-dir <dir>` 也可对已有 work_dir 单独重写。它只对最终 mp4 做本地确定性检查,每条 finding 都是阻断项:
|
|
524
567
|
|
|
525
568
|
```json
|
|
526
569
|
{
|
|
527
|
-
"schema_version":
|
|
528
|
-
"
|
|
529
|
-
"
|
|
530
|
-
"
|
|
531
|
-
"
|
|
570
|
+
"schema_version": 2,
|
|
571
|
+
"artifact": "final_qc.json",
|
|
572
|
+
"ok": false,
|
|
573
|
+
"blocker_count": 1,
|
|
574
|
+
"finding_count": 1,
|
|
575
|
+
"findings": [{"code": "missing_fps", "message": "final output probe metadata is missing a positive finite video fps",
|
|
576
|
+
"blocking": true, "evidence": {"fps": null, "video_stream": {"codec_type": "video", "codec_name": "h264"}},
|
|
577
|
+
"next_action": "rerender_final_output_with_valid_fps"}],
|
|
578
|
+
"metadata": {
|
|
579
|
+
"work_dir": "/abs/work", "final_output": {"path": "/abs/out/recap_x.mp4", "exists": true, "bytes": 1048576},
|
|
580
|
+
"artifacts": {"assembly_qc.json": {"path": "assembly_qc.json", "exists": true, "bytes": 512,
|
|
581
|
+
"summary": {"schema_version": 1, "verdict": "PASS", "blocking": false, "blocking_codes": []}}},
|
|
582
|
+
"probe": {"streams": [{"codec_type": "video", "codec_name": "h264", "width": 1920, "height": 1080, "avg_frame_rate": "30/1"}],
|
|
583
|
+
"format": {"format_name": "mov,mp4,m4a,3gp,3g2,mj2", "duration": "61.2"}},
|
|
584
|
+
"probe_error": null,
|
|
585
|
+
"warnings": [],
|
|
586
|
+
"auto_repair": false
|
|
587
|
+
}
|
|
532
588
|
}
|
|
533
589
|
```
|
|
534
590
|
|
|
535
|
-
|
|
536
|
-
> 最终 `dub_<name>.mp4` 显式输出 48 kHz AAC;不能沿用 `loudnorm` 内部的 96 kHz 分析采样率。
|
|
537
|
-
## shift-left QC artifacts
|
|
591
|
+
阻断码:`missing_final_output`、`empty_final_output`、`probe_failed`(ffprobe 不可用或失败)、`missing_video_stream`、`missing_duration` / `invalid_duration`、`missing_codec`、`missing_fps` / `invalid_fps`,以及尾部 2 秒解码失败的 `undecodable_stream`。`next_action` 是可直接执行的修复提示。`ok` 为 `true` 当且仅当 `blocker_count` 为 0;`--require-final-qc` 只读这两个字段,dashboard 另外读 finding 的 `code` / `message` / `blocking`。
|
|
538
592
|
|
|
539
|
-
`
|
|
593
|
+
`metadata.artifacts` 汇总 `assembly_manifest.json`、`assembly_qc.json`、`visual_qc.json` 的 `schema_version` / `verdict` / `blocking` / `blocking_codes`(不可解析时为 `{"invalid": true}`),只作记录、不转成 final_qc 的阻断项:assembly/visual QC 阻断时 video-assemble 已经非零退出,流程到不了 final_qc。`metadata.probe` 只保留检查用到的流与容器字段(codec、宽高、帧率、时长、采样率等),不保存 `tags` / `disposition` / 文件名,因此从原片带过来的容器标签(如 comment、purl 里的 URL)不会写进报告。
|
|
540
594
|
|
|
541
|
-
`
|
|
595
|
+
`metadata.warnings` 照抄 `visual_qc.json` 的 `warnings`:不阻断、不计入 `blocker_count`,但 Agent 交付时必须转告用户。目前只有一种:默认烧录字幕而 ffmpeg 缺 libass 时降级为外挂字幕,
|
|
542
596
|
|
|
543
|
-
|
|
597
|
+
```json
|
|
598
|
+
{"code": "subtitle_burn_degraded", "reason": "ffmpeg_missing_libass", "delivered": "sidecar_srt",
|
|
599
|
+
"mask_dropped": false, "message": "…", "next_action": "…"}
|
|
600
|
+
```
|
|
544
601
|
|
|
545
|
-
|
|
602
|
+
这时成片里没有字幕,字幕在成片旁的同名 `.srt`(路径见 `assembly_manifest.json` 的 `subtitle_sidecar`);`mask_dropped` 为 `true` 表示原本会画的原字幕遮罩也一并关掉了。`final_qc.py` 打印的摘要 `final_qc.warnings` 列出这些 code,`recap.py` 完成时打印一行警告和外挂字幕路径。
|
|
@@ -91,6 +91,7 @@ python3 scripts/library.py --library-dir <library> show <id|id@vN>
|
|
|
91
91
|
`0 <= y_top < y_bot <= canvas.height`。
|
|
92
92
|
- `packaging` 需要非空 `params.layers`,每层有唯一 `name`、`image` 引用与画布内的整数 `rect {x, y, width, height}`;
|
|
93
93
|
可选 `params.safe_rect`。
|
|
94
|
+
- `production_reference` 见下一节。
|
|
94
95
|
- `status` ∈ `draft` / `adopted` / `retired`。`adopted` 必须有 `adoption.date`(YYYY-MM-DD)、`by`、`statement`(用户原话)
|
|
95
96
|
与 `scope`。可选 `adoption.resources` 记录采用时所用资源文件的身份:
|
|
96
97
|
|
|
@@ -101,6 +102,20 @@ python3 scripts/library.py --library-dir <library> show <id|id@vN>
|
|
|
101
102
|
之后文件的大小或修改时间变化,`check` 给出 `changed_since_adoption` 警告:重新采用,或出一个新版本。
|
|
102
103
|
修改时间是本机事实,把库拷到另一台机器会让所有快照都显示为已变化。
|
|
103
104
|
|
|
105
|
+
### 参考模板 `production_reference`
|
|
106
|
+
|
|
107
|
+
把 video-reference 导出的 `production_reference.json`(只含方法与数值,不含原片事实)登记成可版本化、需采纳的模板:
|
|
108
|
+
|
|
109
|
+
1. `mkdir -p templates/production_reference/<id>/v1`,把导出物原样复制进去,文件名保持 `production_reference.json`。
|
|
110
|
+
2. 写 `template.json`:`params` 恰好是 `{"reference": {"path": "production_reference.json"}}`;`canvas` 从导出物复制,仅作信息,
|
|
111
|
+
绑定时不核对画布。
|
|
112
|
+
3. 写 `samples/<id>/sample.json` 指向原成片,`demonstrates` / `not_reusable` 写人看的摘要;含事实的完整拆解不进库。
|
|
113
|
+
4. `library.py check`:导出物的 `schema` 必须是 `video-reference.production.v1`,`methods` 非空且每条有合法 `dimension` 与 `rule`,
|
|
114
|
+
任何层级出现 `source_facts`、`labels`、`evidence`、`entities`、`statement`、`from`、`path` 键都报错(与参考技能导出时的复扫同一组键)。
|
|
115
|
+
5. 用户确认后把 `status` 改为 `adopted` 并用用户原话写 `adoption`。之后要改内容就出 `v2`,不改已采纳的版本。
|
|
116
|
+
|
|
117
|
+
库仍然只读,登记与采纳都是手工编辑。
|
|
118
|
+
|
|
104
119
|
## 样片 `sample.json`
|
|
105
120
|
|
|
106
121
|
```json
|
|
@@ -138,10 +153,11 @@ python3 scripts/library.py --library-dir <library> show <id|id@vN>
|
|
|
138
153
|
| `subtitle_style` | `SUBTITLE_PLAY_RES_X/Y` = 模板画布,`SUBTITLE_FONT_SIZE` ← `size_px`,`SUBTITLE_OUTLINE` ← `outline_px`,`SUBTITLE_SHADOW` ← `shadow_px`,`SUBTITLE_PRIMARY_COLOR` / `SUBTITLE_OUTLINE_COLOR`(ASS `&HAABBGGRR`),`SUBTITLE_MAX_CHARS` / `SUBTITLE_MAX_LINES`;`band` → 底对齐 `SUBTITLE_ALIGNMENT=2` 且 `SUBTITLE_MARGIN_V` = 画布高 − `y_bot`;`font.family` → `SUBTITLE_FONT_NAME`,字体资源 → 再加 `SUBTITLE_FONT_FILE` |
|
|
139
154
|
| `voice` | provider → `--tts-provider`;MiMo 预置音色 → `--mimo-tts-voice`,参考音频 → `--voice-ref`;Fish Audio → `FISH_TTS_REFERENCE_ID`;index-tts → `INDEX_TTS_VOICE` |
|
|
140
155
|
| `bgm` | `BGM_PATH` ← 该资源的第一个文件 |
|
|
156
|
+
| `production_reference` | 第一次暂停前写出 `work_dir/production_reference.json`:导出物原样加 `written_by` 与 `template {id, version}`,仅写稿 Agent 阅读,没有脚本读取;不核对画布。该位置已有不是 `--project` 写的文件时:与导出物相同则原样保留(暂停提示照常),不同则停止;解除绑定后删掉自己写的副本,调用方自己放的文件不动 |
|
|
141
157
|
| `packaging` | 合成前写出 `work_dir/packaging_layers.json`:每个图层的图片资源第一个文件 + `rect`,由 video-assemble 叠加到成片并写进 `timeline.json` 的 image 轨 |
|
|
142
158
|
|
|
143
159
|
- 你已经显式设置的参数或环境变量与绑定不一致时,运行在开始前停止并指出是哪一项,不会静默覆盖。
|
|
144
|
-
-
|
|
160
|
+
- 合成前核对字幕与包装模板的画布与实际成片画布;不一致即停止——换画幅要用另一个模板。
|
|
145
161
|
- 参考音频的 `consent.status` 为 `denied` 时拒绝绑定;dub 模式与本地采用三件套不接受 `--project`。
|
|
146
162
|
- 续跑命令只带 `--project`,不重复写出由绑定得到的值,改了绑定后续跑会按新绑定解析。
|
|
147
163
|
|