@oh-story/dsh 0.1.10 → 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 +7 -7
- 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 +1 -1
- 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
|
@@ -1,12 +1,34 @@
|
|
|
1
1
|
"""Shared constants for the self-contained video-assemble skill."""
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
from fractions import Fraction
|
|
4
|
+
import math
|
|
5
|
+
|
|
5
6
|
ASSEMBLY_MANIFEST = "assembly_manifest.json"
|
|
6
7
|
ASSEMBLY_QC = "assembly_qc.json"
|
|
7
8
|
VISUAL_QC = "visual_qc.json"
|
|
8
9
|
VISUAL_OVERLAYS = "visual_overlays.json"
|
|
9
10
|
SEGMENT_AUDIO_SCHEMA_VERSION = 1
|
|
11
|
+
|
|
12
|
+
# The picture codecs every packet/frame clock proof in this skill accepts.
|
|
13
|
+
SUPPORTED_PICTURE_CODECS = frozenset({"h264", "hevc"})
|
|
14
|
+
|
|
15
|
+
# The one exact output audio clock every explicit-sound artifact in this skill uses.
|
|
16
|
+
OUTPUT_SAMPLE_RATE = 48_000
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def frame_clock_samples(frame, fps, rate=OUTPUT_SAMPLE_RATE):
|
|
20
|
+
"""Project an exact frame boundary of a video clock onto the output sample clock.
|
|
21
|
+
|
|
22
|
+
Broadcast rates such as 30000/1001 put frame boundaries between whole samples, so
|
|
23
|
+
the exact Fraction position is rounded half-up once, here. Every sample bound in
|
|
24
|
+
the skill - a segment edge and the whole-picture `total_samples` alike - is this
|
|
25
|
+
single projection, so an integral clock is unchanged and a fractional one stays
|
|
26
|
+
consistent across the explicit-sound tools.
|
|
27
|
+
"""
|
|
28
|
+
exact = Fraction(int(frame) * rate, 1) / Fraction(fps)
|
|
29
|
+
return math.floor(exact + Fraction(1, 2))
|
|
30
|
+
|
|
31
|
+
|
|
10
32
|
FILTER_SCRIPT_THRESHOLD_BYTES = 8000
|
|
11
33
|
|
|
12
34
|
# The default subtitle metrics were tuned in this reference canvas.
|
|
@@ -18,17 +40,12 @@ _SUBTITLE_CLOSING_QUOTES = "」』”’))]】》〉\"'"
|
|
|
18
40
|
_MIN_GAP_TO_SUBTITLE = 0.8
|
|
19
41
|
_MIN_READABLE_SECONDS = 0.3
|
|
20
42
|
_MIN_ASR_CLIP_OVERLAP = 0.05
|
|
43
|
+
# timeline.py serializes interval bounds onto a 1e-4 second grid, flooring starts
|
|
44
|
+
# and ceiling ends, so two bounds that were identical before serialization can come
|
|
45
|
+
# back one grid step apart. Contiguity joins must tolerate that whole step.
|
|
46
|
+
_TIMELINE_TIME_GRID_SECONDS = 1e-4
|
|
47
|
+
_CLIP_CONTIGUITY_TOLERANCE = 1.5 * _TIMELINE_TIME_GRID_SECONDS
|
|
21
48
|
_MAX_ORIGINAL_READ_CPS = 9.0
|
|
22
49
|
_AUTO_ORIGINAL_READ_CPS = 6.0
|
|
23
50
|
|
|
24
|
-
_VISUAL_DELIVERY_FORBIDDEN_KEYS = {
|
|
25
|
-
"video_encode_passes",
|
|
26
|
-
"reencode_reason",
|
|
27
|
-
"audio_sample_rate",
|
|
28
|
-
"final_compat_notes",
|
|
29
|
-
"double_encode",
|
|
30
|
-
"delivery_compatibility",
|
|
31
|
-
"loudness_mode",
|
|
32
|
-
"loudnorm_measurement",
|
|
33
|
-
}
|
|
34
51
|
_SUPPORTED_VISUAL_OVERLAY_TYPES = {"top_title", "inline_label_or_callout"}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"""Assembly manifest/QC persistence and delivery contract helpers."""
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
|
+
from fractions import Fraction
|
|
4
5
|
import math
|
|
6
|
+
import subprocess
|
|
5
7
|
import wave
|
|
6
8
|
from pathlib import Path
|
|
7
9
|
|
|
@@ -12,6 +14,7 @@ from assemble_constants import (
|
|
|
12
14
|
SEGMENT_AUDIO_SCHEMA_VERSION,
|
|
13
15
|
)
|
|
14
16
|
from audio_mix import _loudness_mode
|
|
17
|
+
from subtitles.track_binding import manifest_subtitle_evidence
|
|
15
18
|
from artifacts import (
|
|
16
19
|
_load_work_json,
|
|
17
20
|
_source_video_identity,
|
|
@@ -25,44 +28,60 @@ _AUDIO_QC_CODES = frozenset({
|
|
|
25
28
|
|
|
26
29
|
|
|
27
30
|
def _assembly_manifest_payload(input_video, tts_segments, work_dir, output_path,
|
|
28
|
-
tts_meta_path=None, final_output=None, *,
|
|
31
|
+
tts_meta_path=None, final_output=None, *, settings_payload,
|
|
32
|
+
audio_mode="narration", audio_stream_index=0,
|
|
33
|
+
narration_input_binding=None, audio_mix_binding=None):
|
|
29
34
|
"""Slim render record. The orchestrator reads `final_output` to report the result;
|
|
30
35
|
`source_video` stays None unless cut mode explicitly passed --source-video, proving a
|
|
31
36
|
stale ambient SOURCE_VIDEO never leaked into a full-mode timeline / 剪映 export."""
|
|
32
37
|
input_video = Path(input_video)
|
|
33
38
|
output_path = Path(output_path)
|
|
34
|
-
|
|
39
|
+
source_video_identity = _source_video_identity()
|
|
35
40
|
qc_path = Path(work_dir) / ASSEMBLY_QC
|
|
36
|
-
qc = _load_work_json(work_dir, ASSEMBLY_QC)
|
|
41
|
+
qc = _load_work_json(work_dir, ASSEMBLY_QC) # always written by publish_render first
|
|
42
|
+
settings = settings_payload(
|
|
43
|
+
work_dir, audio_mode=audio_mode, audio_stream_index=audio_stream_index
|
|
44
|
+
)
|
|
37
45
|
payload = {
|
|
38
46
|
"schema_version": 2,
|
|
39
47
|
"input_video": str(input_video.resolve()),
|
|
40
|
-
"source_video":
|
|
41
|
-
"
|
|
48
|
+
"source_video": source_video_identity["path"] if source_video_identity else None,
|
|
49
|
+
"source_video_identity": source_video_identity,
|
|
42
50
|
"tts_meta": str(Path(tts_meta_path).resolve()) if tts_meta_path else None,
|
|
43
51
|
"tts_segments": len(tts_segments),
|
|
44
|
-
"
|
|
52
|
+
"audio_mode": audio_mode,
|
|
53
|
+
"selected_audio_stream_index": audio_stream_index,
|
|
54
|
+
"assembly_settings": settings,
|
|
45
55
|
"output_path": str(output_path.resolve()),
|
|
46
56
|
"segment_audio_schema_version": SEGMENT_AUDIO_SCHEMA_VERSION,
|
|
47
|
-
"qc_path": str(qc_path.resolve())
|
|
48
|
-
"qc_verdict": qc
|
|
49
|
-
"qc_blocking_codes": qc
|
|
50
|
-
# The settings
|
|
57
|
+
"qc_path": str(qc_path.resolve()),
|
|
58
|
+
"qc_verdict": qc["verdict"],
|
|
59
|
+
"qc_blocking_codes": qc["blocking_codes"],
|
|
60
|
+
# The settings payload records the configured/fallback loudness policy; these QC
|
|
51
61
|
# fields record what the just-finished render actually used after the loudnorm probe.
|
|
52
|
-
"qc_loudness_mode": qc
|
|
53
|
-
"qc_loudnorm_measurement": qc
|
|
62
|
+
"qc_loudness_mode": qc["loudness_mode"],
|
|
63
|
+
"qc_loudnorm_measurement": qc["loudnorm_measurement"],
|
|
64
|
+
"audio_operations": qc["audio_operations"],
|
|
65
|
+
"adopted_audio": qc["adopted_audio"],
|
|
66
|
+
"narration_input_binding": narration_input_binding,
|
|
67
|
+
"audio_mix_binding": audio_mix_binding,
|
|
54
68
|
"audio_segments": [
|
|
55
69
|
{
|
|
56
70
|
"index": seg["index"],
|
|
57
|
-
|
|
71
|
+
# Informational manifest fields: a tts_meta without the schema marker is v1,
|
|
72
|
+
# and the loudness measurements are None whenever voiceover skipped
|
|
73
|
+
# normalization (strict adoption fixtures in tests/orchestrator omit both).
|
|
74
|
+
"segment_audio_schema_version": seg.get(
|
|
75
|
+
"segment_audio_schema_version", SEGMENT_AUDIO_SCHEMA_VERSION
|
|
76
|
+
),
|
|
58
77
|
"narration": seg["narration"],
|
|
59
|
-
"spoken_text": seg
|
|
60
|
-
"truncated": seg
|
|
61
|
-
"truncate_reason": seg
|
|
62
|
-
"fit_status": seg
|
|
63
|
-
"blocking": seg
|
|
64
|
-
"audio_duration": seg
|
|
65
|
-
"placed_audio_duration": seg
|
|
78
|
+
"spoken_text": seg["spoken_text"],
|
|
79
|
+
"truncated": seg["truncated"],
|
|
80
|
+
"truncate_reason": seg["truncate_reason"],
|
|
81
|
+
"fit_status": seg["fit_status"],
|
|
82
|
+
"blocking": seg["blocking"],
|
|
83
|
+
"audio_duration": seg["audio_duration"],
|
|
84
|
+
"placed_audio_duration": seg["placed_audio_duration"],
|
|
66
85
|
"placed_audio_path": seg.get("placed_audio_path"),
|
|
67
86
|
"actual_place_start": seg.get("actual_place_start"),
|
|
68
87
|
"actual_place_end": seg.get("actual_place_end"),
|
|
@@ -70,12 +89,15 @@ def _assembly_manifest_payload(input_video, tts_segments, work_dir, output_path,
|
|
|
70
89
|
"source_restore_at": seg.get("source_restore_at"),
|
|
71
90
|
"source_handoff_status": seg.get("source_handoff_status"),
|
|
72
91
|
"source_entry_status": seg.get("source_entry_status"),
|
|
73
|
-
"global_narration_speed": seg
|
|
74
|
-
"segment_tempo_factor": seg
|
|
75
|
-
"effective_tempo": seg
|
|
92
|
+
"global_narration_speed": seg["global_narration_speed"],
|
|
93
|
+
"segment_tempo_factor": seg["segment_tempo_factor"],
|
|
94
|
+
"effective_tempo": seg["effective_tempo"],
|
|
76
95
|
"rms_dbfs_before": seg.get("rms_dbfs_before"),
|
|
77
96
|
"rms_dbfs_after": seg.get("rms_dbfs_after"),
|
|
78
97
|
"peak_after": seg.get("peak_after"),
|
|
98
|
+
"output_start_sample": seg.get("output_start_sample"),
|
|
99
|
+
"output_end_sample": seg.get("output_end_sample"),
|
|
100
|
+
"adopted_gain": seg.get("adopted_gain"),
|
|
79
101
|
}
|
|
80
102
|
for seg in tts_segments
|
|
81
103
|
],
|
|
@@ -85,6 +107,9 @@ def _assembly_manifest_payload(input_video, tts_segments, work_dir, output_path,
|
|
|
85
107
|
provenance = _timeline_provenance_status(work_dir)
|
|
86
108
|
if provenance:
|
|
87
109
|
payload["timeline_provenance"] = provenance
|
|
110
|
+
subtitle_evidence = manifest_subtitle_evidence(work_dir, input_video, output_path)
|
|
111
|
+
if subtitle_evidence is not None:
|
|
112
|
+
payload["subtitle_track"] = subtitle_evidence
|
|
88
113
|
return payload
|
|
89
114
|
|
|
90
115
|
|
|
@@ -115,8 +140,8 @@ def _visual_qc_rollup(visual_qc):
|
|
|
115
140
|
"overlays": {
|
|
116
141
|
"present": overlays["present"],
|
|
117
142
|
"rendered": overlays["rendered"],
|
|
118
|
-
"unsupported": overlays
|
|
119
|
-
"overflow": overlays
|
|
143
|
+
"unsupported": overlays["unsupported"],
|
|
144
|
+
"overflow": overlays["overflow"],
|
|
120
145
|
},
|
|
121
146
|
}
|
|
122
147
|
|
|
@@ -126,10 +151,27 @@ def _placed_audio_matches_timeline(seg):
|
|
|
126
151
|
placed_path = Path(seg["placed_audio_path"])
|
|
127
152
|
if not placed_path.exists():
|
|
128
153
|
return False
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
154
|
+
try:
|
|
155
|
+
with wave.open(str(placed_path), "rb") as placed_wav:
|
|
156
|
+
placed_duration = placed_wav.getnframes() / placed_wav.getframerate()
|
|
157
|
+
tolerance = 1.0 / placed_wav.getframerate()
|
|
158
|
+
except wave.Error:
|
|
159
|
+
result = subprocess.run(
|
|
160
|
+
["ffprobe", "-v", "error", "-select_streams", "a:0", "-show_entries",
|
|
161
|
+
"stream=sample_rate,time_base,duration_ts", "-of", "json", str(placed_path)],
|
|
162
|
+
capture_output=True, text=True, timeout=600,
|
|
163
|
+
)
|
|
164
|
+
streams = json.loads(result.stdout).get("streams", []) if not result.returncode else []
|
|
165
|
+
if len(streams) != 1:
|
|
166
|
+
return False
|
|
167
|
+
stream = streams[0]
|
|
168
|
+
try:
|
|
169
|
+
rate = int(stream["sample_rate"])
|
|
170
|
+
placed_duration = float(Fraction(stream["duration_ts"]) * Fraction(stream["time_base"]))
|
|
171
|
+
tolerance = 1.0 / rate
|
|
172
|
+
except (KeyError, TypeError, ValueError, ZeroDivisionError):
|
|
173
|
+
return False
|
|
174
|
+
timeline_start = math.floor(float(seg["actual_place_start"]) * 10_000 + 1e-9) / 10_000
|
|
133
175
|
timeline_end = math.ceil(float(seg["actual_place_end"]) * 10_000 - 1e-9) / 10_000
|
|
134
176
|
serialized_span = timeline_end - timeline_start
|
|
135
177
|
return (
|
|
@@ -138,9 +180,12 @@ def _placed_audio_matches_timeline(seg):
|
|
|
138
180
|
)
|
|
139
181
|
|
|
140
182
|
|
|
141
|
-
def _build_assembly_qc(tts_segments, video_duration, *,
|
|
142
|
-
|
|
143
|
-
visual_qc=None,
|
|
183
|
+
def _build_assembly_qc(tts_segments, video_duration, *, audio_operations, render_delivery,
|
|
184
|
+
output_path=None, source_has_audio=None, loudness_mode=None,
|
|
185
|
+
loudnorm_measurement=None, visual_qc=None, audio_mode="narration",
|
|
186
|
+
adopted_audio=None,
|
|
187
|
+
narration_input_binding=None, audio_mix_binding=None,
|
|
188
|
+
source_audio_status=None):
|
|
144
189
|
"""Machine-readable assembly release gate.
|
|
145
190
|
|
|
146
191
|
Visual facts are rolled up from visual_qc.json. Delivery/render facts live here
|
|
@@ -150,11 +195,11 @@ def _build_assembly_qc(tts_segments, video_duration, *, output_path=None,
|
|
|
150
195
|
segments = tts_segments
|
|
151
196
|
no_safe = [
|
|
152
197
|
s["index"] for s in segments
|
|
153
|
-
if s
|
|
154
|
-
or s
|
|
155
|
-
or s
|
|
198
|
+
if s["fit_status"] == "no_safe_fit"
|
|
199
|
+
or s["truncate_reason"] in {"no_safe_boundary", "no_room"}
|
|
200
|
+
or s["blocking"]
|
|
156
201
|
]
|
|
157
|
-
skipped = [s["index"] for s in segments if s
|
|
202
|
+
skipped = [s["index"] for s in segments if s["fit_status"] == "skipped"]
|
|
158
203
|
tempo_exceeded = []
|
|
159
204
|
truncated = []
|
|
160
205
|
handoff_failed = []
|
|
@@ -165,7 +210,7 @@ def _build_assembly_qc(tts_segments, video_duration, *, output_path=None,
|
|
|
165
210
|
max_effective = max(max_effective, eff)
|
|
166
211
|
if eff > hard_max + 1e-6:
|
|
167
212
|
tempo_exceeded.append(s["index"])
|
|
168
|
-
if s
|
|
213
|
+
if s["truncated"] or s["truncate_reason"] == "tail_trim_tolerance":
|
|
169
214
|
truncated.append(s["index"])
|
|
170
215
|
if s.get("source_handoff_blocking", False):
|
|
171
216
|
handoff_failed.append(s["index"])
|
|
@@ -174,7 +219,7 @@ def _build_assembly_qc(tts_segments, video_duration, *, output_path=None,
|
|
|
174
219
|
|
|
175
220
|
placed = [s["placed_audio_duration"] for s in segments]
|
|
176
221
|
blocking_codes = []
|
|
177
|
-
if not segments:
|
|
222
|
+
if audio_mode == "narration" and not segments:
|
|
178
223
|
blocking_codes.append("missing_narration")
|
|
179
224
|
if skipped:
|
|
180
225
|
blocking_codes.append("skipped_segments")
|
|
@@ -197,7 +242,9 @@ def _build_assembly_qc(tts_segments, video_duration, *, output_path=None,
|
|
|
197
242
|
)
|
|
198
243
|
if visual_rollup["blocking"]:
|
|
199
244
|
blocking_codes.append("visual_qc_failed")
|
|
200
|
-
if
|
|
245
|
+
if source_audio_status is not None:
|
|
246
|
+
source_audio = source_audio_status
|
|
247
|
+
elif source_has_audio is False:
|
|
201
248
|
# Not blocking: assemble can synthesize a silent original track.
|
|
202
249
|
source_audio = "synthetic_silence"
|
|
203
250
|
elif source_has_audio is True:
|
|
@@ -216,7 +263,6 @@ def _build_assembly_qc(tts_segments, video_duration, *, output_path=None,
|
|
|
216
263
|
if output_path.exists() and output["bytes"] <= 0:
|
|
217
264
|
blocking_codes.append("empty_output")
|
|
218
265
|
|
|
219
|
-
delivery = render_delivery if render_delivery is not None else {}
|
|
220
266
|
return {
|
|
221
267
|
"schema_version": 1,
|
|
222
268
|
"artifact": ASSEMBLY_QC,
|
|
@@ -224,6 +270,11 @@ def _build_assembly_qc(tts_segments, video_duration, *, output_path=None,
|
|
|
224
270
|
"blocking": bool(blocking_codes),
|
|
225
271
|
"blocking_codes": blocking_codes,
|
|
226
272
|
"duration": round(float(video_duration), 4),
|
|
273
|
+
"audio_mode": audio_mode,
|
|
274
|
+
"audio_operations": audio_operations,
|
|
275
|
+
"adopted_audio": adopted_audio,
|
|
276
|
+
"narration_input_binding": narration_input_binding,
|
|
277
|
+
"audio_mix_binding": audio_mix_binding,
|
|
227
278
|
"source_audio": source_audio,
|
|
228
279
|
"loudness_mode": loudness_mode or _loudness_mode(loudnorm_measurement),
|
|
229
280
|
"loudnorm_measurement": loudnorm_measurement,
|
|
@@ -235,10 +286,10 @@ def _build_assembly_qc(tts_segments, video_duration, *, output_path=None,
|
|
|
235
286
|
},
|
|
236
287
|
"visual_qc": visual_rollup,
|
|
237
288
|
"delivery_qc": {
|
|
238
|
-
"video_encode_passes":
|
|
239
|
-
"reencode_reason":
|
|
240
|
-
"audio_sample_rate":
|
|
241
|
-
"final_compat_notes":
|
|
289
|
+
"video_encode_passes": render_delivery["video_encode_passes"],
|
|
290
|
+
"reencode_reason": render_delivery["reencode_reason"],
|
|
291
|
+
"audio_sample_rate": render_delivery["audio_sample_rate"],
|
|
292
|
+
"final_compat_notes": render_delivery["final_compat_notes"],
|
|
242
293
|
},
|
|
243
294
|
"summary": {
|
|
244
295
|
"segments": len(segments),
|
|
@@ -1,31 +1,34 @@
|
|
|
1
|
-
"""Render-affecting settings
|
|
1
|
+
"""Render-affecting settings payload recorded in the manifest and compared by resume logic."""
|
|
2
2
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
|
|
5
|
-
from artifacts import
|
|
6
|
-
from assemble_constants import
|
|
7
|
-
SUBTITLE_RENDER_VERSION,
|
|
8
|
-
SUBTITLE_TEXT_NORMALIZE_VERSION,
|
|
9
|
-
VISUAL_OVERLAYS,
|
|
10
|
-
)
|
|
5
|
+
from artifacts import _artifact_identity
|
|
6
|
+
from assemble_constants import VISUAL_OVERLAYS
|
|
11
7
|
from audio_mix import _loudness_mode, final_loudnorm_filter
|
|
12
8
|
from lib import CONFIG
|
|
13
9
|
from source_subtitles import _has_user_subtitles, _source_subtitle_mask_policy
|
|
14
|
-
from
|
|
10
|
+
from subtitles.core import _subtitle_style_config
|
|
11
|
+
from adoption.narration_binding import binding_record
|
|
12
|
+
from adoption.audio_mix_binding import binding_record as audio_mix_binding_record
|
|
13
|
+
from packaging import packaging_settings
|
|
15
14
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
|
|
16
|
+
def assembly_settings_payload(work_dir=None, *, audio_mode="narration", audio_stream_index=0):
|
|
17
|
+
"""Settings that affect the rendered video, as a plain nested dict compared with ``==`` by
|
|
18
|
+
pipeline resume logic. When work_dir is given, a user_subtitles presence flag and the
|
|
19
|
+
``{size, mtime_ns}`` identity of the overlay/subtitle-track inputs are included so dropping
|
|
20
|
+
in or rewriting one of those files rebuilds the cached subtitles."""
|
|
20
21
|
burn_subtitles = CONFIG["burn_subtitles"]
|
|
21
22
|
mask_policy = _source_subtitle_mask_policy(work_dir)
|
|
22
23
|
mask_source_subtitles = mask_policy["active"]
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
overlay_identity = (
|
|
25
|
+
_artifact_identity(Path(work_dir) / VISUAL_OVERLAYS) if work_dir is not None else None
|
|
26
|
+
)
|
|
27
|
+
subtitle_track_identity = (
|
|
28
|
+
_artifact_identity(Path(work_dir) / "subtitle_track.json")
|
|
29
|
+
if work_dir is not None else None
|
|
25
30
|
)
|
|
26
|
-
|
|
27
|
-
"version": SUBTITLE_RENDER_VERSION,
|
|
28
|
-
"subtitle_text_normalize": SUBTITLE_TEXT_NORMALIZE_VERSION,
|
|
31
|
+
settings = {
|
|
29
32
|
"user_subtitles": _has_user_subtitles(work_dir),
|
|
30
33
|
"burn_subtitles": burn_subtitles,
|
|
31
34
|
"force_video_reencode": CONFIG["force_video_reencode"],
|
|
@@ -55,19 +58,55 @@ def assembly_settings_fingerprint(work_dir=None):
|
|
|
55
58
|
"subtitle_y_bot": CONFIG["subtitle_y_bot"],
|
|
56
59
|
"visual_overlays": {
|
|
57
60
|
"artifact": VISUAL_OVERLAYS,
|
|
58
|
-
"present":
|
|
59
|
-
"
|
|
61
|
+
"present": overlay_identity is not None,
|
|
62
|
+
"identity": overlay_identity,
|
|
60
63
|
},
|
|
64
|
+
"packaging_layers": packaging_settings(work_dir),
|
|
61
65
|
},
|
|
62
|
-
"
|
|
63
|
-
"
|
|
64
|
-
"
|
|
65
|
-
"fade_ms": CONFIG["fade_ms"],
|
|
66
|
-
"narration_speed": CONFIG["narration_speed"],
|
|
67
|
-
"narration_cumulative_tempo_max": CONFIG["narration_cumulative_tempo_max"],
|
|
68
|
-
"tts_segment_tempo_max": CONFIG["tts_segment_tempo_max"],
|
|
66
|
+
"audio": {
|
|
67
|
+
"mode": audio_mode,
|
|
68
|
+
"selected_stream_index": audio_stream_index,
|
|
69
69
|
},
|
|
70
|
-
|
|
70
|
+
}
|
|
71
|
+
explicit_mix = (
|
|
72
|
+
audio_mix_binding_record(work_dir)
|
|
73
|
+
if work_dir is not None and audio_mode == "narration" else None
|
|
74
|
+
)
|
|
75
|
+
if explicit_mix:
|
|
76
|
+
settings["audio"]["path"] = "explicit_adopted_full_sound"
|
|
77
|
+
settings["audio_mix_binding"] = explicit_mix
|
|
78
|
+
if audio_mode == "narration":
|
|
79
|
+
narration_binding = binding_record(work_dir) if work_dir else None
|
|
80
|
+
settings["narration_input_binding"] = narration_binding
|
|
81
|
+
adopted_tempo = (
|
|
82
|
+
narration_binding.get("tempo_policy") if narration_binding else None
|
|
83
|
+
)
|
|
84
|
+
settings["narration_timing"] = {
|
|
85
|
+
"delay_seconds": 0.0 if explicit_mix else CONFIG["narration_delay_seconds"],
|
|
86
|
+
"tail_pad_seconds": 0.0 if explicit_mix else CONFIG["narration_tail_pad_seconds"],
|
|
87
|
+
"fade_ms": 0 if explicit_mix else CONFIG["fade_ms"],
|
|
88
|
+
"narration_speed": (
|
|
89
|
+
1.0 if explicit_mix else
|
|
90
|
+
adopted_tempo["global_atempo"] if adopted_tempo else CONFIG["narration_speed"]
|
|
91
|
+
),
|
|
92
|
+
"tempo_source": (
|
|
93
|
+
"explicit_audio_mix" if explicit_mix else
|
|
94
|
+
"adoption" if adopted_tempo else "configuration"
|
|
95
|
+
),
|
|
96
|
+
"narration_cumulative_tempo_max": CONFIG["narration_cumulative_tempo_max"],
|
|
97
|
+
"tts_segment_tempo_max": (
|
|
98
|
+
adopted_tempo["segment_tempo_max"]
|
|
99
|
+
if explicit_mix and adopted_tempo else CONFIG["tts_segment_tempo_max"]
|
|
100
|
+
),
|
|
101
|
+
}
|
|
102
|
+
if explicit_mix and adopted_tempo:
|
|
103
|
+
settings["narration_timing"]["narration_cumulative_tempo_max"] = \
|
|
104
|
+
adopted_tempo["cumulative_tempo_max"]
|
|
105
|
+
settings["narration_timing"]["narration_cumulative_tempo_hard_max"] = \
|
|
106
|
+
adopted_tempo["cumulative_tempo_hard_max"]
|
|
107
|
+
if audio_mode in {"narration", "source-mix"} and not explicit_mix:
|
|
108
|
+
# adopted-packet-copy never decodes or mixes, so mix settings cannot change it.
|
|
109
|
+
settings["audio_mix"] = {
|
|
71
110
|
"ducking_mode": CONFIG["ducking_mode"],
|
|
72
111
|
"duck_fade_seconds": CONFIG["duck_fade_seconds"],
|
|
73
112
|
"duck_bridge_seconds": CONFIG["duck_bridge_seconds"],
|
|
@@ -87,9 +126,13 @@ def assembly_settings_fingerprint(work_dir=None):
|
|
|
87
126
|
"bgm_path": CONFIG["bgm_path"],
|
|
88
127
|
"bgm_volume": CONFIG["bgm_volume"],
|
|
89
128
|
"bgm_ducking_volume": CONFIG["bgm_ducking_volume"],
|
|
90
|
-
}
|
|
91
|
-
}
|
|
129
|
+
}
|
|
92
130
|
if burn_subtitles:
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
131
|
+
settings["subtitle_renderer"] = "ass"
|
|
132
|
+
settings["subtitle_style"] = _subtitle_style_config()
|
|
133
|
+
if subtitle_track_identity is not None:
|
|
134
|
+
settings["subtitle_track"] = {
|
|
135
|
+
"artifact": "subtitle_track.json",
|
|
136
|
+
"identity": subtitle_track_identity,
|
|
137
|
+
}
|
|
138
|
+
return settings
|
|
@@ -4,13 +4,13 @@ import json
|
|
|
4
4
|
import re
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
|
|
7
|
-
from artifacts import _load_work_json
|
|
7
|
+
from artifacts import _load_work_json
|
|
8
8
|
from audio_automation import (
|
|
9
9
|
coalesce_duck_windows,
|
|
10
10
|
ducking_expression,
|
|
11
11
|
release_ducking_expression,
|
|
12
12
|
)
|
|
13
|
-
from lib import CONFIG, log, run_cmd
|
|
13
|
+
from lib import CONFIG, filter_file_args, log, run_cmd
|
|
14
14
|
|
|
15
15
|
def _limiter_filter():
|
|
16
16
|
return f"alimiter=limit={CONFIG['final_limiter_peak']:.2f}:level=false"
|
|
@@ -92,7 +92,7 @@ def _run_loudnorm_first_pass(input_video, narration_wav, original_audio_input,
|
|
|
92
92
|
"-i", str(narration_wav),
|
|
93
93
|
*original_audio_input,
|
|
94
94
|
*bgm_input,
|
|
95
|
-
"
|
|
95
|
+
*filter_file_args("filter_complex", probe_script),
|
|
96
96
|
"-map", "[lnprobe]",
|
|
97
97
|
"-f", "null", "-",
|
|
98
98
|
]
|
|
@@ -126,13 +126,13 @@ def _load_sentence_handoff_anchors(work_dir):
|
|
|
126
126
|
if payload is None:
|
|
127
127
|
return [], None, {"require_measured": cut_mode}
|
|
128
128
|
if cut_mode:
|
|
129
|
-
# Output-clock anchors are only trusted when they
|
|
130
|
-
|
|
129
|
+
# Output-clock anchors are only trusted when they are at least as new as the cut plan.
|
|
130
|
+
plan_path = work_dir / "clip_plan_validated.json"
|
|
131
131
|
fresh = (
|
|
132
132
|
payload.get("schema_version") == 2
|
|
133
133
|
and payload.get("timeline") == "cut_output"
|
|
134
|
-
and
|
|
135
|
-
and
|
|
134
|
+
and plan_path.exists()
|
|
135
|
+
and (work_dir / artifact).stat().st_mtime_ns >= plan_path.stat().st_mtime_ns
|
|
136
136
|
)
|
|
137
137
|
if not fresh:
|
|
138
138
|
return [], None, {"require_measured": True}
|
|
@@ -172,7 +172,7 @@ def _handoff_speech_evidence(work_dir, payload):
|
|
|
172
172
|
speech = _timed_rows(_asr_segments(work_dir))
|
|
173
173
|
if not quiet:
|
|
174
174
|
silence = _load_work_json(work_dir, "silence_periods.json") or []
|
|
175
|
-
quiet = _timed_rows(row for row in silence if not row
|
|
175
|
+
quiet = _timed_rows(row for row in silence if not row["has_speech"])
|
|
176
176
|
return speech, quiet
|
|
177
177
|
|
|
178
178
|
|
|
@@ -278,7 +278,7 @@ def _apply_source_sentence_handoffs(tts_segments, work_dir, video_duration):
|
|
|
278
278
|
speech_spans,
|
|
279
279
|
quiet_windows,
|
|
280
280
|
anchors,
|
|
281
|
-
seg
|
|
281
|
+
seg["overlaps_speech"],
|
|
282
282
|
require_measured=require_measured,
|
|
283
283
|
)
|
|
284
284
|
seg["overlaps_speech"] = measured
|
|
@@ -374,7 +374,7 @@ def _duck_envelope(tts_segments, idle, speech_vol, quiet_vol, fade, bridge):
|
|
|
374
374
|
continue
|
|
375
375
|
hold_end = max(narration_end, seg.get("source_duck_end", narration_end))
|
|
376
376
|
restore_at = max(hold_end, seg.get("source_restore_at", hold_end + fade))
|
|
377
|
-
level = speech_vol if seg
|
|
377
|
+
level = speech_vol if seg["overlaps_speech"] else quiet_vol
|
|
378
378
|
windows.append((start, hold_end, level, restore_at))
|
|
379
379
|
return release_ducking_expression(windows, idle, fade, bridge=bridge)
|
|
380
380
|
|
|
@@ -455,7 +455,7 @@ def _build_audio_filter_complex(
|
|
|
455
455
|
quiet_vol = CONFIG["zone_ducking_volume"]
|
|
456
456
|
expr = _duck_envelope(tts_segments, idle, speech_vol, quiet_vol, fade, bridge)
|
|
457
457
|
if expr:
|
|
458
|
-
n_overlap = sum(1 for s in tts_segments if s
|
|
458
|
+
n_overlap = sum(1 for s in tts_segments if s["overlaps_speech"])
|
|
459
459
|
n_quiet = len(tts_segments) - n_overlap
|
|
460
460
|
log(f"gap-fill ducking: 间隙原声={idle}, 对白段={speech_vol}({n_overlap}), 安静段={quiet_vol}({n_quiet}), 桥接间隙<{bridge}s")
|
|
461
461
|
orig = f"{original_in}volume='{expr}':eval=frame,aresample=48000[orig];"
|