@oh-story/dsh 0.1.9 → 0.1.11
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 +14 -14
- package/lib/client.js +209 -73
- package/lib/client.js.map +3 -3
- package/lib/drama/manifest.json +248 -168
- package/lib/drama/skills/short-drama/SKILL.md +16 -4
- package/lib/drama/skills/short-drama/assets/project-template/short-drama.json +3 -0
- package/lib/drama/skills/short-drama/references/creator-documents.md +3 -0
- package/lib/drama/skills/short-drama/references/knowhow-index.md +15 -0
- package/lib/drama/skills/short-drama/scripts/creator_markdown_check.py +14 -0
- package/lib/drama/skills/short-drama/scripts/project_tool.py +131 -0
- package/lib/drama/skills/short-drama-assets/SKILL.md +2 -1
- package/lib/drama/skills/short-drama-assets/references/asset-review-checklist.md +2 -0
- package/lib/drama/skills/short-drama-assets/references/character-and-look.md +10 -0
- package/lib/drama/skills/short-drama-assets/references/continuity-lock.md +6 -9
- package/lib/drama/skills/short-drama-assets/references/era-anchors.md +37 -0
- package/lib/drama/skills/short-drama-assets/references/stage-contract.md +2 -0
- package/lib/drama/skills/short-drama-assets/references/voice-direction.md +28 -0
- package/lib/drama/skills/short-drama-develop/SKILL.md +10 -4
- package/lib/drama/skills/short-drama-develop/assets/adaptation-map.example.jsonl +1 -1
- package/lib/drama/skills/short-drama-develop/assets/creative-brief.md +8 -0
- package/lib/drama/skills/short-drama-develop/references/adaptation-craft.md +80 -13
- package/lib/drama/skills/short-drama-develop/references/episode-design.md +95 -12
- package/lib/drama/skills/short-drama-develop/references/genre-and-hook-playbook.md +9 -5
- package/lib/drama/skills/short-drama-develop/references/genre-cards//345/244/215/344/273/207/346/211/223/350/204/270.md +1 -1
- package/lib/drama/skills/short-drama-develop/references/genre-cards//347/263/273/347/273/237/346/265/201.md +51 -0
- package/lib/drama/skills/short-drama-develop/references/genre-cards//351/200/206/350/242/255.md +51 -0
- package/lib/drama/skills/short-drama-develop/references/genre-cards//351/207/215/347/224/237/347/251/277/350/266/212.md +51 -0
- package/lib/drama/skills/short-drama-develop/references/genre-cards.md +10 -7
- package/lib/drama/skills/short-drama-develop/references/premise-devices.md +15 -9
- package/lib/drama/skills/short-drama-develop/references/serial-character-and-memory.md +3 -2
- package/lib/drama/skills/short-drama-develop/references/stage-contract.md +14 -7
- package/lib/drama/skills/short-drama-develop/references/story-craft.md +7 -3
- package/lib/drama/skills/short-drama-edit/SKILL.md +65 -44
- package/lib/drama/skills/short-drama-edit/assets/remotion/README.md +49 -13
- package/lib/drama/skills/short-drama-edit/assets/remotion/package.json +4 -2
- package/lib/drama/skills/short-drama-edit/assets/remotion/src/Overlay.tsx +31 -0
- package/lib/drama/skills/short-drama-edit/assets/remotion/src/Root.tsx +5 -5
- package/lib/drama/skills/short-drama-edit/assets/remotion/src/Subtitles.tsx +68 -69
- package/lib/drama/skills/short-drama-edit/assets/remotion/src/font.ts +39 -29
- package/lib/drama/skills/short-drama-edit/assets/remotion/src/rules.mjs +188 -0
- package/lib/drama/skills/short-drama-edit/assets/remotion/src/schema.ts +26 -13
- package/lib/drama/skills/short-drama-edit/assets/remotion/src/screen/Card.tsx +72 -0
- package/lib/drama/skills/short-drama-edit/assets/remotion/src/screen/CornerChip.tsx +39 -0
- package/lib/drama/skills/short-drama-edit/assets/remotion/src/screen/ScreenText.tsx +36 -0
- package/lib/drama/skills/short-drama-edit/assets/remotion/src/screen/SystemPanel.tsx +44 -0
- package/lib/drama/skills/short-drama-edit/assets/remotion/src/screen/TaskPanel.tsx +100 -0
- package/lib/drama/skills/short-drama-edit/assets/remotion/src/screen/chrome.tsx +197 -0
- package/lib/drama/skills/short-drama-edit/assets/remotion/src/screen/tokens.ts +62 -0
- package/lib/drama/skills/short-drama-edit/assets/remotion/tsconfig.json +1 -0
- package/lib/drama/skills/short-drama-edit/assets//345/211/252/350/276/221/345/215/225.md +6 -4
- package/lib/drama/skills/short-drama-edit/references/cut-craft.md +26 -67
- package/lib/drama/skills/short-drama-edit/references/delivery-verify.md +31 -56
- package/lib/drama/skills/short-drama-edit/references/generated-footage.md +42 -78
- package/lib/drama/skills/short-drama-edit/references/sound-and-subtitles.md +44 -92
- package/lib/drama/skills/short-drama-edit/references/stage-contract.md +14 -16
- package/lib/drama/skills/short-drama-edit/scripts/edit_tool.py +1272 -125
- package/lib/drama/skills/short-drama-edit/scripts/selftest.py +113 -9
- package/lib/drama/skills/short-drama-image-prompts/SKILL.md +2 -0
- package/lib/drama/skills/short-drama-image-prompts/references/common-recipe.md +11 -0
- package/lib/drama/skills/short-drama-novel-analyze/SKILL.md +3 -3
- package/lib/drama/skills/short-drama-novel-analyze/assets/episode-candidate.example.jsonl +2 -2
- package/lib/drama/skills/short-drama-novel-analyze/references/adaptation-triage.md +8 -4
- package/lib/drama/skills/short-drama-novel-analyze/references/adaptation-value.md +10 -3
- package/lib/drama/skills/short-drama-novel-analyze/references/aggregation-and-entities.md +25 -1
- package/lib/drama/skills/short-drama-novel-analyze/references/stage-contract.md +1 -0
- package/lib/drama/skills/short-drama-produce/SKILL.md +5 -1
- package/lib/drama/skills/short-drama-produce/references/adapter-contract.md +14 -20
- package/lib/drama/skills/short-drama-produce/references/providers/minimax-h3-video.md +8 -26
- package/lib/drama/skills/short-drama-produce/references/providers/minimax-speech.md +41 -0
- package/lib/drama/skills/short-drama-produce/scripts/provider_adapters.py +142 -1
- package/lib/drama/skills/short-drama-review/SKILL.md +4 -5
- package/lib/drama/skills/short-drama-review/references/production-quality-gates.md +6 -19
- package/lib/drama/skills/short-drama-review/references/review-method.md +6 -1
- package/lib/drama/skills/short-drama-review/references/rubric-assets-prompts.md +11 -0
- package/lib/drama/skills/short-drama-review/references/rubric-story-script.md +77 -3
- package/lib/drama/skills/short-drama-review/references/rubric-visual-motion.md +24 -35
- package/lib/drama/skills/short-drama-review/references/stage-contract.md +3 -0
- package/lib/drama/skills/short-drama-storyboard/SKILL.md +23 -11
- package/lib/drama/skills/short-drama-storyboard/references/blocking-playbooks.md +1 -1
- package/lib/drama/skills/short-drama-storyboard/references/lighting-craft.md +39 -0
- package/lib/drama/skills/short-drama-storyboard/references/production-shot-grammar.md +2 -2
- package/lib/drama/skills/short-drama-storyboard/references/shot-craft.md +139 -34
- package/lib/drama/skills/short-drama-storyboard/references/stage-contract.md +5 -3
- package/lib/drama/skills/short-drama-video-prompts/SKILL.md +15 -12
- package/lib/drama/skills/short-drama-video-prompts/references/camera-audio-continuity.md +29 -0
- package/lib/drama/skills/short-drama-video-prompts/references/delivery-profile.md +4 -5
- package/lib/drama/skills/short-drama-video-prompts/references/generability.md +4 -2
- package/lib/drama/skills/short-drama-video-prompts/references/minimax-h3.md +52 -39
- package/lib/drama/skills/short-drama-video-prompts/references/motion-recipe.md +24 -21
- package/lib/drama/skills/short-drama-video-prompts/references/performance-action-timing.md +9 -23
- package/lib/drama/skills/short-drama-video-prompts/references/production-prompt-grammar.md +14 -26
- package/lib/drama/skills/short-drama-video-prompts/references/review-and-fixtures.md +1 -1
- package/lib/drama/skills/short-drama-video-prompts/references/seedance-2.0.md +2 -3
- package/lib/drama/skills/short-drama-video-prompts/references/seedance-2.5.md +34 -9
- package/lib/drama/skills/short-drama-video-prompts/references/stage-contract.md +3 -2
- package/lib/drama/skills/short-drama-video-prompts/references/target-model-profile.md +8 -1
- package/lib/drama/skills/short-drama-video-prompts/references/wan-3.0.md +103 -0
- package/lib/drama/skills/short-drama-video-prompts/scripts/motion_timing_check.py +8 -7
- package/lib/drama/skills/short-drama-write/SKILL.md +10 -7
- package/lib/drama/skills/short-drama-write/references/dialogue-craft.md +1 -1
- package/lib/drama/skills/short-drama-write/references/script-craft.md +56 -3
- package/lib/drama/skills/short-drama-write/references/stage-contract.md +4 -0
- package/lib/drama/skills/short-drama-write/scripts/duration_estimate.py +48 -2
- package/lib/index.js +346 -98
- package/lib/index.js.map +4 -4
- package/lib/novel-to-game/README.md +299 -59
- package/lib/novel-to-game/README_ZH.md +299 -56
- package/lib/novel-to-game/VERSION +1 -1
- package/lib/novel-to-game/examples/jin-ping-mei/build/BUILD_BRIEF.md +3 -1
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/RUN.md +11 -3
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/css/new-guofeng.css +397 -8
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/css/style.css +0 -31
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/data.js +71 -73
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/engine.js +78 -89
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/main.js +37 -14
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/text.js +5 -5
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/package.json +2 -1
- package/lib/novel-to-game/examples/jin-ping-mei/qa/README.md +11 -16
- package/lib/novel-to-game/examples/jin-ping-mei/qa/verification.json +11 -11
- package/lib/novel-to-game/examples/jin-ping-mei/qa/verify_playable_model.mjs +23 -0
- package/lib/novel-to-game/manifest.json +77 -87
- package/lib/novel-to-game/skills/game-art-direction/SKILL.md +7 -12
- package/lib/novel-to-game/skills/game-art-direction/references/art-direction-method.md +27 -106
- package/lib/novel-to-game/skills/game-build/SKILL.md +13 -14
- package/lib/novel-to-game/skills/game-build/references/blender-asset-workflow.md +24 -0
- package/lib/novel-to-game/skills/game-build/references/build-brief-contract.md +45 -82
- package/lib/novel-to-game/skills/game-build/references/generative-media-pipeline.md +14 -45
- package/lib/novel-to-game/skills/game-build/references/playable-model-contract.md +30 -65
- package/lib/novel-to-game/skills/game-build/references/tts-production-contract.md +13 -27
- package/lib/novel-to-game/skills/game-concept/SKILL.md +13 -15
- package/lib/novel-to-game/skills/game-concept/references/concept-method.md +28 -75
- package/lib/novel-to-game/skills/game-qa/SKILL.md +6 -7
- package/lib/novel-to-game/skills/game-qa/references/test-design-method.md +9 -5
- package/lib/novel-to-game/skills/game-world-design/SKILL.md +12 -16
- package/lib/novel-to-game/skills/game-world-design/references/narrative-design-method.md +23 -72
- package/lib/novel-to-game/skills/game-world-design/references/numeric-design-method.md +14 -48
- package/lib/novel-to-game/skills/game-world-design/references/world-design-method.md +36 -103
- package/lib/novel-to-game/skills/novel-game-analyze/SKILL.md +4 -10
- package/lib/novel-to-game/skills/novel-game-analyze/references/gameability-protocol.md +4 -4
- package/lib/novel-to-game/skills/novel-to-game/SKILL.md +9 -21
- package/lib/novel-to-game/skills/novel-to-game/references/intake-method.md +5 -4
- package/lib/novel-to-game/skills/novel-to-game/references/pipeline-contract.md +5 -8
- package/lib/oh-story/manifest.json +607 -327
- package/lib/oh-story/roles/chapter-extractor.md +177 -237
- package/lib/oh-story/roles/character-designer.md +1 -2
- package/lib/oh-story/roles/consistency-checker.md +15 -13
- package/lib/oh-story/roles/narrative-writer.md +36 -38
- package/lib/oh-story/roles/story-architect.md +9 -61
- package/lib/oh-story/roles/story-explorer.md +20 -125
- package/lib/oh-story/roles/story-researcher.md +0 -1
- package/lib/oh-story/skills/story/SKILL.md +15 -9
- package/lib/oh-story/skills/story/VERSION +1 -1
- package/lib/oh-story/skills/story/references/author-memory-maintenance.md +75 -0
- package/lib/oh-story/skills/story/references/author-memory.md +46 -94
- package/lib/oh-story/skills/story/scripts/author_memory_commit.py +934 -170
- package/lib/oh-story/skills/story-cover/SKILL.md +10 -1
- package/lib/oh-story/skills/story-deslop/SKILL.md +59 -73
- package/lib/oh-story/skills/story-deslop/references/agent-calls.md +5 -0
- package/lib/oh-story/skills/story-deslop/references/anti-ai-writing.md +12 -11
- package/lib/oh-story/skills/story-deslop/references/author-memory-maintenance.md +75 -0
- package/lib/oh-story/skills/story-deslop/references/author-memory.md +46 -94
- package/lib/oh-story/skills/story-deslop/references/banned-words.md +2 -2
- package/lib/oh-story/skills/story-deslop/references/deslop-gates.md +22 -5
- package/lib/oh-story/skills/story-deslop/references/style-resolution.md +4 -4
- package/lib/oh-story/skills/story-deslop/scripts/author_memory_commit.py +934 -170
- package/lib/oh-story/skills/story-deslop/scripts/check-ai-patterns.js +155 -8
- package/lib/oh-story/skills/story-import/SKILL.md +64 -584
- package/lib/oh-story/skills/story-import/references/character-state-reverse.md +1 -1
- package/lib/oh-story/skills/story-import/references/deep-analysis.md +47 -0
- package/lib/oh-story/skills/story-import/references/format-and-structure.md +1 -1
- package/lib/oh-story/skills/story-import/references/import-report.md +64 -0
- package/lib/oh-story/skills/story-import/references/import-special-cases.md +34 -0
- package/lib/oh-story/skills/story-import/references/import-tracking.md +95 -0
- package/lib/oh-story/skills/story-import/references/length-routing.md +1 -1
- package/lib/oh-story/skills/story-import/references/outline-reverse-rules.md +79 -0
- package/lib/oh-story/skills/story-import/references/outline-reverse.md +24 -0
- package/lib/oh-story/skills/story-import/references/state-tracking.md +4 -69
- package/lib/oh-story/skills/story-import/references/structure-mapping-long.md +98 -92
- package/lib/oh-story/skills/story-import/references/structure-mapping-short.md +24 -12
- package/lib/oh-story/skills/story-import/references/tracking-initialization.md +1 -1
- package/lib/oh-story/skills/story-import/references/tracking-transaction.md +15 -11
- package/lib/oh-story/skills/story-import/scripts/build_outline_brief.py +332 -0
- package/lib/oh-story/skills/story-import/scripts/check-outline-contract.js +132 -27
- package/lib/oh-story/skills/story-import/scripts/tracking_commit.py +478 -15
- package/lib/oh-story/skills/story-import/scripts/wordcount_core.py +182 -23
- package/lib/oh-story/skills/story-long-analyze/SKILL.md +81 -270
- package/lib/oh-story/skills/story-long-analyze/references/author-facing.md +252 -0
- package/lib/oh-story/skills/story-long-analyze/references/deconstruction-notes.md +3 -3
- package/lib/oh-story/skills/story-long-analyze/references/final-checks.md +19 -0
- package/lib/oh-story/skills/story-long-analyze/references/index-rebuild.md +11 -0
- package/lib/oh-story/skills/story-long-analyze/references/inspiration-library.md +105 -0
- package/lib/oh-story/skills/story-long-analyze/references/pipeline-ops.md +138 -64
- package/lib/oh-story/skills/story-long-analyze/references/semantic-acceptance-fixtures.md +84 -0
- package/lib/oh-story/skills/story-long-analyze/references/stage1-golden-chapters.md +128 -0
- package/lib/oh-story/skills/story-long-analyze/references/stage2-extraction.md +229 -0
- package/lib/oh-story/skills/story-long-analyze/references/stage3-plot-rhythm.md +480 -0
- package/lib/oh-story/skills/story-long-analyze/references/stage4-characters-settings.md +195 -0
- package/lib/oh-story/skills/story-long-analyze/references/stage5-report.md +33 -0
- package/lib/oh-story/skills/story-long-analyze/references/style-profile-generator.md +55 -128
- package/lib/oh-story/skills/story-long-analyze/references/style-profile-protocol.md +6 -6
- package/lib/oh-story/skills/story-long-analyze/references/synthesis-inputs.md +39 -0
- package/lib/oh-story/skills/story-long-analyze/scripts/build_chapter_index.py +466 -0
- package/lib/oh-story/skills/story-long-analyze/scripts/inspect_existing_assets.py +702 -0
- package/lib/oh-story/skills/story-long-analyze/scripts/inspiration_index.py +864 -0
- package/lib/oh-story/skills/story-long-analyze/scripts/manage_analysis_run.py +1145 -0
- package/lib/oh-story/skills/story-long-analyze/scripts/render_relation_chart.py +425 -0
- package/lib/oh-story/skills/story-long-scan/SKILL.md +45 -194
- package/lib/oh-story/skills/story-long-scan/references/platform-ciweimao.md +19 -0
- package/lib/oh-story/skills/story-long-scan/references/platform-fanqie.md +45 -0
- package/lib/oh-story/skills/story-long-scan/references/platform-jjwxc.md +28 -0
- package/lib/oh-story/skills/story-long-scan/references/platform-qidian.md +35 -0
- package/lib/oh-story/skills/story-long-scan/references/platform-qimao.md +27 -0
- package/lib/oh-story/skills/story-long-scan/references/topic-decision.md +6 -2
- package/lib/oh-story/skills/story-long-write/SKILL.md +49 -81
- package/lib/oh-story/skills/story-long-write/references/agent-calls.md +47 -0
- package/lib/oh-story/skills/story-long-write/references/anti-ai-writing.md +12 -11
- package/lib/oh-story/skills/story-long-write/references/artifact-protocols.md +24 -202
- package/lib/oh-story/skills/story-long-write/references/author-memory-maintenance.md +75 -0
- package/lib/oh-story/skills/story-long-write/references/author-memory.md +46 -94
- package/lib/oh-story/skills/story-long-write/references/banned-words.md +2 -2
- package/lib/oh-story/skills/story-long-write/references/benchmark-recall.md +7 -11
- package/lib/oh-story/skills/story-long-write/references/character-relations.md +49 -1
- package/lib/oh-story/skills/story-long-write/references/cross-book-recall.md +26 -0
- package/lib/oh-story/skills/story-long-write/references/deslop-gates.md +171 -0
- package/lib/oh-story/skills/story-long-write/references/dialogue-mastery.md +4 -0
- package/lib/oh-story/skills/story-long-write/references/emotion-on-page.md +116 -0
- package/lib/oh-story/skills/story-long-write/references/emotional-arc-design.md +1 -114
- package/lib/oh-story/skills/story-long-write/references/emotional-methods.md +2 -2
- package/lib/oh-story/skills/story-long-write/references/genre-prose-cards.md +1 -1
- package/lib/oh-story/skills/story-long-write/references/long-chapter-quality.md +7 -15
- package/lib/oh-story/skills/story-long-write/references/long-format.md +0 -8
- package/lib/oh-story/skills/story-long-write/references/long-genre-catalog.md +1 -1
- package/lib/oh-story/skills/story-long-write/references/long-genre-mechanics.md +3 -3
- package/lib/oh-story/skills/story-long-write/references/opening-design.md +2 -2
- package/lib/oh-story/skills/story-long-write/references/outline-conflict.md +4 -4
- package/lib/oh-story/skills/story-long-write/references/outline-methods.md +7 -7
- package/lib/oh-story/skills/story-long-write/references/outline-rhythm.md +3 -3
- package/lib/oh-story/skills/story-long-write/references/outline-structure-theory.md +14 -69
- package/lib/oh-story/skills/story-long-write/references/plot-frameworks.md +1 -1
- package/lib/oh-story/skills/story-long-write/references/plot-special-topics.md +2 -2
- package/lib/oh-story/skills/story-long-write/references/project-files.md +19 -13
- package/lib/oh-story/skills/story-long-write/references/reader-contract-and-progression.md +13 -5
- package/lib/oh-story/skills/story-long-write/references/reference-index.md +5 -6
- package/lib/oh-story/skills/story-long-write/references/stop-and-fix.md +9 -0
- package/lib/oh-story/skills/story-long-write/references/style-combat-face.md +1 -58
- 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 +4 -4
- package/lib/oh-story/skills/story-long-write/references/tracking-initialization.md +1 -1
- package/lib/oh-story/skills/story-long-write/references/tracking-transaction.md +15 -11
- package/lib/oh-story/skills/story-long-write/references/workflow-chapter.md +81 -121
- package/lib/oh-story/skills/story-long-write/references/workflow-daily.md +41 -139
- package/lib/oh-story/skills/story-long-write/references/workflow-outline.md +157 -0
- package/lib/oh-story/skills/story-long-write/references/workflow-revision.md +57 -67
- package/lib/oh-story/skills/story-long-write/references/workflow-setup.md +112 -253
- package/lib/oh-story/skills/story-long-write/references/workflow-volume.md +142 -0
- package/lib/oh-story/skills/story-long-write/references/writer-prompt-fallback.md +6 -4
- package/lib/oh-story/skills/story-long-write/references/writing-craft.md +19 -65
- package/lib/oh-story/skills/story-long-write/scripts/author_memory_commit.py +934 -170
- package/lib/oh-story/skills/story-long-write/scripts/build_architect_brief.py +260 -0
- package/lib/oh-story/skills/story-long-write/scripts/build_writer_prompt.py +226 -25
- package/lib/oh-story/skills/story-long-write/scripts/check-ai-patterns.js +155 -8
- package/lib/oh-story/skills/story-long-write/scripts/check-outline-contract.js +132 -27
- package/lib/oh-story/skills/story-long-write/scripts/check-outline-copy.js +7 -2
- package/lib/oh-story/skills/story-long-write/scripts/outline_view.py +7 -4
- package/lib/oh-story/skills/story-long-write/scripts/storyctl.py +304 -46
- package/lib/oh-story/skills/story-long-write/scripts/tracking_commit.py +478 -15
- package/lib/oh-story/skills/story-long-write/scripts/wordcount_core.py +182 -23
- package/lib/oh-story/skills/story-review/SKILL.md +31 -302
- package/lib/oh-story/skills/story-review/references/agent-prompts.md +184 -0
- package/lib/oh-story/skills/story-review/references/anti-ai-writing.md +12 -11
- package/lib/oh-story/skills/story-review/references/author-memory-maintenance.md +75 -0
- package/lib/oh-story/skills/story-review/references/author-memory.md +46 -94
- package/lib/oh-story/skills/story-review/references/banned-words.md +2 -2
- package/lib/oh-story/skills/story-review/references/batch-review.md +19 -0
- package/lib/oh-story/skills/story-review/references/character-relations.md +49 -1
- package/lib/oh-story/skills/story-review/references/dialogue-mastery.md +4 -0
- package/lib/oh-story/skills/story-review/references/review-quality.md +6 -11
- package/lib/oh-story/skills/story-review/references/review-tracking.md +10 -0
- package/lib/oh-story/skills/story-review/references/solo.md +52 -0
- package/lib/oh-story/skills/story-review/references/style-resolution.md +4 -4
- package/lib/oh-story/skills/story-review/references/tracking-initialization.md +1 -1
- package/lib/oh-story/skills/story-review/references/tracking-transaction.md +15 -11
- package/lib/oh-story/skills/story-review/scripts/author_memory_commit.py +934 -170
- package/lib/oh-story/skills/story-review/scripts/check-ai-patterns.js +155 -8
- package/lib/oh-story/skills/story-review/scripts/tracking_commit.py +478 -15
- package/lib/oh-story/skills/story-review/scripts/wordcount_core.py +182 -23
- package/lib/oh-story/skills/story-setup/SKILL.md +71 -452
- package/lib/oh-story/skills/story-setup/references/agent-references/agent-quality.md +4 -4
- package/lib/oh-story/skills/story-setup/references/agent-references/anti-ai-writing.md +12 -11
- package/lib/oh-story/skills/story-setup/references/agent-references/banned-words.md +2 -2
- package/lib/oh-story/skills/story-setup/references/agent-references/benchmark-style-load.md +107 -0
- package/lib/oh-story/skills/story-setup/references/agent-references/character-relations.md +49 -1
- package/lib/oh-story/skills/story-setup/references/agent-references/deslop-gates.md +22 -5
- package/lib/oh-story/skills/story-setup/references/agent-references/dialogue-mastery.md +4 -0
- package/lib/oh-story/skills/story-setup/references/agent-references/emotion-on-page.md +116 -0
- package/lib/oh-story/skills/story-setup/references/agent-references/emotional-arc-design.md +1 -114
- package/lib/oh-story/skills/story-setup/references/agent-references/format-and-structure.md +1 -1
- package/lib/oh-story/skills/story-setup/references/agent-references/genre-prose-cards.md +1 -1
- package/lib/oh-story/skills/story-setup/references/agent-references/long-emotional-methods.md +2 -2
- package/lib/oh-story/skills/story-setup/references/agent-references/long-genre-catalog.md +1 -1
- package/lib/oh-story/skills/story-setup/references/agent-references/long-genre-mechanics.md +3 -3
- package/lib/oh-story/skills/story-setup/references/agent-references/long-quality.md +7 -5
- package/lib/oh-story/skills/story-setup/references/agent-references/opening-design.md +2 -2
- package/lib/oh-story/skills/story-setup/references/agent-references/outline-conflict.md +4 -4
- package/lib/oh-story/skills/story-setup/references/agent-references/outline-methods.md +7 -7
- package/lib/oh-story/skills/story-setup/references/agent-references/outline-rhythm.md +3 -3
- package/lib/oh-story/skills/story-setup/references/agent-references/short-emotional-methods.md +1 -1
- package/lib/oh-story/skills/story-setup/references/agent-references/short-genre-formulas.md +0 -15
- package/lib/oh-story/skills/story-setup/references/agent-references/short-reversal.md +1 -1
- package/lib/oh-story/skills/story-setup/references/agent-references/style-combat-face.md +1 -58
- 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 +4 -4
- package/lib/oh-story/skills/story-setup/references/agent-references/writing-craft.md +19 -65
- package/lib/oh-story/skills/story-setup/references/diagnostics.md +4 -4
- package/lib/oh-story/skills/story-short-analyze/SKILL.md +56 -110
- package/lib/oh-story/skills/story-short-analyze/references/analysis-character-relations.md +1 -1
- package/lib/oh-story/skills/story-short-analyze/references/analysis-method.md +42 -0
- package/lib/oh-story/skills/story-short-analyze/references/optional-modules.md +97 -0
- package/lib/oh-story/skills/story-short-analyze/references/output-contract.md +9 -47
- package/lib/oh-story/skills/story-short-analyze/references/quality-checklist.md +80 -0
- package/lib/oh-story/skills/story-short-analyze/references/source-story-quality.md +4 -4
- package/lib/oh-story/skills/story-short-analyze/references/stage2-3-structure-emotion.md +308 -0
- package/lib/oh-story/skills/story-short-analyze/references/stage4-6-reversal-summary.md +611 -0
- package/lib/oh-story/skills/story-short-scan/SKILL.md +35 -65
- package/lib/oh-story/skills/story-short-scan/references/manual-rank-input.md +36 -0
- package/lib/oh-story/skills/story-short-scan/references/platform-dianzhong.md +24 -0
- package/lib/oh-story/skills/story-short-scan/references/platform-heiyan.md +21 -0
- package/lib/oh-story/skills/story-short-scan/references/topic-match.md +38 -0
- package/lib/oh-story/skills/story-short-write/SKILL.md +23 -175
- package/lib/oh-story/skills/story-short-write/references/agent-calls.md +13 -0
- package/lib/oh-story/skills/story-short-write/references/author-memory-maintenance.md +75 -0
- package/lib/oh-story/skills/story-short-write/references/author-memory.md +46 -94
- package/lib/oh-story/skills/story-short-write/references/banned-words.md +2 -2
- package/lib/oh-story/skills/story-short-write/references/benchmark-recall.md +1 -21
- package/lib/oh-story/skills/story-short-write/references/dialogue-mastery.md +4 -0
- package/lib/oh-story/skills/story-short-write/references/emotional-methods.md +1 -1
- package/lib/oh-story/skills/story-short-write/references/genre-writing-formulas.md +0 -15
- package/lib/oh-story/skills/story-short-write/references/output-contract.md +9 -47
- package/lib/oh-story/skills/story-short-write/references/reference-index.md +17 -47
- package/lib/oh-story/skills/story-short-write/references/scene-craft.md +40 -0
- package/lib/oh-story/skills/story-short-write/references/short-craft.md +7 -43
- package/lib/oh-story/skills/story-short-write/references/short-format.md +3 -3
- package/lib/oh-story/skills/story-short-write/references/short-prose-quality.md +2 -1
- package/lib/oh-story/skills/story-short-write/references/short-reversal.md +1 -1
- package/lib/oh-story/skills/story-short-write/references/style-resolution.md +4 -4
- package/lib/oh-story/skills/story-short-write/references/submission-craft.md +1 -1
- package/lib/oh-story/skills/story-short-write/references/villain-and-reveal.md +5 -10
- package/lib/oh-story/skills/story-short-write/references/workflow-design.md +21 -13
- package/lib/oh-story/skills/story-short-write/references/workflow-draft.md +116 -0
- package/lib/oh-story/skills/story-short-write/references/workflow-revision.md +99 -0
- package/lib/oh-story/skills/story-short-write/references/writing-workflow.md +7 -119
- package/lib/oh-story/skills/story-short-write/scripts/author_memory_commit.py +934 -170
- package/lib/oh-story/skills/story-short-write/scripts/check-ai-patterns.js +155 -8
- package/lib/oh-story/skills/story-short-write/scripts/check-outline-copy.js +7 -2
- package/lib/types/client/drama-production-view.d.ts.map +1 -1
- package/lib/types/client/file-activity.d.ts +19 -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-prompts.d.ts +7 -0
- package/lib/types/client/production-prompts.d.ts.map +1 -1
- package/lib/types/client/production-runtime.d.ts +16 -0
- package/lib/types/client/production-runtime.d.ts.map +1 -1
- package/lib/types/client/video-studio.d.ts.map +1 -1
- package/lib/types/drama-adapters.d.ts +5 -3
- package/lib/types/drama-adapters.d.ts.map +1 -1
- package/lib/types/native-hooks.d.ts +53 -1
- package/lib/types/native-hooks.d.ts.map +1 -1
- package/lib/types/role-identity.d.ts +17 -0
- package/lib/types/role-identity.d.ts.map +1 -0
- package/lib/types/role-tool.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/types/workspace-route.d.ts.map +1 -1
- package/lib/video-recap/CHANGELOG.md +60 -1
- package/lib/video-recap/README.en.md +348 -137
- package/lib/video-recap/README.md +340 -136
- package/lib/video-recap/manifest.json +406 -251
- package/lib/video-recap/skills/video-assemble/SKILL.md +30 -15
- package/lib/video-recap/skills/video-assemble/references/audio-modes.md +63 -0
- package/lib/video-recap/skills/video-assemble/references/explicit-audio-mix.md +75 -0
- package/lib/video-recap/skills/video-assemble/references/foreground-compose.md +118 -0
- package/lib/video-recap/skills/video-assemble/references/narration-adoption.md +103 -0
- package/lib/video-recap/skills/video-assemble/references/packaging.md +45 -0
- package/lib/video-recap/skills/video-assemble/references/pair-media.md +74 -0
- package/lib/video-recap/skills/video-assemble/references/source-score.md +147 -0
- package/lib/video-recap/skills/video-assemble/references/subtitle-track.md +202 -0
- package/lib/video-recap/skills/video-assemble/scripts/adoption/__init__.py +1 -0
- package/lib/video-recap/skills/video-assemble/scripts/adoption/audio_mix_binding.py +279 -0
- package/lib/video-recap/skills/video-assemble/scripts/adoption/frozen_audio.py +187 -0
- package/lib/video-recap/skills/video-assemble/scripts/adoption/narration_binding.py +323 -0
- package/lib/video-recap/skills/video-assemble/scripts/adoption/strict_inputs.py +109 -0
- package/lib/video-recap/skills/video-assemble/scripts/adoption/strict_publish.py +130 -0
- package/lib/video-recap/skills/video-assemble/scripts/artifacts.py +10 -46
- package/lib/video-recap/skills/video-assemble/scripts/assemble.py +378 -119
- package/lib/video-recap/skills/video-assemble/scripts/assemble_constants.py +29 -12
- package/lib/video-recap/skills/video-assemble/scripts/assembly_contract.py +95 -44
- package/lib/video-recap/skills/video-assemble/scripts/assembly_settings.py +75 -32
- package/lib/video-recap/skills/video-assemble/scripts/audio_mix.py +11 -11
- package/lib/video-recap/skills/video-assemble/scripts/compose_foreground.py +293 -0
- package/lib/video-recap/skills/video-assemble/scripts/export_jianying.py +71 -78
- package/lib/video-recap/skills/video-assemble/scripts/jianying/__init__.py +1 -0
- package/lib/video-recap/skills/video-assemble/scripts/{jianying_builders.py → jianying/builders.py} +39 -67
- package/lib/video-recap/skills/video-assemble/scripts/{jianying_model.py → jianying/model.py} +3 -11
- package/lib/video-recap/skills/video-assemble/scripts/{jianying_optional.py → jianying/optional.py} +1 -1
- package/lib/video-recap/skills/video-assemble/scripts/jianying/schema.py +53 -0
- package/lib/video-recap/skills/video-assemble/scripts/{jianying_templates.py → jianying/templates.py} +1 -1
- package/lib/video-recap/skills/video-assemble/scripts/{jianying_timeline_contract.py → jianying/timeline_contract.py} +34 -13
- package/lib/video-recap/skills/video-assemble/scripts/{jianying_writer.py → jianying/writer.py} +7 -17
- package/lib/video-recap/skills/video-assemble/scripts/lib.py +37 -4
- package/lib/video-recap/skills/video-assemble/scripts/media.py +12 -21
- package/lib/video-recap/skills/video-assemble/scripts/narration_audio.py +64 -34
- package/lib/video-recap/skills/video-assemble/scripts/packaging.py +122 -0
- package/lib/video-recap/skills/video-assemble/scripts/pair_media.py +216 -0
- package/lib/video-recap/skills/video-assemble/scripts/render_preflight.py +1 -3
- package/lib/video-recap/skills/video-assemble/scripts/source_score.py +469 -0
- package/lib/video-recap/skills/video-assemble/scripts/source_subtitles.py +25 -9
- package/lib/video-recap/skills/video-assemble/scripts/subtitles/__init__.py +1 -0
- package/lib/video-recap/skills/video-assemble/scripts/{subtitle_core.py → subtitles/core.py} +13 -6
- package/lib/video-recap/skills/video-assemble/scripts/{subtitle_render.py → subtitles/render.py} +5 -4
- package/lib/video-recap/skills/video-assemble/scripts/subtitles/track.py +324 -0
- package/lib/video-recap/skills/video-assemble/scripts/subtitles/track_binding.py +200 -0
- package/lib/video-recap/skills/video-assemble/scripts/timeline.py +30 -46
- package/lib/video-recap/skills/video-assemble/scripts/timeline_emit.py +91 -7
- package/lib/video-recap/skills/video-assemble/scripts/visual_render.py +22 -8
- package/lib/video-recap/skills/video-cut/SKILL.md +34 -16
- package/lib/video-recap/skills/video-cut/references/shot-review.md +50 -0
- package/lib/video-recap/skills/video-cut/scripts/cut.py +2 -28
- package/lib/video-recap/skills/video-cut/scripts/cut_cli.py +58 -62
- package/lib/video-recap/skills/video-cut/scripts/cut_contract.py +21 -106
- package/lib/video-recap/skills/video-cut/scripts/cut_render.py +14 -9
- package/lib/video-recap/skills/video-cut/scripts/lib.py +39 -0
- package/lib/video-recap/skills/video-cut/scripts/narration_mapping.py +2 -155
- package/lib/video-recap/skills/video-cut/scripts/narrative_selection.py +244 -0
- package/lib/video-recap/skills/video-cut/scripts/shot_review.py +357 -0
- package/lib/video-recap/skills/video-recap/SKILL.md +86 -42
- package/lib/video-recap/skills/video-recap/references/audio-routing.md +126 -0
- package/lib/video-recap/skills/video-recap/references/config-playbook.md +12 -8
- package/lib/video-recap/skills/video-recap/references/data-schema.md +44 -33
- package/lib/video-recap/skills/video-recap/references/resource-library.md +151 -0
- package/lib/video-recap/skills/video-recap/references/shift-left-qc-schema.md +1 -1
- package/lib/video-recap/skills/video-recap/scripts/doctor.py +123 -30
- package/lib/video-recap/skills/video-recap/scripts/final_qc.py +22 -75
- package/lib/video-recap/skills/video-recap/scripts/lib.py +3 -3
- package/lib/video-recap/skills/video-recap/scripts/library.py +509 -0
- package/lib/video-recap/skills/video-recap/scripts/materials.py +63 -91
- package/lib/video-recap/skills/video-recap/scripts/mimo_qc.py +8 -70
- package/lib/video-recap/skills/video-recap/scripts/project_binding.py +286 -0
- package/lib/video-recap/skills/video-recap/scripts/qc/__init__.py +1 -0
- package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_client.py → qc/mimo_client.py} +1 -1
- package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_evidence.py → qc/mimo_evidence.py} +33 -66
- package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_observations.py → qc/mimo_observations.py} +3 -11
- package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_payload.py → qc/mimo_payload.py} +4 -7
- package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_report.py → qc/mimo_report.py} +17 -28
- package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_runner.py → qc/mimo_runner.py} +2 -2
- package/lib/video-recap/skills/video-recap/scripts/qc_contract.py +0 -19
- package/lib/video-recap/skills/video-recap/scripts/recap_cli.py +119 -50
- package/lib/video-recap/skills/video-recap/scripts/recap_inspect.py +32 -114
- package/lib/video-recap/skills/video-recap/scripts/recap_review.py +9 -23
- package/lib/video-recap/skills/video-recap/scripts/recap_runner.py +387 -334
- package/lib/video-recap/skills/video-recap/scripts/recap_runtime.py +23 -23
- package/lib/video-recap/skills/video-recap/scripts/recap_source.py +278 -0
- package/lib/video-recap/skills/video-recap/scripts/recap_stage_qc.py +25 -12
- package/lib/video-recap/skills/video-recap/scripts/recap_timeline.py +59 -52
- package/lib/video-recap/skills/video-recap/scripts/resource_lock.py +162 -0
- package/lib/video-recap/skills/video-script/SKILL.md +16 -13
- package/lib/video-recap/skills/video-script/references/creative-editing-playbook.md +9 -5
- package/lib/video-recap/skills/video-script/references/promotional-copy.md +68 -0
- package/lib/video-recap/skills/video-script/references/research-guide.md +1 -1
- package/lib/video-recap/skills/video-script/scripts/evidence_bundle.py +24 -76
- package/lib/video-recap/skills/video-script/scripts/lib.py +10 -87
- package/lib/video-recap/skills/video-script/scripts/narration_lint.py +63 -39
- package/lib/video-recap/skills/video-script/scripts/review.py +0 -2
- package/lib/video-recap/skills/video-script/scripts/review_grounding.py +35 -59
- package/lib/video-recap/skills/video-script/scripts/review_response.py +66 -128
- package/lib/video-recap/skills/video-script/scripts/review_runner.py +12 -20
- package/lib/video-recap/skills/video-script/scripts/speech_ownership.py +4 -4
- package/lib/video-recap/skills/video-script/scripts/validate.py +53 -42
- package/lib/video-recap/skills/video-understanding/SKILL.md +6 -4
- package/lib/video-recap/skills/video-understanding/references/data-schema.md +50 -156
- package/lib/video-recap/skills/video-understanding/references/research-guide.md +4 -4
- package/lib/video-recap/skills/video-understanding/scripts/asr.py +85 -65
- package/lib/video-recap/skills/video-understanding/scripts/asr_timing_evidence.py +270 -0
- package/lib/video-recap/skills/video-understanding/scripts/brief.py +2 -5
- package/lib/video-recap/skills/video-understanding/scripts/briefing/__init__.py +1 -0
- package/lib/video-recap/skills/{video-script/scripts/agent_brief.py → video-understanding/scripts/briefing/builder.py} +38 -35
- package/lib/video-recap/skills/video-understanding/scripts/{brief_context.py → briefing/context.py} +4 -35
- package/lib/video-recap/skills/{video-script/scripts/brief_inputs.py → video-understanding/scripts/briefing/inputs.py} +9 -31
- package/lib/video-recap/skills/{video-script/scripts/brief_timeline.py → video-understanding/scripts/briefing/timeline.py} +5 -6
- package/lib/video-recap/skills/video-understanding/scripts/consolidate.py +65 -116
- package/lib/video-recap/skills/video-understanding/scripts/detect.py +31 -40
- package/lib/video-recap/skills/video-understanding/scripts/lib.py +30 -86
- package/lib/video-recap/skills/video-understanding/scripts/narration_lint.py +63 -39
- package/lib/video-recap/skills/video-understanding/scripts/speech_ownership.py +4 -4
- package/lib/video-recap/skills/video-understanding/scripts/storyboard.py +17 -79
- package/lib/video-recap/skills/video-understanding/scripts/understanding_brief.py +14 -40
- package/lib/video-recap/skills/video-understanding/scripts/understanding_cache.py +64 -46
- package/lib/video-recap/skills/video-understanding/scripts/understanding_runner.py +31 -11
- package/lib/video-recap/skills/video-understanding/scripts/understanding_storyboard.py +19 -14
- package/lib/video-recap/skills/video-understanding/scripts/vlm.py +80 -108
- package/lib/video-recap/skills/video-voiceover/SKILL.md +29 -19
- package/lib/video-recap/skills/video-voiceover/references/index-tts.md +31 -0
- package/lib/video-recap/skills/video-voiceover/scripts/approved_text_policy.py +123 -0
- package/lib/video-recap/skills/video-voiceover/scripts/dub.py +51 -59
- package/lib/video-recap/skills/video-voiceover/scripts/lib.py +8 -44
- package/lib/video-recap/skills/video-voiceover/scripts/providers/__init__.py +1 -0
- package/lib/video-recap/skills/video-voiceover/scripts/providers/index_tts.py +184 -0
- package/lib/video-recap/skills/video-voiceover/scripts/voiceover.py +212 -134
- package/package.json +45 -45
- package/lib/novel-to-game/skills/game-world-design/references/dialogue-design-method.md +0 -58
- package/lib/novel-to-game/skills/game-world-design/references/game-writing-craft.md +0 -53
- package/lib/novel-to-game/skills/novel-to-game/references/intake-benchmark-reference.md +0 -38
- package/lib/oh-story/skills/story-long-analyze/references/material-decomposition.md +0 -530
- package/lib/oh-story/skills/story-long-analyze/references/output-templates.md +0 -559
- package/lib/oh-story/skills/story-long-scan/references/scan-output-format.md +0 -207
- package/lib/oh-story/skills/story-long-write/references/state-tracking.md +0 -108
- package/lib/oh-story/skills/story-short-analyze/references/material-decomposition.md +0 -678
- package/lib/oh-story/skills/story-short-analyze/references/output-templates.md +0 -561
- package/lib/video-recap/skills/video-assemble/scripts/jianying_schema.py +0 -98
- package/lib/video-recap/skills/video-recap/references/creative-editing-playbook.md +0 -225
- package/lib/video-recap/skills/video-recap/references/research-guide.md +0 -74
- package/lib/video-recap/skills/video-recap/references/timeline-and-jianying.md +0 -250
- package/lib/video-recap/skills/video-script/scripts/brief_context.py +0 -348
- package/lib/video-recap/skills/video-script/scripts/narration.py +0 -12
- package/lib/video-recap/skills/video-understanding/scripts/agent_brief.py +0 -399
- package/lib/video-recap/skills/video-understanding/scripts/brief_inputs.py +0 -290
- package/lib/video-recap/skills/video-understanding/scripts/brief_timeline.py +0 -327
- /package/lib/video-recap/skills/video-assemble/scripts/{jianying_tracks.py → jianying/tracks.py} +0 -0
- /package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_contract.py → qc/mimo_contract.py} +0 -0
- /package/lib/video-recap/skills/video-voiceover/scripts/{fish_audio.py → providers/fish_audio.py} +0 -0
|
@@ -9,18 +9,17 @@ Optional, synchronous, in-pipeline. Two independent LLM passes over the video's
|
|
|
9
9
|
TEXT per segment index only; each segment's original start/end is re-attached here, so a
|
|
10
10
|
cleanup pass can never shift the timing-bearing spans downstream chunking depends on.
|
|
11
11
|
|
|
12
|
-
Both passes
|
|
13
|
-
|
|
12
|
+
Both passes are idempotent (a fresh artifact is reused); a failure raises and the runner
|
|
13
|
+
records it in consolidation.status.json. Mirrors review.py's pure-seam + thin-driver shape so it is
|
|
14
14
|
unit-testable with a mocked api_call. NON-required: the pipeline runs unchanged without it.
|
|
15
15
|
"""
|
|
16
16
|
|
|
17
17
|
import argparse
|
|
18
|
-
import hashlib
|
|
19
18
|
import json
|
|
20
19
|
import re
|
|
21
20
|
from pathlib import Path
|
|
22
21
|
|
|
23
|
-
from lib import CONFIG, log, api_call
|
|
22
|
+
from lib import CONFIG, log, api_call, file_identity, load_background_research
|
|
24
23
|
from understanding_cache import _fresh
|
|
25
24
|
|
|
26
25
|
# Shared tolerance for the per-segment span check. The brief-side gate inlines the SAME
|
|
@@ -59,9 +58,7 @@ INDEX_PROMPT = """你在根据逐场景画面分析、ASR对白、background_res
|
|
|
59
58
|
def build_clean_messages(asr_result):
|
|
60
59
|
lines = []
|
|
61
60
|
for i, seg in enumerate(asr_result or []):
|
|
62
|
-
|
|
63
|
-
continue
|
|
64
|
-
lines.append(f"{i}. {str(seg.get('text', '')).strip()}")
|
|
61
|
+
lines.append(f"{i}. {seg['text'].strip()}")
|
|
65
62
|
user = f"{CLEAN_PROMPT}\n\n## 逐段转写(共 {len(lines)} 段)\n" + "\n".join(lines)
|
|
66
63
|
return [{"role": "user", "content": user}]
|
|
67
64
|
|
|
@@ -69,7 +66,7 @@ def build_clean_messages(asr_result):
|
|
|
69
66
|
def parse_clean_response(text, asr_result):
|
|
70
67
|
"""Zip cleaned TEXT onto the original segments (start/end preserved by construction).
|
|
71
68
|
Returns the original asr_result UNCHANGED on any parse / shape / count problem."""
|
|
72
|
-
base =
|
|
69
|
+
base = list(asr_result or [])
|
|
73
70
|
if not base:
|
|
74
71
|
return asr_result
|
|
75
72
|
data = _extract_json(text)
|
|
@@ -87,8 +84,8 @@ def parse_clean_response(text, asr_result):
|
|
|
87
84
|
out = []
|
|
88
85
|
for i, seg in enumerate(base):
|
|
89
86
|
cleaned = by_index.get(i, {})
|
|
90
|
-
new_text = str(cleaned.get("text", "")).strip() or
|
|
91
|
-
merged = {"start": seg
|
|
87
|
+
new_text = str(cleaned.get("text", "")).strip() or seg["text"]
|
|
88
|
+
merged = {"start": seg["start"], "end": seg["end"], "text": new_text}
|
|
92
89
|
speaker = str(cleaned.get("speaker", "")).strip()
|
|
93
90
|
if speaker:
|
|
94
91
|
merged["speaker"] = speaker
|
|
@@ -96,17 +93,10 @@ def parse_clean_response(text, asr_result):
|
|
|
96
93
|
return out
|
|
97
94
|
|
|
98
95
|
|
|
99
|
-
def
|
|
96
|
+
def _input_identity(work_dir, name):
|
|
97
|
+
"""{size, mtime_ns} of one input artifact, or None when it is absent."""
|
|
100
98
|
path = Path(work_dir) / name
|
|
101
|
-
return
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
def _asr_clean_source_md5(work_dir):
|
|
105
|
-
return _json_md5_file(work_dir, "asr_clean.json")
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
def _research_source_md5(work_dir):
|
|
109
|
-
return _json_md5_file(work_dir, "background_research.json")
|
|
99
|
+
return file_identity(path) if path.exists() else None
|
|
110
100
|
|
|
111
101
|
|
|
112
102
|
def _research_glossary_from_context(background_research):
|
|
@@ -148,23 +138,22 @@ def _research_glossary_from_context(background_research):
|
|
|
148
138
|
return glossary
|
|
149
139
|
|
|
150
140
|
|
|
141
|
+
def _dialogue_source(asr_result, asr_clean):
|
|
142
|
+
"""asr_clean.json segments when present (consolidate's own output), else raw asr_result rows."""
|
|
143
|
+
return asr_clean["segments"] if asr_clean else (asr_result or [])
|
|
144
|
+
|
|
145
|
+
|
|
151
146
|
def _dialogue_segments_for_index(asr_result=None, asr_clean=None):
|
|
152
|
-
clean_segments = (
|
|
153
|
-
(asr_clean or {}).get("segments") if isinstance(asr_clean, dict) else None
|
|
154
|
-
)
|
|
155
|
-
source = clean_segments if isinstance(clean_segments, list) else (asr_result or [])
|
|
156
147
|
out = []
|
|
157
|
-
for i, seg in enumerate(
|
|
158
|
-
|
|
159
|
-
continue
|
|
160
|
-
text = str(seg.get("text", "")).strip()
|
|
148
|
+
for i, seg in enumerate(_dialogue_source(asr_result, asr_clean)):
|
|
149
|
+
text = seg["text"].strip()
|
|
161
150
|
if not text:
|
|
162
151
|
continue
|
|
163
152
|
out.append(
|
|
164
153
|
{
|
|
165
154
|
"id": f"asr:{i}",
|
|
166
|
-
"start": seg
|
|
167
|
-
"end": seg
|
|
155
|
+
"start": seg["start"],
|
|
156
|
+
"end": seg["end"],
|
|
168
157
|
"text": text,
|
|
169
158
|
}
|
|
170
159
|
)
|
|
@@ -280,44 +269,34 @@ def build_index_messages(
|
|
|
280
269
|
):
|
|
281
270
|
lines = []
|
|
282
271
|
for i, scene in enumerate(vlm_analysis or []):
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
desc = str(scene.get("description", "")).strip().replace("\n", " ")
|
|
289
|
-
facts = scene.get("frame_facts")
|
|
272
|
+
sid = scene["scene_id"]
|
|
273
|
+
start = float(scene["start"])
|
|
274
|
+
end = float(scene["end"])
|
|
275
|
+
desc = scene["description"].strip().replace("\n", " ")
|
|
276
|
+
facts = scene.get("frame_facts") # vlm.py only writes the key when non-empty
|
|
290
277
|
fact_txt = ""
|
|
291
|
-
if
|
|
278
|
+
if facts:
|
|
292
279
|
actions = []
|
|
293
280
|
|
|
294
281
|
def fact_key(x):
|
|
282
|
+
# timestamps are regex-captured from the VLM reply ("[\d.]+"), so "1.2.3" is possible
|
|
295
283
|
try:
|
|
296
284
|
return (0, float(x))
|
|
297
|
-
except
|
|
285
|
+
except ValueError:
|
|
298
286
|
return (1, str(x))
|
|
299
287
|
|
|
300
288
|
for ts in sorted(facts.keys(), key=fact_key):
|
|
301
|
-
|
|
302
|
-
actions.extend(vals if isinstance(vals, list) else [str(vals)])
|
|
289
|
+
actions.extend(facts[ts])
|
|
303
290
|
if actions:
|
|
304
291
|
fact_txt = " | 帧实: " + ";".join(a for a in actions[:6] if a)
|
|
305
292
|
lines.append(f"[visual:{i} 场景{sid} {start:.0f}-{end:.0f}s] {desc}{fact_txt}")
|
|
306
293
|
asr_lines = []
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
)
|
|
310
|
-
source_asr = (
|
|
311
|
-
clean_segments if isinstance(clean_segments, list) else (asr_result or [])
|
|
312
|
-
)
|
|
313
|
-
for i, seg in enumerate(source_asr or []):
|
|
314
|
-
if not isinstance(seg, dict):
|
|
315
|
-
continue
|
|
316
|
-
text = str(seg.get("text", "")).strip()
|
|
294
|
+
for i, seg in enumerate(_dialogue_source(asr_result, asr_clean)):
|
|
295
|
+
text = seg["text"].strip()
|
|
317
296
|
if not text:
|
|
318
297
|
continue
|
|
319
298
|
asr_lines.append(
|
|
320
|
-
f"[asr:{i} {float(seg
|
|
299
|
+
f"[asr:{i} {float(seg['start']):.0f}-{float(seg['end']):.0f}s] {text}"
|
|
321
300
|
)
|
|
322
301
|
glossary = _research_glossary_from_context(background_research)
|
|
323
302
|
glossary_lines = []
|
|
@@ -432,44 +411,31 @@ def _extract_json(text):
|
|
|
432
411
|
return None
|
|
433
412
|
|
|
434
413
|
|
|
435
|
-
def _asr_source_md5(work_dir):
|
|
436
|
-
"""Provenance: md5 of the on-disk asr_result.json BYTES (writer + reader hash the same thing)."""
|
|
437
|
-
path = Path(work_dir) / "asr_result.json"
|
|
438
|
-
return hashlib.md5(path.read_bytes()).hexdigest() if path.exists() else ""
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
def _vlm_source_md5(work_dir):
|
|
442
|
-
"""Provenance: md5 of the on-disk vlm_analysis.json bytes."""
|
|
443
|
-
path = Path(work_dir) / "vlm_analysis.json"
|
|
444
|
-
return hashlib.md5(path.read_bytes()).hexdigest() if path.exists() else ""
|
|
445
|
-
|
|
446
|
-
|
|
447
414
|
def _index_meta_path(work_dir):
|
|
448
415
|
return Path(work_dir) / "understanding_index.json.meta.json"
|
|
449
416
|
|
|
450
417
|
|
|
451
|
-
def
|
|
452
|
-
|
|
418
|
+
def _index_meta(work_dir, vlm_analysis):
|
|
419
|
+
"""Provenance of understanding_index.json: `source` (vlm_analysis.json identity) + `model`
|
|
420
|
+
are what brief consumers check; the other inputs and the prompt text are the producer's
|
|
421
|
+
own rebuild gate."""
|
|
422
|
+
return {
|
|
423
|
+
"schema_version": INDEX_SCHEMA_VERSION,
|
|
424
|
+
"source": _input_identity(work_dir, "vlm_analysis.json"),
|
|
425
|
+
"inputs": {
|
|
426
|
+
"asr_result": _input_identity(work_dir, "asr_result.json"),
|
|
427
|
+
"asr_clean": _input_identity(work_dir, "asr_clean.json"),
|
|
428
|
+
"background_research": _input_identity(work_dir, "background_research.json"),
|
|
429
|
+
},
|
|
430
|
+
"scene_count": len([s for s in (vlm_analysis or []) if isinstance(s, dict)]),
|
|
431
|
+
"model": CONFIG["vlm_model"],
|
|
432
|
+
"prompt": INDEX_PROMPT,
|
|
433
|
+
}
|
|
453
434
|
|
|
454
435
|
|
|
455
436
|
def _write_index_meta(work_dir, vlm_analysis):
|
|
456
437
|
_index_meta_path(work_dir).write_text(
|
|
457
|
-
json.dumps(
|
|
458
|
-
{
|
|
459
|
-
"schema_version": INDEX_SCHEMA_VERSION,
|
|
460
|
-
"source_md5": _vlm_source_md5(work_dir),
|
|
461
|
-
"asr_md5": _asr_source_md5(work_dir),
|
|
462
|
-
"asr_clean_md5": _asr_clean_source_md5(work_dir),
|
|
463
|
-
"research_md5": _research_source_md5(work_dir),
|
|
464
|
-
"scene_count": len(
|
|
465
|
-
[s for s in (vlm_analysis or []) if isinstance(s, dict)]
|
|
466
|
-
),
|
|
467
|
-
"model": CONFIG.get("vlm_model", ""),
|
|
468
|
-
"prompt_md5": _prompt_fingerprint(INDEX_PROMPT),
|
|
469
|
-
},
|
|
470
|
-
ensure_ascii=False,
|
|
471
|
-
indent=2,
|
|
472
|
-
),
|
|
438
|
+
json.dumps(_index_meta(work_dir, vlm_analysis), ensure_ascii=False, indent=2),
|
|
473
439
|
encoding="utf-8",
|
|
474
440
|
)
|
|
475
441
|
|
|
@@ -482,31 +448,18 @@ def _index_cache_matches(work_dir, vlm_analysis):
|
|
|
482
448
|
meta = json.loads(meta_path.read_text(encoding="utf-8"))
|
|
483
449
|
except (json.JSONDecodeError, OSError):
|
|
484
450
|
return False
|
|
485
|
-
return (
|
|
486
|
-
isinstance(meta, dict)
|
|
487
|
-
and meta.get("schema_version") == INDEX_SCHEMA_VERSION
|
|
488
|
-
and meta.get("source_md5") == _vlm_source_md5(work_dir)
|
|
489
|
-
and meta.get("asr_md5", "") == _asr_source_md5(work_dir)
|
|
490
|
-
and meta.get("asr_clean_md5", "") == _asr_clean_source_md5(work_dir)
|
|
491
|
-
and meta.get("research_md5", "") == _research_source_md5(work_dir)
|
|
492
|
-
and meta.get("scene_count")
|
|
493
|
-
== len([s for s in (vlm_analysis or []) if isinstance(s, dict)])
|
|
494
|
-
and meta.get("model") == CONFIG.get("vlm_model", "")
|
|
495
|
-
and meta.get("prompt_md5") == _prompt_fingerprint(INDEX_PROMPT)
|
|
496
|
-
)
|
|
451
|
+
return meta == _index_meta(work_dir, vlm_analysis)
|
|
497
452
|
|
|
498
453
|
|
|
499
454
|
# ── thin drivers (I/O + api_call) ─────────────────────────────────────────────
|
|
500
455
|
|
|
501
456
|
|
|
502
457
|
def _load(work_dir, name):
|
|
458
|
+
"""Read one of this skill's own JSON artifacts: absent → None, corrupt → raise."""
|
|
503
459
|
path = Path(work_dir) / name
|
|
504
460
|
if not path.exists():
|
|
505
461
|
return None
|
|
506
|
-
|
|
507
|
-
return json.loads(path.read_text(encoding="utf-8"))
|
|
508
|
-
except (ValueError, OSError):
|
|
509
|
-
return None
|
|
462
|
+
return json.loads(path.read_text(encoding="utf-8"))
|
|
510
463
|
|
|
511
464
|
|
|
512
465
|
def consolidate_transcript(work_dir):
|
|
@@ -519,16 +472,16 @@ def consolidate_transcript(work_dir):
|
|
|
519
472
|
if _fresh(out_path, work_dir / "asr_result.json"):
|
|
520
473
|
existing = _load(work_dir, "asr_clean.json") or {}
|
|
521
474
|
if (
|
|
522
|
-
existing.get("
|
|
523
|
-
and existing.get("model") == CONFIG
|
|
524
|
-
and existing.get("
|
|
475
|
+
existing.get("source") == _input_identity(work_dir, "asr_result.json")
|
|
476
|
+
and existing.get("model") == CONFIG["vlm_model"]
|
|
477
|
+
and existing.get("prompt") == CLEAN_PROMPT
|
|
525
478
|
and existing.get("postprocess_version") == ASR_CLEAN_POSTPROCESS_VERSION
|
|
526
479
|
):
|
|
527
480
|
log("consolidate(asr): asr_clean.json 已最新,跳过")
|
|
528
481
|
return existing
|
|
529
482
|
resp = api_call(
|
|
530
483
|
{
|
|
531
|
-
"model": CONFIG
|
|
484
|
+
"model": CONFIG["vlm_model"],
|
|
532
485
|
"messages": build_clean_messages(asr_result),
|
|
533
486
|
"max_tokens": 4000,
|
|
534
487
|
"temperature": 0.2,
|
|
@@ -537,9 +490,9 @@ def consolidate_transcript(work_dir):
|
|
|
537
490
|
content = _response_text(resp)
|
|
538
491
|
segments = parse_clean_response(content, asr_result)
|
|
539
492
|
payload = {
|
|
540
|
-
"
|
|
541
|
-
"model": CONFIG
|
|
542
|
-
"
|
|
493
|
+
"source": _input_identity(work_dir, "asr_result.json"),
|
|
494
|
+
"model": CONFIG["vlm_model"],
|
|
495
|
+
"prompt": CLEAN_PROMPT,
|
|
543
496
|
"postprocess_version": ASR_CLEAN_POSTPROCESS_VERSION,
|
|
544
497
|
"segments": segments,
|
|
545
498
|
}
|
|
@@ -564,10 +517,10 @@ def consolidate_index(work_dir):
|
|
|
564
517
|
return _load(work_dir, "understanding_index.json")
|
|
565
518
|
asr_result = _load(work_dir, "asr_result.json") or []
|
|
566
519
|
asr_clean = _load(work_dir, "asr_clean.json") or {}
|
|
567
|
-
background_research =
|
|
520
|
+
background_research = load_background_research(work_dir)
|
|
568
521
|
resp = api_call(
|
|
569
522
|
{
|
|
570
|
-
"model": CONFIG
|
|
523
|
+
"model": CONFIG["vlm_model"],
|
|
571
524
|
"messages": build_index_messages(
|
|
572
525
|
vlm_analysis,
|
|
573
526
|
asr_result=asr_result,
|
|
@@ -599,18 +552,14 @@ def consolidate_index(work_dir):
|
|
|
599
552
|
|
|
600
553
|
|
|
601
554
|
def consolidate(work_dir, do_asr=False, do_index=True):
|
|
602
|
-
"""Default = index-only (Pass B, zero timing risk). Pass A (asr) is opt-in.
|
|
555
|
+
"""Default = index-only (Pass B, zero timing risk). Pass A (asr) is opt-in.
|
|
556
|
+
|
|
557
|
+
Failures propagate; understanding_runner catches them and writes the "failed" status."""
|
|
603
558
|
result = {}
|
|
604
559
|
if do_asr:
|
|
605
|
-
|
|
606
|
-
result["asr_clean"] = consolidate_transcript(work_dir)
|
|
607
|
-
except Exception as e:
|
|
608
|
-
log(f"consolidate(asr) 跳过(忽略): {e}")
|
|
560
|
+
result["asr_clean"] = consolidate_transcript(work_dir)
|
|
609
561
|
if do_index:
|
|
610
|
-
|
|
611
|
-
result["index"] = consolidate_index(work_dir)
|
|
612
|
-
except Exception as e:
|
|
613
|
-
log(f"consolidate(index) 跳过(忽略): {e}")
|
|
562
|
+
result["index"] = consolidate_index(work_dir)
|
|
614
563
|
return result
|
|
615
564
|
|
|
616
565
|
|
|
@@ -6,7 +6,7 @@ from concurrent.futures import ThreadPoolExecutor
|
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
|
|
8
8
|
from lib import CONFIG
|
|
9
|
-
from lib import log, run_cmd, get_video_duration,
|
|
9
|
+
from lib import log, run_cmd, get_video_duration, file_identity
|
|
10
10
|
|
|
11
11
|
# ── Step 2: 场景检测 ──────────────────────────────────────────────────
|
|
12
12
|
|
|
@@ -48,10 +48,10 @@ def detect_scenes(video_path, work_dir, threshold=None):
|
|
|
48
48
|
|
|
49
49
|
# 先过滤黑/白帧过渡场景,再合并短场景
|
|
50
50
|
# (合并会保留短场景的 start,若黑场被并入长场景,单点采样会误删整段,故顺序在前)
|
|
51
|
-
if CONFIG
|
|
51
|
+
if CONFIG["scene_junk_filter"]:
|
|
52
52
|
scenes = _filter_junk_scenes(scenes, video_path)
|
|
53
53
|
# 合并短场景(< 3s 合并到相邻场景)
|
|
54
|
-
scenes = _merge_short_scenes(scenes, min_duration=CONFIG
|
|
54
|
+
scenes = _merge_short_scenes(scenes, min_duration=CONFIG["scene_merge_min"])
|
|
55
55
|
|
|
56
56
|
# 保存
|
|
57
57
|
scenes_file = work_dir / "scenes.json"
|
|
@@ -115,9 +115,9 @@ def _sample_frame_luma(video_path, timestamp, sample_size=64):
|
|
|
115
115
|
|
|
116
116
|
def _is_junk_scene(video_path, timestamp, threshold_dark=None, threshold_bright=None):
|
|
117
117
|
"""Return True for near-black or near-white scene-start frames."""
|
|
118
|
-
threshold_dark = CONFIG
|
|
119
|
-
threshold_bright = CONFIG
|
|
120
|
-
pixel_ratio = min(1.0,
|
|
118
|
+
threshold_dark = CONFIG["scene_junk_dark_luma"] if threshold_dark is None else threshold_dark
|
|
119
|
+
threshold_bright = CONFIG["scene_junk_bright_luma"] if threshold_bright is None else threshold_bright
|
|
120
|
+
pixel_ratio = min(1.0, float(CONFIG["scene_junk_pixel_ratio"]))
|
|
121
121
|
try:
|
|
122
122
|
lumas = _sample_frame_luma(video_path, timestamp)
|
|
123
123
|
except Exception as exc:
|
|
@@ -157,7 +157,7 @@ def _filter_junk_scenes(scenes, video_path):
|
|
|
157
157
|
# 每个探点都是一次 ffmpeg 进程(seek + 解一帧),一部 30 分钟片有几百个场景,串行下来
|
|
158
158
|
# 光进程启动就要几十秒。按场景并行:每个 worker 内部仍然短路,所以既保留了「多数场景只
|
|
159
159
|
# 探一次」,又把进程启动开销摊平。判定逻辑与探点完全不变。
|
|
160
|
-
workers =
|
|
160
|
+
workers = int(CONFIG["scene_junk_workers"])
|
|
161
161
|
with ThreadPoolExecutor(max_workers=min(workers, len(scenes))) as executor:
|
|
162
162
|
verdicts = list(executor.map(lambda s: _scene_is_junk(s, video_path), scenes))
|
|
163
163
|
filtered = []
|
|
@@ -203,7 +203,7 @@ def annotate_quiet_windows_with_asr(periods, asr_result=None, *, video_duration=
|
|
|
203
203
|
Coarse grid ASR (large synthetic windows from chunking) must not turn every quiet
|
|
204
204
|
window into speech. The return value is (annotated_periods, qc). Inputs are copied.
|
|
205
205
|
"""
|
|
206
|
-
out = [dict(p) for p in (periods or [])
|
|
206
|
+
out = [dict(p) for p in (periods or [])]
|
|
207
207
|
qc = {"coarse_asr_windows": 0, "low_confidence_speech_flags": 0, "asr_granularity": "none"}
|
|
208
208
|
for qp in out:
|
|
209
209
|
qp.setdefault("speech_overlap_ratio", 0.0)
|
|
@@ -216,20 +216,15 @@ def annotate_quiet_windows_with_asr(periods, asr_result=None, *, video_duration=
|
|
|
216
216
|
|
|
217
217
|
valid_segments = []
|
|
218
218
|
for seg in asr_result:
|
|
219
|
-
|
|
220
|
-
continue
|
|
221
|
-
try:
|
|
222
|
-
ss, se = float(seg.get("start", 0)), float(seg.get("end", 0))
|
|
223
|
-
except (TypeError, ValueError):
|
|
224
|
-
continue
|
|
219
|
+
ss, se = float(seg["start"]), float(seg["end"])
|
|
225
220
|
if se > ss:
|
|
226
221
|
valid_segments.append((ss, se))
|
|
227
222
|
asr_coverage = sum(se - ss for ss, se in valid_segments)
|
|
228
223
|
avg_seg_dur = asr_coverage / len(valid_segments) if valid_segments else 0
|
|
229
224
|
if video_duration is None:
|
|
230
|
-
ends = [float(p
|
|
225
|
+
ends = [float(p["end"]) for p in out] + [se for _, se in valid_segments]
|
|
231
226
|
video_duration = max(ends or [0.0])
|
|
232
|
-
configured = float(configured_segment_seconds if configured_segment_seconds is not None else
|
|
227
|
+
configured = float(configured_segment_seconds if configured_segment_seconds is not None else CONFIG["asr_segment_seconds"])
|
|
233
228
|
coarse_asr = (
|
|
234
229
|
(len(valid_segments) <= 5 and asr_coverage > float(video_duration) * 0.8) or
|
|
235
230
|
asr_coverage > float(video_duration) * 1.5 or
|
|
@@ -243,7 +238,7 @@ def annotate_quiet_windows_with_asr(periods, asr_result=None, *, video_duration=
|
|
|
243
238
|
overlap_seconds = 0.0
|
|
244
239
|
for ss, se in valid_segments:
|
|
245
240
|
overlap_seconds += max(0.0, min(float(qp["end"]), se) - max(float(qp["start"]), ss))
|
|
246
|
-
ratio = overlap_seconds / max(0.001, float(qp
|
|
241
|
+
ratio = overlap_seconds / max(0.001, float(qp["duration"]))
|
|
247
242
|
qp["asr_overlap_seconds"] = round(overlap_seconds, 3)
|
|
248
243
|
qp["speech_overlap_ratio"] = round(ratio, 4)
|
|
249
244
|
qp["asr_granularity"] = granularity
|
|
@@ -333,7 +328,7 @@ def detect_silence_periods(video_path, work_dir, asr_result=None):
|
|
|
333
328
|
"duration": round(total_dur - dur_start, 2)})
|
|
334
329
|
|
|
335
330
|
# 合并相邻静音段(间隔 < merge_gap 的合并为一个大窗口)
|
|
336
|
-
merge_gap = CONFIG
|
|
331
|
+
merge_gap = CONFIG["silence_merge_gap"]
|
|
337
332
|
merged = []
|
|
338
333
|
for rp in sorted(raw_periods, key=lambda x: x["start"]):
|
|
339
334
|
if merged and rp["start"] - merged[-1]["end"] < merge_gap:
|
|
@@ -352,7 +347,7 @@ def detect_silence_periods(video_path, work_dir, asr_result=None):
|
|
|
352
347
|
periods,
|
|
353
348
|
asr_result,
|
|
354
349
|
video_duration=get_video_duration(str(audio_path)) if asr_result else None,
|
|
355
|
-
configured_segment_seconds=CONFIG
|
|
350
|
+
configured_segment_seconds=CONFIG["asr_segment_seconds"],
|
|
356
351
|
)
|
|
357
352
|
|
|
358
353
|
(work_dir / "silence_periods.qc.json").write_text(
|
|
@@ -368,12 +363,6 @@ def detect_silence_periods(video_path, work_dir, asr_result=None):
|
|
|
368
363
|
return periods
|
|
369
364
|
|
|
370
365
|
|
|
371
|
-
def _asr_segments(asr_result):
|
|
372
|
-
if isinstance(asr_result, dict):
|
|
373
|
-
asr_result = asr_result.get("segments", [])
|
|
374
|
-
return [item for item in (asr_result or []) if isinstance(item, dict)]
|
|
375
|
-
|
|
376
|
-
|
|
377
366
|
def detect_speech_boundary_anchors(work_dir, asr_result):
|
|
378
367
|
"""Write sentence-end entry anchors by aligning ASR punctuation to short pauses.
|
|
379
368
|
|
|
@@ -385,14 +374,14 @@ def detect_speech_boundary_anchors(work_dir, asr_result):
|
|
|
385
374
|
work_dir = Path(work_dir)
|
|
386
375
|
audio_path = work_dir / "audio.wav"
|
|
387
376
|
out_path = work_dir / "speech_boundary_anchors.json"
|
|
388
|
-
segments =
|
|
377
|
+
segments = list(asr_result or [])
|
|
389
378
|
report = {
|
|
390
379
|
"schema_version": 1,
|
|
391
380
|
"artifact": "speech_boundary_anchors.json",
|
|
392
381
|
"status": "completed",
|
|
393
382
|
"detector": {
|
|
394
|
-
"noise_threshold": CONFIG
|
|
395
|
-
"min_pause_seconds": float(CONFIG
|
|
383
|
+
"noise_threshold": CONFIG["source_boundary_noise_threshold"],
|
|
384
|
+
"min_pause_seconds": float(CONFIG["source_boundary_min_pause"]),
|
|
396
385
|
"alignment": "terminal_punctuation_to_nearest_acoustic_pause",
|
|
397
386
|
},
|
|
398
387
|
"sentence_anchors": [],
|
|
@@ -408,8 +397,8 @@ def detect_speech_boundary_anchors(work_dir, asr_result):
|
|
|
408
397
|
"ffmpeg", "-hide_banner", "-nostats", "-i", str(audio_path),
|
|
409
398
|
"-af", (
|
|
410
399
|
"silencedetect="
|
|
411
|
-
f"noise={CONFIG
|
|
412
|
-
f"d={float(CONFIG
|
|
400
|
+
f"noise={CONFIG['source_boundary_noise_threshold']}:"
|
|
401
|
+
f"d={float(CONFIG['source_boundary_min_pause'])}"
|
|
413
402
|
),
|
|
414
403
|
"-f", "null", "-",
|
|
415
404
|
], timeout=120)
|
|
@@ -434,16 +423,13 @@ def detect_speech_boundary_anchors(work_dir, asr_result):
|
|
|
434
423
|
})
|
|
435
424
|
report["acoustic_pauses"] = pauses
|
|
436
425
|
|
|
437
|
-
max_error = float(CONFIG
|
|
426
|
+
max_error = float(CONFIG["source_boundary_max_alignment_error"])
|
|
438
427
|
used = set()
|
|
439
428
|
anchors = []
|
|
440
429
|
for asr_index, segment in enumerate(segments):
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
except (TypeError, ValueError):
|
|
445
|
-
continue
|
|
446
|
-
text = str(segment.get("text") or "").strip()
|
|
430
|
+
seg_start = float(segment["start"])
|
|
431
|
+
seg_end = float(segment["end"])
|
|
432
|
+
text = segment["text"].strip()
|
|
447
433
|
if seg_end <= seg_start or not text:
|
|
448
434
|
continue
|
|
449
435
|
last_midpoint = seg_start - 1e-6
|
|
@@ -496,17 +482,22 @@ def _audio_cache_matches(audio_path, video_path):
|
|
|
496
482
|
except (OSError, ValueError, TypeError):
|
|
497
483
|
return False
|
|
498
484
|
try:
|
|
499
|
-
expected =
|
|
485
|
+
expected = file_identity(video_path)
|
|
500
486
|
except OSError:
|
|
501
487
|
return False
|
|
502
|
-
return
|
|
488
|
+
return (
|
|
489
|
+
isinstance(meta, dict)
|
|
490
|
+
and meta.get("source_video") == str(Path(video_path).resolve())
|
|
491
|
+
and meta.get("source_video_identity") == expected
|
|
492
|
+
)
|
|
503
493
|
|
|
504
494
|
|
|
505
495
|
def _write_audio_meta(work_dir, video_path):
|
|
506
496
|
_audio_meta_path(work_dir).write_text(
|
|
507
497
|
json.dumps({
|
|
508
498
|
"schema_version": 1,
|
|
509
|
-
"
|
|
499
|
+
"source_video": str(Path(video_path).resolve()),
|
|
500
|
+
"source_video_identity": file_identity(video_path),
|
|
510
501
|
"audio": "audio.wav",
|
|
511
502
|
}, ensure_ascii=False, indent=2),
|
|
512
503
|
encoding="utf-8",
|