@oh-story/dsh 0.1.10 → 0.1.12
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 +11 -11
- package/lib/client.js +7 -3
- package/lib/client.js.map +2 -2
- package/lib/drama/manifest.json +212 -142
- package/lib/drama/skills/short-drama/SKILL.md +14 -2
- 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/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/era-anchors.md +37 -0
- package/lib/drama/skills/short-drama-assets/references/stage-contract.md +2 -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 +43 -20
- 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 +4 -2
- package/lib/drama/skills/short-drama-edit/references/cut-craft.md +3 -1
- package/lib/drama/skills/short-drama-edit/references/delivery-verify.md +6 -2
- package/lib/drama/skills/short-drama-edit/references/generated-footage.md +12 -6
- package/lib/drama/skills/short-drama-edit/references/sound-and-subtitles.md +13 -8
- package/lib/drama/skills/short-drama-edit/references/stage-contract.md +2 -0
- package/lib/drama/skills/short-drama-edit/scripts/edit_tool.py +1153 -121
- package/lib/drama/skills/short-drama-edit/scripts/selftest.py +49 -9
- 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-review/SKILL.md +2 -2
- 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 +15 -0
- package/lib/drama/skills/short-drama-review/references/stage-contract.md +3 -0
- package/lib/drama/skills/short-drama-storyboard/SKILL.md +13 -5
- package/lib/drama/skills/short-drama-storyboard/references/blocking-playbooks.md +1 -1
- 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 +82 -14
- package/lib/drama/skills/short-drama-storyboard/references/stage-contract.md +4 -2
- package/lib/drama/skills/short-drama-video-prompts/SKILL.md +5 -3
- package/lib/drama/skills/short-drama-video-prompts/references/camera-audio-continuity.md +12 -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 +40 -1
- package/lib/drama/skills/short-drama-video-prompts/references/motion-recipe.md +20 -14
- package/lib/drama/skills/short-drama-video-prompts/references/performance-action-timing.md +1 -1
- package/lib/drama/skills/short-drama-video-prompts/references/production-prompt-grammar.md +8 -7
- package/lib/drama/skills/short-drama-video-prompts/references/review-and-fixtures.md +1 -1
- package/lib/drama/skills/short-drama-video-prompts/references/stage-contract.md +1 -0
- package/lib/drama/skills/short-drama-video-prompts/references/target-model-profile.md +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 +8 -3
- 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 +71 -21
- package/lib/index.js.map +3 -3
- package/lib/oh-story/manifest.json +545 -310
- package/lib/oh-story/roles/chapter-extractor.md +11 -11
- package/lib/oh-story/roles/character-designer.md +1 -2
- package/lib/oh-story/roles/consistency-checker.md +11 -9
- package/lib/oh-story/roles/narrative-writer.md +24 -25
- 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 +6 -6
- 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 +40 -118
- package/lib/oh-story/skills/story/scripts/author_memory_commit.py +3 -3
- package/lib/oh-story/skills/story-deslop/SKILL.md +24 -25
- 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 +1 -1
- 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 +40 -118
- package/lib/oh-story/skills/story-deslop/references/banned-words.md +2 -2
- package/lib/oh-story/skills/story-deslop/references/deslop-gates.md +21 -4
- 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 +3 -3
- package/lib/oh-story/skills/story-deslop/scripts/check-ai-patterns.js +155 -8
- package/lib/oh-story/skills/story-import/SKILL.md +63 -579
- 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/structure-mapping-long.md +95 -91
- 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 +11 -9
- 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 +101 -14
- package/lib/oh-story/skills/story-import/scripts/tracking_commit.py +278 -23
- package/lib/oh-story/skills/story-import/scripts/wordcount_core.py +179 -23
- package/lib/oh-story/skills/story-long-analyze/SKILL.md +28 -50
- package/lib/oh-story/skills/story-long-analyze/references/author-facing.md +12 -5
- package/lib/oh-story/skills/story-long-analyze/references/deconstruction-notes.md +1 -1
- 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/pipeline-ops.md +21 -55
- 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 +17 -0
- package/lib/oh-story/skills/story-long-analyze/references/style-profile-protocol.md +1 -1
- package/lib/oh-story/skills/story-long-analyze/references/synthesis-inputs.md +39 -0
- package/lib/oh-story/skills/story-long-analyze/scripts/manage_analysis_run.py +47 -8
- package/lib/oh-story/skills/story-long-scan/SKILL.md +41 -193
- 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 +32 -73
- 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 +1 -1
- package/lib/oh-story/skills/story-long-write/references/artifact-protocols.md +18 -201
- 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 +40 -118
- 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 +6 -10
- package/lib/oh-story/skills/story-long-write/references/character-relations.md +49 -1
- package/lib/oh-story/skills/story-long-write/references/deslop-gates.md +171 -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/genre-prose-cards.md +1 -1
- package/lib/oh-story/skills/story-long-write/references/long-chapter-quality.md +3 -2
- 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 +10 -6
- package/lib/oh-story/skills/story-long-write/references/reader-contract-and-progression.md +1 -1
- package/lib/oh-story/skills/story-long-write/references/reference-index.md +4 -4
- 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 +11 -9
- package/lib/oh-story/skills/story-long-write/references/workflow-chapter.md +33 -44
- package/lib/oh-story/skills/story-long-write/references/workflow-daily.md +18 -51
- 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 +53 -67
- package/lib/oh-story/skills/story-long-write/references/workflow-setup.md +112 -251
- 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 +5 -3
- package/lib/oh-story/skills/story-long-write/references/writing-craft.md +18 -56
- package/lib/oh-story/skills/story-long-write/scripts/author_memory_commit.py +3 -3
- 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 +96 -16
- 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 +101 -14
- 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 +2 -1
- package/lib/oh-story/skills/story-long-write/scripts/storyctl.py +250 -50
- package/lib/oh-story/skills/story-long-write/scripts/tracking_commit.py +278 -23
- package/lib/oh-story/skills/story-long-write/scripts/wordcount_core.py +179 -23
- package/lib/oh-story/skills/story-review/SKILL.md +19 -273
- 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 +1 -1
- 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 +40 -118
- 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/review-quality.md +2 -1
- 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 +11 -9
- package/lib/oh-story/skills/story-review/scripts/author_memory_commit.py +3 -3
- 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 +278 -23
- package/lib/oh-story/skills/story-review/scripts/wordcount_core.py +179 -23
- package/lib/oh-story/skills/story-setup/SKILL.md +69 -458
- 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 +1 -1
- 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 +21 -4
- 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-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 +18 -56
- package/lib/oh-story/skills/story-setup/references/diagnostics.md +3 -3
- package/lib/oh-story/skills/story-short-analyze/SKILL.md +37 -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/{material-decomposition.md → stage4-6-reversal-summary.md} +303 -374
- package/lib/oh-story/skills/story-short-scan/SKILL.md +30 -64
- 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 -34
- 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 +40 -118
- 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/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 -49
- 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 +19 -12
- package/lib/oh-story/skills/story-short-write/references/workflow-draft.md +22 -32
- package/lib/oh-story/skills/story-short-write/references/workflow-revision.md +84 -17
- 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 +3 -3
- 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/video-studio.d.ts.map +1 -1
- package/lib/types/native-hooks.d.ts +2 -2
- package/lib/types/native-hooks.d.ts.map +1 -1
- 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-understanding/scripts/{agent_brief.py → briefing/builder.py} +38 -35
- package/lib/video-recap/skills/{video-script/scripts/brief_context.py → video-understanding/scripts/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-understanding/scripts/{brief_timeline.py → 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 +41 -41
- package/lib/oh-story/skills/story-long-analyze/references/material-decomposition.md +0 -538
- package/lib/oh-story/skills/story-long-analyze/references/output-templates.md +0 -603
- package/lib/oh-story/skills/story-long-scan/references/scan-output-format.md +0 -207
- 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/agent_brief.py +0 -399
- package/lib/video-recap/skills/video-script/scripts/brief_timeline.py +0 -327
- package/lib/video-recap/skills/video-script/scripts/narration.py +0 -12
- package/lib/video-recap/skills/video-understanding/scripts/brief_context.py +0 -348
- package/lib/video-recap/skills/video-understanding/scripts/brief_inputs.py +0 -290
- /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
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
4
|
import math
|
|
5
|
-
import os
|
|
6
5
|
import shlex
|
|
7
6
|
import shutil
|
|
8
7
|
import subprocess
|
|
@@ -11,7 +10,8 @@ from pathlib import Path
|
|
|
11
10
|
|
|
12
11
|
import materials as material_lib
|
|
13
12
|
from doctor import ffmpeg_has_subtitles_filter
|
|
14
|
-
from lib import env_bool, load_json
|
|
13
|
+
from lib import env_bool, env_int, load_json
|
|
14
|
+
from recap_source import audio_binding
|
|
15
15
|
|
|
16
16
|
BUNDLE = Path(__file__).resolve().parents[2] # the skills/ directory
|
|
17
17
|
|
|
@@ -29,13 +29,8 @@ def _run(skill, script, *cli_args):
|
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
def _optional_env_int(name):
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
return None
|
|
35
|
-
try:
|
|
36
|
-
value = int(raw)
|
|
37
|
-
except ValueError as exc:
|
|
38
|
-
raise ValueError(f"{name} must be an integer; got {raw!r}") from exc
|
|
32
|
+
"""lib.env_int with -1 as the documented "unset" sentinel."""
|
|
33
|
+
value = env_int(name, None)
|
|
39
34
|
return None if value == -1 else value
|
|
40
35
|
|
|
41
36
|
|
|
@@ -67,6 +62,11 @@ def _read_video_duration_or_raise(path):
|
|
|
67
62
|
|
|
68
63
|
def _probe_display_height_or_raise(path, *, require_square_pixels=False):
|
|
69
64
|
"""Return ffmpeg's display-coordinate height, accounting for rotation and SAR."""
|
|
65
|
+
return _probe_display_size_or_raise(path, require_square_pixels=require_square_pixels)[1]
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _probe_display_size_or_raise(path, *, require_square_pixels=False):
|
|
69
|
+
"""Return ffmpeg's display-coordinate (width, height), accounting for rotation and SAR."""
|
|
70
70
|
cmd = [
|
|
71
71
|
"ffprobe",
|
|
72
72
|
"-v",
|
|
@@ -107,7 +107,7 @@ def _probe_display_height_or_raise(path, *, require_square_pixels=False):
|
|
|
107
107
|
(int(round(float(value))) % 360 for value in rotation_values if value not in (None, "")),
|
|
108
108
|
0,
|
|
109
109
|
)
|
|
110
|
-
return display_width if rotation in {90, 270} else height
|
|
110
|
+
return (height, display_width) if rotation in {90, 270} else (display_width, height)
|
|
111
111
|
|
|
112
112
|
|
|
113
113
|
def _analysis_settings(args):
|
|
@@ -124,10 +124,6 @@ def _analysis_settings(args):
|
|
|
124
124
|
}
|
|
125
125
|
|
|
126
126
|
|
|
127
|
-
def _material_settings_fingerprint(args):
|
|
128
|
-
return material_lib.settings_fingerprint(_analysis_settings(args))
|
|
129
|
-
|
|
130
|
-
|
|
131
127
|
def _coerce_videos(video_or_videos):
|
|
132
128
|
if isinstance(video_or_videos, (list, tuple)):
|
|
133
129
|
return [Path(v).resolve() for v in video_or_videos]
|
|
@@ -138,8 +134,9 @@ def _run_manifest_payload(video, args):
|
|
|
138
134
|
return {
|
|
139
135
|
"schema_version": 1,
|
|
140
136
|
"source_video": str(Path(video).resolve()),
|
|
141
|
-
"
|
|
137
|
+
"source_video_identity": material_lib.file_identity(video),
|
|
142
138
|
"settings": _analysis_settings(args),
|
|
139
|
+
"audio": audio_binding(args),
|
|
143
140
|
}
|
|
144
141
|
|
|
145
142
|
|
|
@@ -148,19 +145,20 @@ def _write_run_manifest(work_dir, video, args):
|
|
|
148
145
|
(work_dir / RUN_MANIFEST).write_text(
|
|
149
146
|
json.dumps(payload, ensure_ascii=False, indent=2), encoding="utf-8"
|
|
150
147
|
)
|
|
148
|
+
return payload
|
|
151
149
|
|
|
152
150
|
|
|
153
151
|
def _build_multi_source_records(videos, args):
|
|
154
152
|
records = []
|
|
155
153
|
for video in _coerce_videos(videos):
|
|
156
|
-
|
|
154
|
+
identity = material_lib.file_identity(video)
|
|
157
155
|
records.append(
|
|
158
156
|
{
|
|
159
157
|
"source_path": str(video),
|
|
160
158
|
"source_name": video.name,
|
|
161
|
-
"
|
|
162
|
-
"
|
|
163
|
-
"material_id": material_lib.material_id_for(video,
|
|
159
|
+
"source_video_identity": identity,
|
|
160
|
+
"settings": _analysis_settings(args),
|
|
161
|
+
"material_id": material_lib.material_id_for(video, identity),
|
|
164
162
|
}
|
|
165
163
|
)
|
|
166
164
|
records = material_lib.assign_source_ids(records)
|
|
@@ -177,7 +175,7 @@ def _multi_run_manifest_payload(videos, args, source_records):
|
|
|
177
175
|
{
|
|
178
176
|
"source_id": s["source_id"],
|
|
179
177
|
"source_path": s["source_path"],
|
|
180
|
-
"
|
|
178
|
+
"source_video_identity": s["source_video_identity"],
|
|
181
179
|
"source_work_dir": s["source_work_dir"],
|
|
182
180
|
"material_id": s["material_id"],
|
|
183
181
|
}
|
|
@@ -185,6 +183,7 @@ def _multi_run_manifest_payload(videos, args, source_records):
|
|
|
185
183
|
],
|
|
186
184
|
"source_videos": [str(v) for v in _coerce_videos(videos)],
|
|
187
185
|
"settings": _analysis_settings(args),
|
|
186
|
+
"audio": audio_binding(args),
|
|
188
187
|
}
|
|
189
188
|
|
|
190
189
|
|
|
@@ -204,7 +203,7 @@ def _write_multi_source_manifest(work_dir, source_records):
|
|
|
204
203
|
"source_id": s["source_id"],
|
|
205
204
|
"source_path": s["source_path"],
|
|
206
205
|
"source_name": s["source_name"],
|
|
207
|
-
"
|
|
206
|
+
"source_video_identity": s["source_video_identity"],
|
|
208
207
|
"source_work_dir": s["source_work_dir"],
|
|
209
208
|
"material_id": s["material_id"],
|
|
210
209
|
}
|
|
@@ -244,8 +243,9 @@ def _ffmpeg_present_but_cannot_burn():
|
|
|
244
243
|
def _preflight_burn_subtitles(args):
|
|
245
244
|
"""Fail fast BEFORE any understanding/VLM/ASR/TTS spend when subtitle burn-in is on but
|
|
246
245
|
this ffmpeg can't burn it. Without it the run only dies at the final assemble
|
|
247
|
-
`-vf subtitles=` step — after the whole expensive pipeline has run.
|
|
248
|
-
|
|
246
|
+
`-vf subtitles=` step — after the whole expensive pipeline has run. Dub renders through
|
|
247
|
+
dub.py, which never burns subtitles, so it is exempt."""
|
|
248
|
+
if args.edit_mode == "dub" or not _burn_subtitles_intended(args):
|
|
249
249
|
return
|
|
250
250
|
if _ffmpeg_present_but_cannot_burn():
|
|
251
251
|
raise SystemExit(
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
"""Validate recap audio ownership without importing another skill."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
AUDIO_MODES = ("narration", "source-mix", "adopted-packet-copy")
|
|
8
|
+
|
|
9
|
+
_TTS_OPTIONS = frozenset({
|
|
10
|
+
"--tts-provider",
|
|
11
|
+
"--mimo-tts-voice",
|
|
12
|
+
"--voice-ref",
|
|
13
|
+
"--allow-partial-tts",
|
|
14
|
+
"--preserve-approved-text",
|
|
15
|
+
"--review-narration",
|
|
16
|
+
"--no-review-narration",
|
|
17
|
+
"--require-narration-review",
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
_LOCAL_ADOPTION_OPTIONS = (
|
|
21
|
+
("tts_meta", "--tts-meta"),
|
|
22
|
+
("narration_adoption", "--narration-adoption"),
|
|
23
|
+
("audio_mix_adoption", "--audio-mix-adoption"),
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
_LOCAL_ADOPTION_CONFLICTS = _TTS_OPTIONS | frozenset({
|
|
27
|
+
"--mimo-qc",
|
|
28
|
+
"--mimo-qc-refresh",
|
|
29
|
+
"--export-jianying",
|
|
30
|
+
"--jianying-bundle-media",
|
|
31
|
+
"--jianying-no-bundle-media",
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def uses_narration(args):
|
|
36
|
+
return args.audio_mode == "narration"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def uses_local_adoption(args):
|
|
40
|
+
return all(getattr(args, field) is not None for field, _ in _LOCAL_ADOPTION_OPTIONS)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def needs_voiceover(args):
|
|
44
|
+
return uses_narration(args) and not uses_local_adoption(args)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def audio_binding(args):
|
|
48
|
+
binding = {
|
|
49
|
+
"mode": args.audio_mode,
|
|
50
|
+
"selected_stream_index": args.audio_stream_index,
|
|
51
|
+
}
|
|
52
|
+
if uses_local_adoption(args):
|
|
53
|
+
binding["local_adoption"] = {
|
|
54
|
+
field: {"path": str(Path(getattr(args, field)).resolve())}
|
|
55
|
+
for field, _ in _LOCAL_ADOPTION_OPTIONS
|
|
56
|
+
}
|
|
57
|
+
return binding
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _resolve_local_file(parser, value, option):
|
|
61
|
+
path = Path(value).expanduser().resolve()
|
|
62
|
+
if not path.is_file():
|
|
63
|
+
parser.error(f"{option} does not exist or is not a file: {path}")
|
|
64
|
+
return str(path)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def validate_local_adoption(parser, args):
|
|
68
|
+
"""Validate and resolve the assembly-only local adoption boundary."""
|
|
69
|
+
supplied = [getattr(args, field) is not None for field, _ in _LOCAL_ADOPTION_OPTIONS]
|
|
70
|
+
if any(supplied) and not all(supplied):
|
|
71
|
+
parser.error("--tts-meta, --narration-adoption and --audio-mix-adoption are all-or-none")
|
|
72
|
+
if not all(supplied):
|
|
73
|
+
return
|
|
74
|
+
if args.edit_mode != "full" or args.audio_mode != "narration":
|
|
75
|
+
parser.error("local audio adoption requires --edit-mode full and --audio-mode narration")
|
|
76
|
+
if args.audio_stream_index != 0:
|
|
77
|
+
parser.error("local audio adoption requires --audio-stream-index 0")
|
|
78
|
+
if len(args.video) != 1:
|
|
79
|
+
parser.error("local audio adoption requires exactly one input video")
|
|
80
|
+
if args.work_dir is None:
|
|
81
|
+
parser.error("local audio adoption requires explicit --work-dir")
|
|
82
|
+
conflicts = sorted(set(args._explicit_options).intersection(_LOCAL_ADOPTION_CONFLICTS))
|
|
83
|
+
if conflicts:
|
|
84
|
+
parser.error("local audio adoption cannot use: " + ", ".join(conflicts))
|
|
85
|
+
for field, option in _LOCAL_ADOPTION_OPTIONS:
|
|
86
|
+
setattr(args, field, _resolve_local_file(parser, getattr(args, field), option))
|
|
87
|
+
|
|
88
|
+
work_dir = Path(args.work_dir).expanduser().resolve()
|
|
89
|
+
if work_dir.exists():
|
|
90
|
+
parser.error("local audio adoption requires a new --work-dir")
|
|
91
|
+
output_dir = (
|
|
92
|
+
Path(args.output_dir).expanduser().resolve()
|
|
93
|
+
if args.output_dir is not None else work_dir.parent
|
|
94
|
+
)
|
|
95
|
+
# Pre-run guard only: mirrors the assembler's `recap_<stem>.mp4` naming so an existing
|
|
96
|
+
# delivery is refused before work starts. The authoritative published path is read back
|
|
97
|
+
# from the child's assembly_manifest.json after the run.
|
|
98
|
+
delivery = output_dir / f"recap_{Path(args.video[0]).stem}.mp4"
|
|
99
|
+
if delivery.exists():
|
|
100
|
+
parser.error(f"local audio adoption will not overwrite delivery: {delivery}")
|
|
101
|
+
args.work_dir = str(work_dir)
|
|
102
|
+
args.output_dir = str(output_dir)
|
|
103
|
+
|
|
104
|
+
# Ambient authoring configuration is irrelevant because this route never synthesizes
|
|
105
|
+
# or reviews narration. Explicit forms were rejected above.
|
|
106
|
+
args.tts_provider = "auto"
|
|
107
|
+
args.mimo_tts_voice = None
|
|
108
|
+
args.voice_ref = None
|
|
109
|
+
args.mimo_qc = "off"
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def load_local_assembly_evidence(work_dir):
|
|
113
|
+
"""Load the assembler's binding records for the adopted local bundle."""
|
|
114
|
+
work_dir = Path(work_dir)
|
|
115
|
+
try:
|
|
116
|
+
return {
|
|
117
|
+
name: json.loads((work_dir / filename).read_text(encoding="utf-8"))
|
|
118
|
+
for name, filename in (
|
|
119
|
+
("assembly", "assembly_manifest.json"),
|
|
120
|
+
("narration", "narration_input_binding.json"),
|
|
121
|
+
("mix", "audio_mix_binding.json"),
|
|
122
|
+
)
|
|
123
|
+
}
|
|
124
|
+
except (OSError, UnicodeDecodeError, json.JSONDecodeError) as exc:
|
|
125
|
+
raise SystemExit("assembler did not publish readable local adoption evidence") from exc
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _same_file(declared, expected):
|
|
129
|
+
return Path(declared).resolve() == Path(expected).resolve()
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def verify_local_assembly_evidence(evidence, manifest):
|
|
133
|
+
"""The child bindings must reference the same adoption files and picture recap ran with."""
|
|
134
|
+
try:
|
|
135
|
+
local = manifest["audio"]["local_adoption"]
|
|
136
|
+
narration = evidence["narration"]
|
|
137
|
+
mix = evidence["mix"]
|
|
138
|
+
matches = (
|
|
139
|
+
_same_file(narration["adoption"]["path"], local["narration_adoption"]["path"])
|
|
140
|
+
and _same_file(narration["adoption"]["tts_meta"]["path"], local["tts_meta"]["path"])
|
|
141
|
+
and _same_file(mix["adoption"]["path"], local["audio_mix_adoption"]["path"])
|
|
142
|
+
and _same_file(mix["picture"]["path"], manifest["source_video"])
|
|
143
|
+
)
|
|
144
|
+
except (KeyError, TypeError):
|
|
145
|
+
matches = False
|
|
146
|
+
if not matches:
|
|
147
|
+
raise SystemExit("assembler bindings do not match the local adoption manifest")
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def owned_local_delivery(evidence):
|
|
151
|
+
"""Return a stable ownership token only when all child reports bind the published file."""
|
|
152
|
+
try:
|
|
153
|
+
expected = Path(evidence["assembly"]["final_output"]).resolve()
|
|
154
|
+
except (KeyError, TypeError) as exc:
|
|
155
|
+
raise SystemExit(
|
|
156
|
+
"assembly manifest does not declare final_output; cannot own the delivery"
|
|
157
|
+
) from exc
|
|
158
|
+
try:
|
|
159
|
+
declared = [
|
|
160
|
+
Path(evidence["narration"]["final_output"]["path"]).resolve(),
|
|
161
|
+
Path(evidence["mix"]["final_output"]["path"]).resolve(),
|
|
162
|
+
]
|
|
163
|
+
except (KeyError, TypeError):
|
|
164
|
+
return None
|
|
165
|
+
if declared != [expected, expected] or not expected.is_file():
|
|
166
|
+
return None
|
|
167
|
+
stat = expected.stat()
|
|
168
|
+
return {"path": expected, "device": stat.st_dev, "inode": stat.st_ino}
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def remove_owned_local_delivery(token):
|
|
172
|
+
if token is None:
|
|
173
|
+
return
|
|
174
|
+
path = token["path"]
|
|
175
|
+
if not path.is_file():
|
|
176
|
+
return
|
|
177
|
+
stat = path.stat()
|
|
178
|
+
if (stat.st_dev, stat.st_ino) == (token["device"], token["inode"]):
|
|
179
|
+
path.unlink()
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def reject_unbound_narration_workdir(work_dir, args):
|
|
183
|
+
"""Do not reinterpret an unbound narrated work directory as source-owned audio."""
|
|
184
|
+
work_dir = Path(work_dir)
|
|
185
|
+
if (
|
|
186
|
+
not uses_narration(args)
|
|
187
|
+
and not (work_dir / "recap_run_manifest.json").exists()
|
|
188
|
+
and (work_dir / "narration.json").exists()
|
|
189
|
+
):
|
|
190
|
+
raise SystemExit(
|
|
191
|
+
"当前 work_dir 含 narration.json 但缺少可验证的音频运行策略;"
|
|
192
|
+
"非解说模式请使用新的 --work-dir"
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def validate_audio_routing(parser, args):
|
|
197
|
+
"""Reject combinations the current pipeline cannot execute truthfully."""
|
|
198
|
+
validate_local_adoption(parser, args)
|
|
199
|
+
mode = args.audio_mode
|
|
200
|
+
stream = args.audio_stream_index
|
|
201
|
+
if stream < 0:
|
|
202
|
+
parser.error("--audio-stream-index must be a non-negative integer")
|
|
203
|
+
if args.edit_mode == "dub" and mode != "narration":
|
|
204
|
+
parser.error("--edit-mode dub cannot be combined with a non-narration --audio-mode")
|
|
205
|
+
# index-tts + MiMo voice/--voice-ref is rejected once, in recap_runner.main, after the
|
|
206
|
+
# ambient VOICE_REF/MIMO_TTS_VOICE have been folded in.
|
|
207
|
+
if args.tts_provider == "index-tts" and args.edit_mode == "dub":
|
|
208
|
+
parser.error("--edit-mode dub does not support --tts-provider index-tts")
|
|
209
|
+
if mode == "narration":
|
|
210
|
+
if stream != 0:
|
|
211
|
+
parser.error("narration currently requires --audio-stream-index 0")
|
|
212
|
+
return
|
|
213
|
+
|
|
214
|
+
conflicts = sorted(set(args._explicit_options).intersection(_TTS_OPTIONS))
|
|
215
|
+
if conflicts:
|
|
216
|
+
detail = ", ".join(conflicts)
|
|
217
|
+
parser.error(f"--audio-mode {mode} cannot use TTS/strict narration options: {detail}")
|
|
218
|
+
if args.mimo_qc != "off":
|
|
219
|
+
parser.error(f"--audio-mode {mode} currently requires --mimo-qc off")
|
|
220
|
+
if args.edit_mode == "cut" and stream != 0:
|
|
221
|
+
parser.error("cut audio modes currently require --audio-stream-index 0")
|
|
222
|
+
if args.export_jianying and stream != 0:
|
|
223
|
+
parser.error("JianYing export currently requires --audio-stream-index 0")
|
|
224
|
+
# Environment defaults are irrelevant once source audio owns the run. Normalizing
|
|
225
|
+
# prevents an invalid ambient provider from leaking into later state or continuation.
|
|
226
|
+
args.tts_provider = "auto"
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def extend_assemble_args(cli_args, args):
|
|
230
|
+
mode = args.audio_mode
|
|
231
|
+
stream = args.audio_stream_index
|
|
232
|
+
if mode != "narration":
|
|
233
|
+
cli_args += ["--audio-mode", mode]
|
|
234
|
+
if stream != 0:
|
|
235
|
+
cli_args += ["--audio-stream-index", str(stream)]
|
|
236
|
+
return cli_args
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def reject_unsupported_subtitle_track(work_dir, args):
|
|
240
|
+
if args.audio_mode == "source-mix" and (Path(work_dir) / "subtitle_track.json").exists():
|
|
241
|
+
raise SystemExit(
|
|
242
|
+
"source-mix 当前不能绑定显式 subtitle_track.json;请使用新的 work_dir,"
|
|
243
|
+
"或选择 adopted-packet-copy"
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def begin_non_narration_qc(work_dir, args, write_stage):
|
|
248
|
+
"""Start a clean run-local QC ledger without reading or deleting old TTS."""
|
|
249
|
+
(Path(work_dir) / "preflight_qc.json").unlink(missing_ok=True)
|
|
250
|
+
return write_stage(
|
|
251
|
+
work_dir,
|
|
252
|
+
"pre_assemble",
|
|
253
|
+
metadata={
|
|
254
|
+
"audio_mode": args.audio_mode,
|
|
255
|
+
"selected_audio_stream_index": args.audio_stream_index,
|
|
256
|
+
"tts": "not_applicable",
|
|
257
|
+
"narration_validation": "not_applicable",
|
|
258
|
+
"narration_review": "not_applicable",
|
|
259
|
+
"visual_overlays": "preserved_not_authored_by_this_run",
|
|
260
|
+
},
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def begin_local_adoption_qc(work_dir, write_stage):
|
|
265
|
+
"""Record that authoring was intentionally skipped for adopted local assets."""
|
|
266
|
+
return write_stage(
|
|
267
|
+
work_dir,
|
|
268
|
+
"pre_assemble",
|
|
269
|
+
metadata={
|
|
270
|
+
"audio_mode": "narration",
|
|
271
|
+
"selected_audio_stream_index": 0,
|
|
272
|
+
"tts": "adopted_local_not_generated",
|
|
273
|
+
"narration_validation": "not_run",
|
|
274
|
+
"narration_review": "not_run",
|
|
275
|
+
"semantic_validation": "video-assemble",
|
|
276
|
+
"visual_overlays": "not_authored_not_present",
|
|
277
|
+
},
|
|
278
|
+
)
|
|
@@ -45,11 +45,9 @@ def _write_shift_left_stage_qc(work_dir, stage, metadata, findings=None):
|
|
|
45
45
|
|
|
46
46
|
|
|
47
47
|
def _tts_qc_metadata(work_dir):
|
|
48
|
+
"""Called after video-voiceover exited 0, which always writes tts_meta.json."""
|
|
48
49
|
work_dir = Path(work_dir)
|
|
49
|
-
metadata = {}
|
|
50
|
-
tts_meta = work_dir / "tts_meta.json"
|
|
51
|
-
if tts_meta.exists():
|
|
52
|
-
metadata["tts_meta"] = json.loads(tts_meta.read_text(encoding="utf-8"))
|
|
50
|
+
metadata = {"tts_meta": json.loads((work_dir / "tts_meta.json").read_text(encoding="utf-8"))}
|
|
53
51
|
tts_dir = work_dir / "tts_segments"
|
|
54
52
|
if tts_dir.is_dir():
|
|
55
53
|
metadata["tts_segments"] = [
|
|
@@ -59,14 +57,12 @@ def _tts_qc_metadata(work_dir):
|
|
|
59
57
|
|
|
60
58
|
|
|
61
59
|
def _post_render_qc_metadata(work_dir, final_output):
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
)
|
|
69
|
-
return metadata
|
|
60
|
+
"""Called after video-assemble exited 0, which always writes assembly_manifest.json."""
|
|
61
|
+
manifest = Path(work_dir) / ASSEMBLY_MANIFEST
|
|
62
|
+
return {
|
|
63
|
+
"final_output": str(final_output),
|
|
64
|
+
"assembly_manifest": json.loads(manifest.read_text(encoding="utf-8")),
|
|
65
|
+
}
|
|
70
66
|
|
|
71
67
|
|
|
72
68
|
def _write_final_qc_reports(work_dir, final_output):
|
|
@@ -94,6 +90,23 @@ def _print_final_qc_pointer(result):
|
|
|
94
90
|
)
|
|
95
91
|
|
|
96
92
|
|
|
93
|
+
def _require_final_qc(result, work_dir):
|
|
94
|
+
"""Fail closed unless both final summaries are literal blocker-free passes."""
|
|
95
|
+
paths = [Path(work_dir) / name for name in ("final_qc.json", "golden_eval.json")]
|
|
96
|
+
print("[video-recap] 最终 QC 报告: " + "; ".join(map(str, paths)))
|
|
97
|
+
invalid = []
|
|
98
|
+
for name in ("final_qc", "golden_eval"):
|
|
99
|
+
summary = result.get(name) if isinstance(result, dict) else None
|
|
100
|
+
blockers = summary.get("blocker_count") if isinstance(summary, dict) else None
|
|
101
|
+
if not isinstance(summary, dict) or summary.get("ok") is not True or \
|
|
102
|
+
type(blockers) is not int or blockers != 0:
|
|
103
|
+
invalid.append(name)
|
|
104
|
+
if invalid:
|
|
105
|
+
raise SystemExit(
|
|
106
|
+
"严格最终 QC 未通过或摘要格式无效: " + ", ".join(invalid)
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
|
|
97
110
|
def _mimo_qc_stage_enabled(args, stage):
|
|
98
111
|
return (
|
|
99
112
|
args.mimo_qc == "both"
|