@oh-story/dsh 0.1.7 → 0.1.9
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 +30 -15
- package/lib/client.js +93 -16
- package/lib/client.js.map +2 -2
- package/lib/drama/manifest.json +165 -69
- package/lib/drama/skills/short-drama/SKILL.md +5 -1
- package/lib/drama/skills/short-drama/scripts/creator_markdown_check.py +126 -9
- package/lib/drama/skills/short-drama/scripts/project_tool.py +11 -0
- package/lib/drama/skills/short-drama-assets/references/character-and-look.md +18 -0
- package/lib/drama/skills/short-drama-assets/references/continuity-lock.md +10 -1
- package/lib/drama/skills/short-drama-edit/SKILL.md +148 -0
- package/lib/drama/skills/short-drama-edit/agents/openai.yaml +4 -0
- package/lib/drama/skills/short-drama-edit/assets/remotion/README.md +60 -0
- package/lib/drama/skills/short-drama-edit/assets/remotion/package.json +15 -0
- package/lib/drama/skills/short-drama-edit/assets/remotion/remotion.config.ts +6 -0
- package/lib/drama/skills/short-drama-edit/assets/remotion/src/Root.tsx +26 -0
- package/lib/drama/skills/short-drama-edit/assets/remotion/src/Subtitles.tsx +88 -0
- package/lib/drama/skills/short-drama-edit/assets/remotion/src/font.ts +46 -0
- package/lib/drama/skills/short-drama-edit/assets/remotion/src/index.ts +4 -0
- package/lib/drama/skills/short-drama-edit/assets/remotion/src/schema.ts +32 -0
- package/lib/drama/skills/short-drama-edit/assets/remotion/tsconfig.json +13 -0
- package/lib/drama/skills/short-drama-edit/assets//345/211/252/350/276/221/345/215/225.md +43 -0
- package/lib/drama/skills/short-drama-edit/references/cut-craft.md +81 -0
- package/lib/drama/skills/short-drama-edit/references/delivery-verify.md +58 -0
- package/lib/drama/skills/short-drama-edit/references/generated-footage.md +96 -0
- package/lib/drama/skills/short-drama-edit/references/sound-and-subtitles.md +113 -0
- package/lib/drama/skills/short-drama-edit/references/stage-contract.md +55 -0
- package/lib/drama/skills/short-drama-edit/scripts/edit_tool.py +1041 -0
- package/lib/drama/skills/short-drama-edit/scripts/selftest.py +456 -0
- package/lib/drama/skills/short-drama-image-prompts/assets/image-prompts.md +5 -0
- package/lib/drama/skills/short-drama-image-prompts/assets/lookdev-prompts.md +3 -0
- package/lib/drama/skills/short-drama-novel-analyze/references/aggregation-and-entities.md +2 -2
- package/lib/drama/skills/short-drama-novel-analyze/references/chapter-extraction.md +4 -1
- package/lib/drama/skills/short-drama-produce/SKILL.md +24 -3
- package/lib/drama/skills/short-drama-produce/references/adapter-contract.md +28 -0
- package/lib/drama/skills/short-drama-produce/references/providers/minimax-h3-video.md +24 -5
- package/lib/drama/skills/short-drama-produce/references/providers/seedance.md +7 -0
- package/lib/drama/skills/short-drama-produce/scripts/fixture_adapter.py +18 -1
- package/lib/drama/skills/short-drama-produce/scripts/production_tool.py +181 -2
- package/lib/drama/skills/short-drama-produce/scripts/provider_adapters.py +57 -2
- package/lib/drama/skills/short-drama-review/SKILL.md +3 -0
- package/lib/drama/skills/short-drama-review/references/production-quality-gates.md +20 -0
- package/lib/drama/skills/short-drama-review/references/rubric-visual-motion.md +36 -0
- package/lib/drama/skills/short-drama-storyboard/SKILL.md +7 -0
- package/lib/drama/skills/short-drama-storyboard/assets/keyframe-prompts.md +4 -0
- package/lib/drama/skills/short-drama-storyboard/references/shot-craft.md +39 -0
- package/lib/drama/skills/short-drama-storyboard/references/stage-contract.md +2 -0
- package/lib/drama/skills/short-drama-video-prompts/SKILL.md +7 -1
- package/lib/drama/skills/short-drama-video-prompts/references/minimax-h3.md +101 -6
- package/lib/drama/skills/short-drama-video-prompts/references/motion-recipe.md +7 -1
- package/lib/drama/skills/short-drama-video-prompts/references/performance-action-timing.md +35 -0
- package/lib/drama/skills/short-drama-video-prompts/references/production-prompt-grammar.md +3 -1
- package/lib/drama/skills/short-drama-video-prompts/references/seedance-2.0.md +13 -3
- package/lib/drama/skills/short-drama-video-prompts/references/seedance-2.5.md +35 -4
- package/lib/drama/skills/short-drama-video-prompts/references/stage-contract.md +1 -1
- package/lib/drama/skills/short-drama-write/SKILL.md +13 -2
- package/lib/drama/skills/short-drama-write/references/screenplay-format.md +6 -0
- package/lib/drama/skills/short-drama-write/references/stage-contract.md +1 -0
- package/lib/index.js +4 -2
- package/lib/index.js.map +2 -2
- package/lib/novel-to-game/VERSION +1 -1
- package/lib/novel-to-game/manifest.json +28 -28
- package/lib/novel-to-game/skills/game-art-direction/SKILL.md +6 -27
- package/lib/novel-to-game/skills/game-build/SKILL.md +12 -29
- package/lib/novel-to-game/skills/game-concept/SKILL.md +10 -26
- package/lib/novel-to-game/skills/game-qa/SKILL.md +5 -15
- package/lib/novel-to-game/skills/game-qa/references/test-design-method.md +1 -1
- package/lib/novel-to-game/skills/game-world-design/SKILL.md +19 -28
- package/lib/novel-to-game/skills/game-world-design/references/dialogue-design-method.md +1 -2
- package/lib/novel-to-game/skills/novel-game-analyze/SKILL.md +6 -9
- package/lib/novel-to-game/skills/novel-to-game/SKILL.md +6 -11
- package/lib/novel-to-game/skills/novel-to-game/references/intake-benchmark-reference.md +0 -4
- package/lib/novel-to-game/skills/novel-to-game/references/intake-method.md +1 -3
- package/lib/novel-to-game/skills/novel-to-game/references/pipeline-contract.md +3 -5
- package/lib/oh-story/manifest.json +292 -172
- package/lib/oh-story/roles/character-designer.md +1 -1
- package/lib/oh-story/roles/narrative-writer.md +56 -242
- package/lib/oh-story/roles/story-architect.md +1 -1
- package/lib/oh-story/skills/story/SKILL.md +1 -1
- package/lib/oh-story/skills/story/VERSION +1 -1
- package/lib/oh-story/skills/story/references/author-memory.md +10 -8
- package/lib/oh-story/skills/story-deslop/SKILL.md +20 -182
- package/lib/oh-story/skills/story-deslop/references/anti-ai-writing.md +19 -47
- package/lib/oh-story/skills/story-deslop/references/author-memory.md +10 -8
- package/lib/oh-story/skills/story-deslop/references/banned-words.md +3 -1
- package/lib/oh-story/skills/story-deslop/references/deslop-gates.md +154 -0
- package/lib/oh-story/skills/story-deslop/references/style-resolution.md +27 -0
- package/lib/oh-story/skills/story-deslop/scripts/check-ai-patterns.js +6 -1
- package/lib/oh-story/skills/story-deslop/scripts/check-degeneration.js +39 -20
- package/lib/oh-story/skills/story-deslop/scripts/normalize-punctuation.js +10 -5
- package/lib/oh-story/skills/story-deslop/scripts/style-whitelist.js +39 -0
- package/lib/oh-story/skills/story-import/SKILL.md +2 -2
- package/lib/oh-story/skills/story-import/references/format-and-structure.md +2 -2
- package/lib/oh-story/skills/story-import/references/tracking-initialization.md +31 -0
- package/lib/oh-story/skills/story-import/references/tracking-transaction.md +2 -29
- package/lib/oh-story/skills/story-import/scripts/check-outline-contract.js +176 -12
- package/lib/oh-story/skills/story-long-analyze/SKILL.md +1 -1
- package/lib/oh-story/skills/story-long-write/SKILL.md +20 -193
- package/lib/oh-story/skills/story-long-write/references/anti-ai-writing.md +19 -47
- package/lib/oh-story/skills/story-long-write/references/artifact-protocols.md +62 -7
- package/lib/oh-story/skills/story-long-write/references/author-memory.md +10 -8
- package/lib/oh-story/skills/story-long-write/references/banned-words.md +3 -1
- package/lib/oh-story/skills/story-long-write/references/benchmark-recall.md +20 -0
- package/lib/oh-story/skills/story-long-write/references/dialogue-mastery.md +6 -4
- package/lib/oh-story/skills/story-long-write/references/long-chapter-quality.md +2 -2
- package/lib/oh-story/skills/story-long-write/references/long-format.md +2 -0
- package/lib/oh-story/skills/story-long-write/references/outline-structure-theory.md +2 -0
- package/lib/oh-story/skills/story-long-write/references/project-files.md +108 -0
- package/lib/oh-story/skills/story-long-write/references/reference-index.md +87 -0
- package/lib/oh-story/skills/story-long-write/references/style-combat-face.md +1 -1
- package/lib/oh-story/skills/story-long-write/references/style-craft.md +9 -29
- package/lib/oh-story/skills/story-long-write/references/style-genre-modules.md +1 -1
- package/lib/oh-story/skills/story-long-write/references/style-resolution.md +27 -0
- package/lib/oh-story/skills/story-long-write/references/tracking-initialization.md +31 -0
- package/lib/oh-story/skills/story-long-write/references/tracking-transaction.md +2 -29
- package/lib/oh-story/skills/story-long-write/references/workflow-chapter.md +51 -52
- package/lib/oh-story/skills/story-long-write/references/workflow-daily.md +8 -7
- package/lib/oh-story/skills/story-long-write/references/workflow-setup.md +36 -15
- package/lib/oh-story/skills/story-long-write/references/writer-prompt-fallback.md +22 -0
- package/lib/oh-story/skills/story-long-write/references/writing-craft.md +69 -2
- package/lib/oh-story/skills/story-long-write/scripts/build_writer_prompt.py +410 -0
- package/lib/oh-story/skills/story-long-write/scripts/check-ai-patterns.js +6 -1
- package/lib/oh-story/skills/story-long-write/scripts/check-degeneration.js +39 -20
- package/lib/oh-story/skills/story-long-write/scripts/check-outline-contract.js +176 -12
- package/lib/oh-story/skills/story-long-write/scripts/check-outline-copy.js +90 -23
- package/lib/oh-story/skills/story-long-write/scripts/normalize-punctuation.js +10 -5
- package/lib/oh-story/skills/story-long-write/scripts/outline_view.py +283 -0
- package/lib/oh-story/skills/story-long-write/scripts/style-whitelist.js +39 -0
- package/lib/oh-story/skills/story-review/SKILL.md +13 -3
- package/lib/oh-story/skills/story-review/references/anti-ai-writing.md +19 -47
- package/lib/oh-story/skills/story-review/references/author-memory.md +10 -8
- package/lib/oh-story/skills/story-review/references/banned-words.md +3 -1
- package/lib/oh-story/skills/story-review/references/dialogue-mastery.md +6 -4
- package/lib/oh-story/skills/story-review/references/review-quality.md +2 -2
- package/lib/oh-story/skills/story-review/references/style-resolution.md +27 -0
- package/lib/oh-story/skills/story-review/references/tracking-initialization.md +31 -0
- package/lib/oh-story/skills/story-review/references/tracking-transaction.md +2 -29
- package/lib/oh-story/skills/story-review/scripts/check-ai-patterns.js +6 -1
- package/lib/oh-story/skills/story-review/scripts/check-degeneration.js +39 -20
- package/lib/oh-story/skills/story-review/scripts/normalize-punctuation.js +10 -5
- package/lib/oh-story/skills/story-review/scripts/style-whitelist.js +39 -0
- package/lib/oh-story/skills/story-setup/SKILL.md +17 -10
- package/lib/oh-story/skills/story-setup/references/agent-references/anti-ai-writing.md +19 -47
- package/lib/oh-story/skills/story-setup/references/agent-references/banned-words.md +3 -1
- package/lib/oh-story/skills/story-setup/references/agent-references/deslop-gates.md +154 -0
- package/lib/oh-story/skills/story-setup/references/agent-references/dialogue-mastery.md +6 -4
- package/lib/oh-story/skills/story-setup/references/agent-references/format-and-structure.md +2 -2
- package/lib/oh-story/skills/story-setup/references/agent-references/short-genre-formulas.md +1 -1
- package/lib/oh-story/skills/story-setup/references/agent-references/style-combat-face.md +1 -1
- package/lib/oh-story/skills/story-setup/references/agent-references/style-genre-modules.md +1 -1
- package/lib/oh-story/skills/story-setup/references/agent-references/style-resolution.md +27 -0
- package/lib/oh-story/skills/story-setup/references/agent-references/writing-craft.md +69 -2
- package/lib/oh-story/skills/story-setup/references/diagnostics.md +38 -0
- package/lib/oh-story/skills/story-short-analyze/references/analysis-writing-techniques.md +1 -1
- package/lib/oh-story/skills/story-short-analyze/references/source-story-quality.md +2 -2
- package/lib/oh-story/skills/story-short-write/SKILL.md +18 -186
- package/lib/oh-story/skills/story-short-write/references/author-memory.md +10 -8
- package/lib/oh-story/skills/story-short-write/references/banned-words.md +3 -1
- package/lib/oh-story/skills/story-short-write/references/benchmark-recall.md +60 -0
- package/lib/oh-story/skills/story-short-write/references/dialogue-mastery.md +6 -4
- package/lib/oh-story/skills/story-short-write/references/genre-styles//344/270/226/346/203/205/346/211/223/350/204/270.md +2 -2
- package/lib/oh-story/skills/story-short-write/references/genre-styles//345/217/214/347/224/267/344/270/273.md +1 -1
- package/lib/oh-story/skills/story-short-write/references/genre-styles//345/244/215/344/273/207/346/211/223/350/204/270.md +3 -3
- package/lib/oh-story/skills/story-short-write/references/genre-styles//345/256/205/346/226/227/345/256/253/346/226/227.md +3 -3
- package/lib/oh-story/skills/story-short-write/references/genre-styles//346/200/273/350/243/201/350/261/252/351/227/250.md +3 -3
- package/lib/oh-story/skills/story-short-write/references/genre-styles//346/202/254/347/226/221.md +5 -5
- package/lib/oh-story/skills/story-short-write/references/genre-styles//346/260/221/344/277/227/346/200/252/350/260/210.md +4 -4
- package/lib/oh-story/skills/story-short-write/references/genre-styles//347/224/234/345/256/240.md +5 -5
- package/lib/oh-story/skills/story-short-write/references/genre-styles//350/277/275/345/246/273/347/201/253/350/221/254/345/234/272.md +3 -3
- package/lib/oh-story/skills/story-short-write/references/genre-writing-formulas.md +1 -1
- package/lib/oh-story/skills/story-short-write/references/genre-writing-techniques.md +1 -1
- package/lib/oh-story/skills/story-short-write/references/reference-index.md +53 -0
- package/lib/oh-story/skills/story-short-write/references/short-craft.md +12 -14
- package/lib/oh-story/skills/story-short-write/references/short-deslop.md +10 -10
- package/lib/oh-story/skills/story-short-write/references/short-format.md +3 -1
- package/lib/oh-story/skills/story-short-write/references/short-prose-quality.md +2 -2
- package/lib/oh-story/skills/story-short-write/references/style-resolution.md +27 -0
- package/lib/oh-story/skills/story-short-write/references/workflow-design.md +77 -0
- package/lib/oh-story/skills/story-short-write/references/writing-workflow.md +5 -5
- package/lib/oh-story/skills/story-short-write/scripts/check-ai-patterns.js +6 -1
- package/lib/oh-story/skills/story-short-write/scripts/check-degeneration.js +39 -20
- package/lib/oh-story/skills/story-short-write/scripts/check-delivery-contract.js +34 -5
- package/lib/oh-story/skills/story-short-write/scripts/check-outline-copy.js +90 -23
- package/lib/oh-story/skills/story-short-write/scripts/normalize-punctuation.js +10 -5
- package/lib/oh-story/skills/story-short-write/scripts/style-whitelist.js +39 -0
- package/lib/types/client/drama-production-view.d.ts.map +1 -1
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/production-prompts.d.ts.map +1 -1
- package/lib/types/client/production-runtime.d.ts +17 -0
- package/lib/types/client/production-runtime.d.ts.map +1 -1
- package/lib/types/skill-provider.d.ts.map +1 -1
- package/lib/video-recap/CHANGELOG.md +31 -1
- package/lib/video-recap/manifest.json +162 -162
- package/lib/video-recap/skills/video-assemble/scripts/artifacts.py +4 -9
- package/lib/video-recap/skills/video-assemble/scripts/assemble.py +47 -60
- package/lib/video-recap/skills/video-assemble/scripts/assembly_contract.py +77 -115
- package/lib/video-recap/skills/video-assemble/scripts/assembly_settings.py +41 -40
- package/lib/video-recap/skills/video-assemble/scripts/audio_automation.py +22 -32
- package/lib/video-recap/skills/video-assemble/scripts/audio_mix.py +102 -192
- package/lib/video-recap/skills/video-assemble/scripts/jianying_builders.py +14 -35
- package/lib/video-recap/skills/video-assemble/scripts/jianying_model.py +14 -12
- package/lib/video-recap/skills/video-assemble/scripts/jianying_optional.py +8 -9
- package/lib/video-recap/skills/video-assemble/scripts/jianying_schema.py +3 -14
- package/lib/video-recap/skills/video-assemble/scripts/jianying_timeline_contract.py +23 -12
- package/lib/video-recap/skills/video-assemble/scripts/jianying_tracks.py +1 -4
- package/lib/video-recap/skills/video-assemble/scripts/jianying_writer.py +24 -25
- package/lib/video-recap/skills/video-assemble/scripts/lib.py +31 -85
- package/lib/video-recap/skills/video-assemble/scripts/media.py +100 -144
- package/lib/video-recap/skills/video-assemble/scripts/narration_audio.py +101 -195
- package/lib/video-recap/skills/video-assemble/scripts/render_preflight.py +10 -21
- package/lib/video-recap/skills/video-assemble/scripts/source_subtitles.py +90 -169
- package/lib/video-recap/skills/video-assemble/scripts/subtitle_core.py +69 -122
- package/lib/video-recap/skills/video-assemble/scripts/subtitle_render.py +10 -17
- package/lib/video-recap/skills/video-assemble/scripts/timeline.py +33 -48
- package/lib/video-recap/skills/video-assemble/scripts/timeline_emit.py +14 -20
- package/lib/video-recap/skills/video-assemble/scripts/visual_render.py +135 -213
- package/lib/video-recap/skills/video-cut/scripts/cut_cli.py +49 -57
- package/lib/video-recap/skills/video-cut/scripts/cut_contract.py +38 -74
- package/lib/video-recap/skills/video-cut/scripts/cut_render.py +46 -95
- package/lib/video-recap/skills/video-cut/scripts/lib.py +23 -25
- package/lib/video-recap/skills/video-cut/scripts/media_geometry.py +70 -173
- package/lib/video-recap/skills/video-cut/scripts/narration_mapping.py +28 -53
- package/lib/video-recap/skills/video-cut/scripts/sentence_boundaries.py +162 -322
- package/lib/video-recap/skills/video-recap/references/timeline-and-jianying.md +6 -7
- package/lib/video-recap/skills/video-recap/scripts/doctor.py +104 -124
- package/lib/video-recap/skills/video-recap/scripts/final_qc.py +255 -412
- package/lib/video-recap/skills/video-recap/scripts/lib.py +30 -52
- package/lib/video-recap/skills/video-recap/scripts/materials.py +97 -120
- package/lib/video-recap/skills/video-recap/scripts/mimo_qc_client.py +7 -12
- package/lib/video-recap/skills/video-recap/scripts/mimo_qc_evidence.py +46 -97
- package/lib/video-recap/skills/video-recap/scripts/mimo_qc_observations.py +18 -36
- package/lib/video-recap/skills/video-recap/scripts/mimo_qc_payload.py +29 -47
- package/lib/video-recap/skills/video-recap/scripts/mimo_qc_report.py +43 -76
- package/lib/video-recap/skills/video-recap/scripts/mimo_qc_runner.py +2 -1
- package/lib/video-recap/skills/video-recap/scripts/qc_contract.py +2 -1
- package/lib/video-recap/skills/video-recap/scripts/recap_cli.py +2 -2
- package/lib/video-recap/skills/video-recap/scripts/recap_inspect.py +129 -145
- package/lib/video-recap/skills/video-recap/scripts/recap_review.py +27 -20
- package/lib/video-recap/skills/video-recap/scripts/recap_runner.py +20 -31
- package/lib/video-recap/skills/video-recap/scripts/recap_runtime.py +31 -106
- package/lib/video-recap/skills/video-recap/scripts/recap_stage_qc.py +43 -113
- package/lib/video-recap/skills/video-recap/scripts/recap_timeline.py +196 -377
- package/lib/video-recap/skills/video-script/scripts/agent_brief.py +1 -1
- package/lib/video-recap/skills/video-script/scripts/agent_text.py +63 -172
- package/lib/video-recap/skills/video-script/scripts/brief_context.py +80 -135
- package/lib/video-recap/skills/video-script/scripts/brief_inputs.py +125 -167
- package/lib/video-recap/skills/video-script/scripts/brief_timeline.py +93 -208
- package/lib/video-recap/skills/video-script/scripts/deslop_qc.py +20 -46
- package/lib/video-recap/skills/video-script/scripts/lib.py +30 -26
- package/lib/video-recap/skills/video-script/scripts/narration_lint.py +145 -140
- package/lib/video-recap/skills/video-script/scripts/review_response.py +12 -42
- package/lib/video-recap/skills/video-script/scripts/review_runner.py +0 -6
- package/lib/video-recap/skills/video-script/scripts/speech_ownership.py +39 -100
- package/lib/video-recap/skills/video-script/scripts/timeline_fusion.py +33 -124
- package/lib/video-recap/skills/video-understanding/scripts/agent_brief.py +1 -1
- package/lib/video-recap/skills/video-understanding/scripts/agent_text.py +63 -172
- package/lib/video-recap/skills/video-understanding/scripts/asr.py +1 -2
- package/lib/video-recap/skills/video-understanding/scripts/brief_context.py +80 -135
- package/lib/video-recap/skills/video-understanding/scripts/brief_inputs.py +125 -167
- package/lib/video-recap/skills/video-understanding/scripts/brief_timeline.py +93 -208
- package/lib/video-recap/skills/video-understanding/scripts/consolidate.py +1 -1
- package/lib/video-recap/skills/video-understanding/scripts/deslop_qc.py +20 -46
- package/lib/video-recap/skills/video-understanding/scripts/lib.py +31 -26
- package/lib/video-recap/skills/video-understanding/scripts/narration_lint.py +145 -140
- package/lib/video-recap/skills/video-understanding/scripts/speech_ownership.py +39 -100
- package/lib/video-recap/skills/video-understanding/scripts/timeline_fusion.py +33 -124
- package/lib/video-recap/skills/video-understanding/scripts/vlm.py +1 -4
- package/lib/video-recap/skills/video-voiceover/scripts/dub.py +7 -13
- package/lib/video-recap/skills/video-voiceover/scripts/fish_audio.py +12 -30
- package/lib/video-recap/skills/video-voiceover/scripts/lib.py +98 -131
- package/lib/video-recap/skills/video-voiceover/scripts/tts_audio.py +17 -28
- package/lib/video-recap/skills/video-voiceover/scripts/voiceover.py +151 -240
- package/package.json +41 -41
|
@@ -114,36 +114,15 @@ def merge_review_findings(chunks):
|
|
|
114
114
|
|
|
115
115
|
|
|
116
116
|
def _bundle_fingerprint(bundle):
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
)
|
|
127
|
-
except (TypeError, ValueError, RecursionError) as exc:
|
|
128
|
-
if isinstance(bundle, dict):
|
|
129
|
-
bundle.setdefault("metadata", {})["evidence_bundle_fingerprint_warning"] = (
|
|
130
|
-
f"evidence bundle fingerprint unavailable: {type(exc).__name__}"
|
|
131
|
-
)
|
|
132
|
-
return ""
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
def _bundle_fingerprint_warning(bundle):
|
|
136
|
-
if not isinstance(bundle, dict):
|
|
137
|
-
return "evidence bundle fingerprint unavailable: invalid bundle"
|
|
138
|
-
warning = (bundle.get("metadata") or {}).get("evidence_bundle_fingerprint_warning")
|
|
139
|
-
if warning:
|
|
140
|
-
return warning
|
|
141
|
-
return ""
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
def _append_warning_once(target, warning):
|
|
145
|
-
if warning and warning not in target:
|
|
146
|
-
target.append(warning)
|
|
117
|
+
return stable_hash(
|
|
118
|
+
{
|
|
119
|
+
"schema_version": bundle.get("schema_version"),
|
|
120
|
+
"clock": bundle.get("clock"),
|
|
121
|
+
"coverage": bundle.get("coverage"),
|
|
122
|
+
"items": bundle.get("items"),
|
|
123
|
+
"context_items": bundle.get("context_items"),
|
|
124
|
+
}
|
|
125
|
+
)
|
|
147
126
|
|
|
148
127
|
|
|
149
128
|
def _bundle_prompt_size(bundle):
|
|
@@ -163,13 +142,9 @@ def _chunk_evidence_bundle(bundle, *, max_items=80, max_chars=12000):
|
|
|
163
142
|
one = dict(bundle)
|
|
164
143
|
one["chunk_index"] = 0
|
|
165
144
|
one["chunk_count"] = 1
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
if fp_warning:
|
|
170
|
-
one["metadata"]["evidence_bundle_fingerprint_warning"] = fp_warning
|
|
171
|
-
one.setdefault("warnings", list(bundle.get("warnings") or []))
|
|
172
|
-
_append_warning_once(one["warnings"], fp_warning)
|
|
145
|
+
one.setdefault("metadata", {})["evidence_bundle_fingerprint"] = (
|
|
146
|
+
_bundle_fingerprint(bundle)
|
|
147
|
+
)
|
|
173
148
|
return [one]
|
|
174
149
|
ranges = bundle.get("coverage", {}).get("selected_ranges") or []
|
|
175
150
|
chunks = []
|
|
@@ -210,15 +185,10 @@ def _chunk_evidence_bundle(bundle, *, max_items=80, max_chars=12000):
|
|
|
210
185
|
chunks = [chunk]
|
|
211
186
|
count = len(chunks)
|
|
212
187
|
fp = _bundle_fingerprint(bundle)
|
|
213
|
-
fp_warning = _bundle_fingerprint_warning(bundle)
|
|
214
188
|
for chunk in chunks:
|
|
215
189
|
chunk["chunk_count"] = count
|
|
216
190
|
chunk.setdefault("metadata", {})["chunked_review"] = count > 1
|
|
217
191
|
chunk["metadata"]["evidence_bundle_fingerprint"] = fp
|
|
218
|
-
if fp_warning:
|
|
219
|
-
chunk["metadata"]["evidence_bundle_fingerprint_warning"] = fp_warning
|
|
220
|
-
chunk.setdefault("warnings", list(bundle.get("warnings") or []))
|
|
221
|
-
_append_warning_once(chunk["warnings"], fp_warning)
|
|
222
192
|
return chunks
|
|
223
193
|
|
|
224
194
|
|
|
@@ -17,9 +17,7 @@ from review_grounding import (
|
|
|
17
17
|
remap_grounding_to_output_timeline,
|
|
18
18
|
)
|
|
19
19
|
from review_response import (
|
|
20
|
-
_append_warning_once,
|
|
21
20
|
_bundle_fingerprint,
|
|
22
|
-
_bundle_fingerprint_warning,
|
|
23
21
|
_chunk_evidence_bundle,
|
|
24
22
|
_load_review_research_context,
|
|
25
23
|
_merge_chunk_reviews,
|
|
@@ -65,10 +63,6 @@ def review_narration(work_dir, *, timeline="source", strict_evidence=False):
|
|
|
65
63
|
warnings=warnings,
|
|
66
64
|
)
|
|
67
65
|
bundle_fp = _bundle_fingerprint(bundle)
|
|
68
|
-
fp_warning = _bundle_fingerprint_warning(bundle)
|
|
69
|
-
if fp_warning:
|
|
70
|
-
_append_warning_once(warnings, fp_warning)
|
|
71
|
-
_append_warning_once(bundle.setdefault("warnings", []), fp_warning)
|
|
72
66
|
chunk_reviews = []
|
|
73
67
|
chunks = _chunk_evidence_bundle(bundle)
|
|
74
68
|
for chunk in chunks:
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"""Load measured source-speech evidence and classify narration ownership."""
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
|
-
import math
|
|
5
4
|
from pathlib import Path
|
|
6
5
|
|
|
7
6
|
from lib import CONFIG, stable_hash
|
|
@@ -17,36 +16,16 @@ def _empty_evidence(mode):
|
|
|
17
16
|
|
|
18
17
|
|
|
19
18
|
def _read_json(path):
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
except (OSError, json.JSONDecodeError):
|
|
23
|
-
return None
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
def _timed_rows(payload, key):
|
|
27
|
-
rows = payload.get(key, []) if isinstance(payload, dict) else []
|
|
28
|
-
out = []
|
|
29
|
-
for row in rows if isinstance(rows, list) else []:
|
|
30
|
-
if not isinstance(row, dict):
|
|
31
|
-
continue
|
|
32
|
-
try:
|
|
33
|
-
start, end = float(row.get("start")), float(row.get("end"))
|
|
34
|
-
except (TypeError, ValueError):
|
|
35
|
-
continue
|
|
36
|
-
if math.isfinite(start) and math.isfinite(end) and end > start:
|
|
37
|
-
out.append({**row, "start": start, "end": end})
|
|
38
|
-
return out
|
|
19
|
+
"""JSON artifact, or None when the optional file was never written."""
|
|
20
|
+
return json.loads(path.read_text(encoding="utf-8")) if path.exists() else None
|
|
39
21
|
|
|
40
22
|
|
|
41
23
|
def _output_payload_is_current(payload, work_dir):
|
|
42
|
-
if not isinstance(payload, dict):
|
|
43
|
-
return False
|
|
44
|
-
if payload.get("schema_version") != 2 or payload.get("timeline") != "cut_output":
|
|
45
|
-
return False
|
|
46
24
|
plan = _read_json(Path(work_dir) / "clip_plan_validated.json")
|
|
47
|
-
return
|
|
48
|
-
|
|
49
|
-
and
|
|
25
|
+
return (
|
|
26
|
+
payload is not None
|
|
27
|
+
and plan is not None
|
|
28
|
+
and payload["clip_plan_fingerprint"] == stable_hash(plan)
|
|
50
29
|
)
|
|
51
30
|
|
|
52
31
|
|
|
@@ -55,55 +34,31 @@ def load_source_sentence_evidence(work_dir, mode="full"):
|
|
|
55
34
|
if work_dir is None:
|
|
56
35
|
return _empty_evidence(mode)
|
|
57
36
|
work_dir = Path(work_dir)
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
break
|
|
84
|
-
raw_quiet = _read_json(work_dir / "silence_periods.json")
|
|
85
|
-
candidate = {
|
|
86
|
-
"quiet_windows": [
|
|
87
|
-
row
|
|
88
|
-
for row in raw_quiet
|
|
89
|
-
if isinstance(row, dict) and not bool(row.get("has_speech", False))
|
|
90
|
-
]
|
|
91
|
-
} if isinstance(raw_quiet, list) else {}
|
|
92
|
-
quiet_windows = _timed_rows(candidate, "quiet_windows")
|
|
93
|
-
|
|
94
|
-
anchors = []
|
|
95
|
-
for anchor in payload.get("sentence_anchors", []):
|
|
96
|
-
if not isinstance(anchor, dict) or anchor.get("confidence") not in {
|
|
97
|
-
"high",
|
|
98
|
-
"medium",
|
|
99
|
-
}:
|
|
100
|
-
continue
|
|
101
|
-
try:
|
|
102
|
-
when = float(anchor.get("time"))
|
|
103
|
-
except (TypeError, ValueError):
|
|
104
|
-
continue
|
|
105
|
-
if math.isfinite(when) and when >= 0:
|
|
106
|
-
anchors.append({**anchor, "time": round(when, 3)})
|
|
37
|
+
if mode == "full":
|
|
38
|
+
payload = _read_json(work_dir / "speech_boundary_anchors.json") or {
|
|
39
|
+
"sentence_anchors": []
|
|
40
|
+
}
|
|
41
|
+
speech_spans = [
|
|
42
|
+
row for row in _read_json(work_dir / "asr_result.json") or [] if row["text"]
|
|
43
|
+
]
|
|
44
|
+
quiet_windows = [
|
|
45
|
+
row
|
|
46
|
+
for row in _read_json(work_dir / "silence_periods.json") or []
|
|
47
|
+
if not row["has_speech"]
|
|
48
|
+
]
|
|
49
|
+
else:
|
|
50
|
+
payload = _read_json(work_dir / "speech_boundary_anchors_output.json")
|
|
51
|
+
if mode == "cut_output" and not _output_payload_is_current(payload, work_dir):
|
|
52
|
+
return _empty_evidence(mode)
|
|
53
|
+
if payload is None:
|
|
54
|
+
payload = {"sentence_anchors": [], "speech_spans": [], "quiet_windows": []}
|
|
55
|
+
speech_spans = payload["speech_spans"]
|
|
56
|
+
quiet_windows = payload["quiet_windows"]
|
|
57
|
+
anchors = [
|
|
58
|
+
anchor
|
|
59
|
+
for anchor in payload["sentence_anchors"]
|
|
60
|
+
if anchor["confidence"] in {"high", "medium"}
|
|
61
|
+
]
|
|
107
62
|
return {
|
|
108
63
|
"anchors": sorted(anchors, key=lambda item: item["time"]),
|
|
109
64
|
"speech_spans": speech_spans,
|
|
@@ -147,16 +102,9 @@ def _speech_overlap_excluding_quiet(start, end, speech, quiet):
|
|
|
147
102
|
|
|
148
103
|
def segment_overlaps_source_speech(seg, evidence):
|
|
149
104
|
"""Classify aggregate mix ownership across the complete narration interval."""
|
|
150
|
-
|
|
151
|
-
start, end = float(seg.get("start")), float(seg.get("end"))
|
|
152
|
-
except (AttributeError, TypeError, ValueError):
|
|
153
|
-
return bool(getattr(seg, "get", lambda *_: True)("overlaps_speech", True))
|
|
154
|
-
duration = max(0.0, end - start)
|
|
105
|
+
start, end = seg["start"], seg["end"]
|
|
155
106
|
quiet = evidence["quiet_windows"]
|
|
156
|
-
quiet_min = max(
|
|
157
|
-
0.3,
|
|
158
|
-
duration * float(CONFIG.get("quiet_overlap_min_ratio", 0.8) or 0.8),
|
|
159
|
-
)
|
|
107
|
+
quiet_min = max(0.3, (end - start) * CONFIG["quiet_overlap_min_ratio"])
|
|
160
108
|
speech = evidence["speech_spans"]
|
|
161
109
|
if speech:
|
|
162
110
|
return _speech_overlap_excluding_quiet(start, end, speech, quiet) > 0.05
|
|
@@ -167,12 +115,8 @@ def segment_overlaps_source_speech(seg, evidence):
|
|
|
167
115
|
return bool(seg.get("overlaps_speech", True))
|
|
168
116
|
|
|
169
117
|
|
|
170
|
-
def entry_overlaps_source_speech(
|
|
118
|
+
def entry_overlaps_source_speech(start, evidence, *, authored_overlap=True, tolerance=0.05):
|
|
171
119
|
"""Classify the entry instant; later quiet time cannot erase an unsafe start."""
|
|
172
|
-
try:
|
|
173
|
-
start = float(seg.get("start"))
|
|
174
|
-
except (AttributeError, TypeError, ValueError):
|
|
175
|
-
return True
|
|
176
120
|
if any(
|
|
177
121
|
row["start"] - tolerance <= start <= row["end"] + tolerance
|
|
178
122
|
for row in evidence["quiet_windows"]
|
|
@@ -187,18 +131,13 @@ def entry_overlaps_source_speech(seg, evidence, tolerance=0.05):
|
|
|
187
131
|
return False
|
|
188
132
|
if evidence["anchors"] or evidence["require_measured"]:
|
|
189
133
|
return True
|
|
190
|
-
return bool(
|
|
134
|
+
return bool(authored_overlap)
|
|
191
135
|
|
|
192
136
|
|
|
193
137
|
def measure_narration_speech_ownership(narration, work_dir, mode="full"):
|
|
194
138
|
"""Return narration copies with aggregate ownership derived from evidence."""
|
|
195
139
|
evidence = load_source_sentence_evidence(work_dir, mode=mode)
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
continue
|
|
201
|
-
item = dict(seg)
|
|
202
|
-
item["overlaps_speech"] = segment_overlaps_source_speech(item, evidence)
|
|
203
|
-
measured.append(item)
|
|
204
|
-
return measured
|
|
140
|
+
return [
|
|
141
|
+
{**seg, "overlaps_speech": segment_overlaps_source_speech(seg, evidence)}
|
|
142
|
+
for seg in narration
|
|
143
|
+
]
|
|
@@ -1,88 +1,49 @@
|
|
|
1
1
|
"""Fuse scenes, ASR, and quiet windows for narration planning."""
|
|
2
2
|
|
|
3
|
-
import importlib.util
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
from pathlib import Path
|
|
7
|
-
|
|
8
3
|
from lib import CONFIG
|
|
9
|
-
|
|
10
|
-
|
|
11
4
|
from agent_text import _overlap_seconds, _recommended_char_budget
|
|
12
5
|
from narration_lint import _validate_narration_budget
|
|
13
6
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
_deslop_qc_path = Path(__file__).with_name("deslop_qc.py")
|
|
18
|
-
_deslop_qc_spec = importlib.util.spec_from_file_location(
|
|
19
|
-
"deslop_qc", _deslop_qc_path
|
|
20
|
-
)
|
|
21
|
-
if _deslop_qc_spec is None or _deslop_qc_spec.loader is None:
|
|
22
|
-
raise
|
|
23
|
-
_deslop_qc_module = importlib.util.module_from_spec(_deslop_qc_spec)
|
|
24
|
-
_deslop_qc_spec.loader.exec_module(_deslop_qc_module)
|
|
25
|
-
analyze_deslop_qc = _deslop_qc_module.analyze_deslop_qc
|
|
7
|
+
|
|
8
|
+
def _quiet_windows(silence_periods):
|
|
9
|
+
return [qp for qp in silence_periods if not qp["has_speech"]]
|
|
26
10
|
|
|
27
11
|
|
|
28
12
|
def _align_narration_to_quiet(narration, scenes_analysis, silence_periods):
|
|
29
13
|
"""Recompute overlaps_speech from real quiet windows; keep the agent's timing.
|
|
30
14
|
|
|
31
15
|
The dense continuous-bed design places narration ON the pictured beat over a
|
|
32
|
-
ducked original bed, so
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
correct the overlaps_speech flag that the ducking stage consumes, leaving the
|
|
36
|
-
agent's start/end (and text) intact.
|
|
37
|
-
"""
|
|
38
|
-
if not silence_periods:
|
|
39
|
-
for n in narration:
|
|
40
|
-
n["overlaps_speech"] = True
|
|
41
|
-
return _validate_narration_budget(narration, scenes_analysis)
|
|
16
|
+
ducked original bed, so segments are never relocated into silence gaps. Only
|
|
17
|
+
the overlaps_speech flag that the ducking stage consumes is corrected, leaving
|
|
18
|
+
the agent's start/end (and text) intact.
|
|
42
19
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
quiet_ratio_min = float(CONFIG.get("quiet_overlap_min_ratio", 0.8) or 0.8)
|
|
47
|
-
|
|
48
|
-
# Run budget/dedup FIRST, then set the flag on the final (possibly merged) spans,
|
|
49
|
-
# so a dedup-merged beat's overlaps_speech reflects its extended timing, not its
|
|
50
|
-
# original shorter span (which the ducking stage in assemble.py would mis-duck).
|
|
20
|
+
Budget/dedup runs FIRST so a dedup-merged beat's overlaps_speech reflects its
|
|
21
|
+
extended timing, not its original shorter span.
|
|
22
|
+
"""
|
|
51
23
|
aligned = _validate_narration_budget(narration, scenes_analysis)
|
|
24
|
+
quiet_windows = _quiet_windows(silence_periods)
|
|
25
|
+
quiet_ratio_min = CONFIG["quiet_overlap_min_ratio"]
|
|
52
26
|
for n in aligned:
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
continue
|
|
59
|
-
seg_dur = max(0.0, seg_end - seg_start)
|
|
60
|
-
quiet_overlap = _quiet_overlap_seconds(seg_start, seg_end, quiet_windows)
|
|
27
|
+
seg_dur = n["end"] - n["start"]
|
|
28
|
+
quiet_overlap = sum(
|
|
29
|
+
_overlap_seconds(n["start"], n["end"], qw["start"], qw["end"])
|
|
30
|
+
for qw in quiet_windows
|
|
31
|
+
)
|
|
61
32
|
n["overlaps_speech"] = quiet_overlap < max(0.3, seg_dur * quiet_ratio_min)
|
|
62
|
-
|
|
63
33
|
return aligned
|
|
64
34
|
|
|
65
35
|
|
|
66
36
|
def _scene_asr_lines(asr_result, scene):
|
|
67
37
|
lines = []
|
|
68
|
-
for seg in asr_result
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
end = float(seg.get("end", start))
|
|
72
|
-
except (TypeError, ValueError):
|
|
73
|
-
continue
|
|
74
|
-
if scene["start"] < end and scene["end"] > start:
|
|
75
|
-
text = str(seg.get("text", "")).strip()
|
|
76
|
-
if text:
|
|
77
|
-
lines.append(f" [{start:.1f}-{end:.1f}] {text}")
|
|
38
|
+
for seg in asr_result:
|
|
39
|
+
if scene["start"] < seg["end"] and scene["end"] > seg["start"] and seg["text"]:
|
|
40
|
+
lines.append(f" [{seg['start']:.1f}-{seg['end']:.1f}] {seg['text']}")
|
|
78
41
|
return lines
|
|
79
42
|
|
|
80
43
|
|
|
81
44
|
def _quiet_windows_for_scene(silence_periods, scene):
|
|
82
45
|
windows = []
|
|
83
|
-
for qp in silence_periods
|
|
84
|
-
if not _silence_window_is_usable_quiet(qp):
|
|
85
|
-
continue
|
|
46
|
+
for qp in _quiet_windows(silence_periods):
|
|
86
47
|
if qp["start"] < scene["end"] and qp["end"] > scene["start"]:
|
|
87
48
|
start = max(qp["start"], scene["start"])
|
|
88
49
|
end = min(qp["end"], scene["end"])
|
|
@@ -91,87 +52,35 @@ def _quiet_windows_for_scene(silence_periods, scene):
|
|
|
91
52
|
return windows
|
|
92
53
|
|
|
93
54
|
|
|
94
|
-
def _silence_window_is_usable_quiet(qp):
|
|
95
|
-
if not isinstance(qp, dict):
|
|
96
|
-
return False
|
|
97
|
-
reason = str(qp.get("has_speech_reason", "")).lower()
|
|
98
|
-
granularity = str(qp.get("asr_granularity", "")).lower()
|
|
99
|
-
if reason in {
|
|
100
|
-
"coarse_asr_overlap_ignored",
|
|
101
|
-
"asr_overlap_low_confidence_quiet",
|
|
102
|
-
"coarse_asr_no_overlap",
|
|
103
|
-
}:
|
|
104
|
-
return True
|
|
105
|
-
if granularity == "coarse_grid" and qp.get("has_speech") is True:
|
|
106
|
-
return True
|
|
107
|
-
return not qp.get("has_speech", False)
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
def _quiet_overlap_seconds(start, end, quiet_windows):
|
|
111
|
-
overlap_seconds = 0.0
|
|
112
|
-
for qw in quiet_windows:
|
|
113
|
-
try:
|
|
114
|
-
if isinstance(qw, dict):
|
|
115
|
-
q_start = float(qw.get("start", 0))
|
|
116
|
-
q_end = float(qw.get("end", q_start))
|
|
117
|
-
else:
|
|
118
|
-
q_start, q_end = qw
|
|
119
|
-
q_start = float(q_start)
|
|
120
|
-
q_end = float(q_end)
|
|
121
|
-
except (TypeError, ValueError):
|
|
122
|
-
continue
|
|
123
|
-
overlap_seconds += _overlap_seconds(start, end, q_start, q_end)
|
|
124
|
-
return overlap_seconds
|
|
125
|
-
|
|
126
|
-
|
|
127
55
|
def _build_timeline_fusion(scenes, asr_segments, silence_periods):
|
|
128
56
|
"""Fuse VLM scenes, ASR dialogue and quiet narration slots on one timeline."""
|
|
129
57
|
fusion = []
|
|
130
|
-
quiet_windows =
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
for scene in scenes or []:
|
|
134
|
-
try:
|
|
135
|
-
start = float(scene.get("start", 0))
|
|
136
|
-
end = float(scene.get("end", start))
|
|
137
|
-
except (TypeError, ValueError):
|
|
138
|
-
continue
|
|
58
|
+
quiet_windows = _quiet_windows(silence_periods)
|
|
59
|
+
for scene in scenes:
|
|
60
|
+
start, end = scene["start"], scene["end"]
|
|
139
61
|
dialogue_segments = []
|
|
140
62
|
dialogue_overlap = 0.0
|
|
141
|
-
for seg in asr_segments
|
|
142
|
-
|
|
143
|
-
continue
|
|
144
|
-
try:
|
|
145
|
-
seg_start = float(seg.get("start", 0))
|
|
146
|
-
seg_end = float(seg.get("end", seg_start))
|
|
147
|
-
except (TypeError, ValueError):
|
|
148
|
-
continue
|
|
149
|
-
overlap = _overlap_seconds(start, end, seg_start, seg_end)
|
|
63
|
+
for seg in asr_segments:
|
|
64
|
+
overlap = _overlap_seconds(start, end, seg["start"], seg["end"])
|
|
150
65
|
if overlap <= 0:
|
|
151
66
|
continue
|
|
152
|
-
text = str(seg.get("text", "")).strip()
|
|
153
67
|
dialogue_overlap += overlap
|
|
154
68
|
dialogue_segments.append(
|
|
155
69
|
{
|
|
156
|
-
"start": round(
|
|
157
|
-
"end": round(
|
|
70
|
+
"start": round(seg["start"], 2),
|
|
71
|
+
"end": round(seg["end"], 2),
|
|
158
72
|
"overlap_seconds": round(overlap, 2),
|
|
159
|
-
"text": text,
|
|
73
|
+
"text": seg["text"],
|
|
160
74
|
}
|
|
161
75
|
)
|
|
162
76
|
|
|
163
77
|
narration_slots = []
|
|
164
78
|
for window in quiet_windows:
|
|
165
|
-
|
|
166
|
-
w_start = float(window.get("start", 0))
|
|
167
|
-
w_end = float(window.get("end", w_start))
|
|
168
|
-
except (TypeError, ValueError):
|
|
169
|
-
continue
|
|
170
|
-
overlap = _overlap_seconds(start, end, w_start, w_end)
|
|
79
|
+
overlap = _overlap_seconds(start, end, window["start"], window["end"])
|
|
171
80
|
if overlap <= 0:
|
|
172
81
|
continue
|
|
173
|
-
slot_start = max(start,
|
|
174
|
-
slot_end = min(end,
|
|
82
|
+
slot_start = max(start, window["start"])
|
|
83
|
+
slot_end = min(end, window["end"])
|
|
175
84
|
narration_slots.append(
|
|
176
85
|
{
|
|
177
86
|
"start": round(slot_start, 2),
|
|
@@ -183,9 +92,9 @@ def _build_timeline_fusion(scenes, asr_segments, silence_periods):
|
|
|
183
92
|
|
|
184
93
|
fusion.append(
|
|
185
94
|
{
|
|
186
|
-
"scene_id": scene
|
|
95
|
+
"scene_id": scene["scene_id"],
|
|
187
96
|
"time_range": [round(start, 2), round(end, 2)],
|
|
188
|
-
"visual_description": scene
|
|
97
|
+
"visual_description": scene["description"],
|
|
189
98
|
"depth_analysis": scene.get("depth_analysis", ""),
|
|
190
99
|
"frame_facts": scene.get("frame_facts", {}),
|
|
191
100
|
"dialogue_segments": dialogue_segments,
|
|
@@ -220,7 +220,7 @@ def build_agent_brief(
|
|
|
220
220
|
lines.extend(_format_timeline_fusion_for_brief(timeline_fusion))
|
|
221
221
|
lines.extend(_format_sentence_entry_anchors_for_brief(work_dir, edit_mode))
|
|
222
222
|
|
|
223
|
-
overview_text = (mimo_overview.get("content"
|
|
223
|
+
overview_text = (mimo_overview or {}).get("content", "").strip()
|
|
224
224
|
if overview_text:
|
|
225
225
|
lines.extend(
|
|
226
226
|
[
|