@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
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
"""Resolve a ``recap_project.json`` binding into the settings stage skills already accept.
|
|
2
|
+
|
|
3
|
+
Only video-recap reads the library. A bound subtitle style becomes ``SUBTITLE_*`` env for
|
|
4
|
+
video-assemble, a bound voice becomes the voiceover provider / voice arguments, a bound
|
|
5
|
+
BGM becomes ``BGM_PATH``. Stage skills keep seeing concrete paths and values; they never
|
|
6
|
+
learn about templates. Anything the caller already set explicitly must agree with the
|
|
7
|
+
binding — a silent override would make the delivered file disagree with the project.
|
|
8
|
+
"""
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import json
|
|
12
|
+
import os
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
import library as library_lib
|
|
16
|
+
|
|
17
|
+
PROJECT_FILE = "recap_project.json"
|
|
18
|
+
PROJECT_SCHEMA = "video-recap.project.v1"
|
|
19
|
+
PROJECT_KEYS = {"schema", "name", "library", "bindings", "notes"}
|
|
20
|
+
BINDING_KINDS = ("subtitle_style", "packaging", "voice", "bgm")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class BindingError(SystemExit):
|
|
24
|
+
"""A project binding that cannot be applied; exits before any stage runs."""
|
|
25
|
+
|
|
26
|
+
def __init__(self, message):
|
|
27
|
+
super().__init__(f"项目绑定无法使用: {message}")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def project_path(value) -> Path:
|
|
31
|
+
path = Path(value).expanduser().resolve()
|
|
32
|
+
if path.is_dir():
|
|
33
|
+
path = path / PROJECT_FILE
|
|
34
|
+
if not path.is_file():
|
|
35
|
+
raise BindingError(f"找不到 {path}")
|
|
36
|
+
return path
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _value(params, key):
|
|
40
|
+
node = params.get(key)
|
|
41
|
+
return node.get("value") if isinstance(node, dict) else None
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _record(entry):
|
|
45
|
+
return json.loads(Path(entry["record"]).read_text(encoding="utf-8"))
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _subtitle_env(template, record, resources):
|
|
49
|
+
params = record["params"]
|
|
50
|
+
canvas = record["canvas"]
|
|
51
|
+
env = {
|
|
52
|
+
"SUBTITLE_PLAY_RES_X": canvas["width"],
|
|
53
|
+
"SUBTITLE_PLAY_RES_Y": canvas["height"],
|
|
54
|
+
"SUBTITLE_FONT_SIZE": _value(params, "size_px"),
|
|
55
|
+
"SUBTITLE_OUTLINE": _value(params, "outline_px"),
|
|
56
|
+
"SUBTITLE_SHADOW": _value(params, "shadow_px"),
|
|
57
|
+
"SUBTITLE_PRIMARY_COLOR": _value(params, "primary_color"),
|
|
58
|
+
"SUBTITLE_OUTLINE_COLOR": _value(params, "outline_color"),
|
|
59
|
+
"SUBTITLE_MAX_CHARS": _value(params, "max_chars"),
|
|
60
|
+
"SUBTITLE_MAX_LINES": _value(params, "max_lines"),
|
|
61
|
+
}
|
|
62
|
+
font = params["font"]
|
|
63
|
+
if font.get("resource"):
|
|
64
|
+
resource = resources[font["resource"]]
|
|
65
|
+
env["SUBTITLE_FONT_NAME"] = (_record(resource).get("font") or {}).get("family")
|
|
66
|
+
env["SUBTITLE_FONT_FILE"] = resource["files"][0]["path"]
|
|
67
|
+
else:
|
|
68
|
+
env["SUBTITLE_FONT_NAME"] = font["family"]
|
|
69
|
+
band = _value(params, "band")
|
|
70
|
+
if band:
|
|
71
|
+
env["SUBTITLE_ALIGNMENT"] = 2
|
|
72
|
+
env["SUBTITLE_MARGIN_V"] = canvas["height"] - band["y_bot"]
|
|
73
|
+
return {k: str(v) for k, v in env.items() if v is not None}
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _voice_updates(resource, args):
|
|
77
|
+
record = _record(resource)
|
|
78
|
+
voice = record.get("voice") or {}
|
|
79
|
+
provider = voice.get("provider")
|
|
80
|
+
consent = (record.get("consent") or {}).get("status")
|
|
81
|
+
if resource["files"] and consent == "denied":
|
|
82
|
+
raise BindingError(f"音色 {resource['id']} 的参考音频声音授权为 denied")
|
|
83
|
+
if args.tts_provider not in ("auto", provider):
|
|
84
|
+
raise BindingError(f"音色 {resource['id']} 属于 {provider},与 --tts-provider {args.tts_provider} 冲突")
|
|
85
|
+
updates, env = {"tts_provider": provider}, {}
|
|
86
|
+
if provider == "mimo-tts":
|
|
87
|
+
if resource["files"]:
|
|
88
|
+
updates["voice_ref"] = resource["files"][0]["path"]
|
|
89
|
+
else:
|
|
90
|
+
updates["mimo_tts_voice"] = voice.get("voice_id")
|
|
91
|
+
elif provider == "fish-audio":
|
|
92
|
+
env["FISH_TTS_REFERENCE_ID"] = voice.get("voice_id")
|
|
93
|
+
else:
|
|
94
|
+
env["INDEX_TTS_VOICE"] = voice.get("voice_id")
|
|
95
|
+
return updates, env
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _same_setting(key, current, value):
|
|
99
|
+
if key.endswith(("_PATH", "_FILE", "VOICE_REF")):
|
|
100
|
+
return Path(current).expanduser().resolve() == Path(value).expanduser().resolve()
|
|
101
|
+
try:
|
|
102
|
+
return float(current) == float(value)
|
|
103
|
+
except ValueError:
|
|
104
|
+
return current == value
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _check_env_conflicts(env, environ):
|
|
108
|
+
for key, value in env.items():
|
|
109
|
+
current = environ.get(key, "").strip()
|
|
110
|
+
if current and not _same_setting(key, current, value):
|
|
111
|
+
raise BindingError(f"{key}={current} 与项目绑定的 {value} 冲突;删掉其中一处")
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _check_arg_conflicts(updates, args):
|
|
115
|
+
for key, value in updates.items():
|
|
116
|
+
current = getattr(args, key, None)
|
|
117
|
+
if key == "tts_provider" or current in (None, ""):
|
|
118
|
+
continue
|
|
119
|
+
if str(current) != str(value):
|
|
120
|
+
raise BindingError(f"--{key.replace('_', '-')} {current} 与项目绑定的 {value} 冲突")
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def resolve_project(value, args, environ=None, *, include_voice=True) -> dict:
|
|
124
|
+
"""Load and validate a project; return what to apply plus the lock's project block."""
|
|
125
|
+
environ = os.environ if environ is None else environ
|
|
126
|
+
path = project_path(value)
|
|
127
|
+
try:
|
|
128
|
+
project = json.loads(path.read_text(encoding="utf-8"))
|
|
129
|
+
except ValueError as exc:
|
|
130
|
+
raise BindingError(f"{path} 无法解析: {exc}") from None
|
|
131
|
+
if not isinstance(project, dict) or project.get("schema") != PROJECT_SCHEMA:
|
|
132
|
+
raise BindingError(f"{path} 的 schema 必须是 {PROJECT_SCHEMA}")
|
|
133
|
+
unknown = sorted(set(project) - PROJECT_KEYS)
|
|
134
|
+
bindings = project.get("bindings") or {}
|
|
135
|
+
unknown += sorted(f"bindings.{k}" for k in set(bindings) - set(BINDING_KINDS))
|
|
136
|
+
if unknown:
|
|
137
|
+
raise BindingError(f"{path} 有未知字段: {', '.join(unknown)}")
|
|
138
|
+
library_root = (path.parent / str(project.get("library", ""))).resolve()
|
|
139
|
+
if not (library_root / "library.json").is_file():
|
|
140
|
+
raise BindingError(f"库目录无效(缺少 library.json): {library_root}")
|
|
141
|
+
index, report = library_lib.scan_library(library_root)
|
|
142
|
+
resources = {r["id"]: r for r in index["resources"]}
|
|
143
|
+
templates = {f"{t['id']}@v{t['version']}": t for t in index["templates"]}
|
|
144
|
+
errors = {}
|
|
145
|
+
for issue in report.errors:
|
|
146
|
+
errors.setdefault(issue["path"], issue["message"])
|
|
147
|
+
|
|
148
|
+
def require_valid(entry, label):
|
|
149
|
+
rel = Path(entry["record"]).relative_to(library_root).as_posix()
|
|
150
|
+
if rel in errors:
|
|
151
|
+
raise BindingError(f"{label}: {errors[rel]}(先运行 library.py check)")
|
|
152
|
+
|
|
153
|
+
def template(kind):
|
|
154
|
+
ref = bindings.get(kind)
|
|
155
|
+
if not ref:
|
|
156
|
+
return None
|
|
157
|
+
entry = templates.get(ref)
|
|
158
|
+
if entry is None or entry["kind"] != kind:
|
|
159
|
+
raise BindingError(f"{kind} 模板 {ref} 不存在或无效(先运行 library.py check)")
|
|
160
|
+
if entry["status"] != "adopted":
|
|
161
|
+
raise BindingError(f"{kind} 模板 {ref} 的状态是 {entry['status']},只有 adopted 能绑定")
|
|
162
|
+
require_valid(entry, ref)
|
|
163
|
+
for _, rid in entry["refs"]:
|
|
164
|
+
if rid not in resources:
|
|
165
|
+
raise BindingError(f"{ref} 引用的资源 {rid} 不存在或无效(先运行 library.py check)")
|
|
166
|
+
require_valid(resources[rid], f"{ref} 引用的资源 {rid}")
|
|
167
|
+
return entry
|
|
168
|
+
|
|
169
|
+
def resource(kind):
|
|
170
|
+
rid = bindings.get(kind)
|
|
171
|
+
if not rid:
|
|
172
|
+
return None
|
|
173
|
+
entry = resources.get(rid)
|
|
174
|
+
if entry is None or entry["kind"] != kind:
|
|
175
|
+
raise BindingError(f"{kind} 资源 {rid} 不存在或无效(先运行 library.py check)")
|
|
176
|
+
require_valid(entry, f"{kind} 资源 {rid}")
|
|
177
|
+
return entry
|
|
178
|
+
|
|
179
|
+
env, updates, used_templates = {}, {}, []
|
|
180
|
+
subtitle = template("subtitle_style")
|
|
181
|
+
if subtitle:
|
|
182
|
+
env.update(_subtitle_env(subtitle, _record(subtitle), resources))
|
|
183
|
+
used_templates.append({"role": "subtitle_style", "id": subtitle["id"],
|
|
184
|
+
"version": subtitle["version"], "status": subtitle["status"],
|
|
185
|
+
"canvas": subtitle["canvas"]})
|
|
186
|
+
packaging = template("packaging")
|
|
187
|
+
if packaging:
|
|
188
|
+
used_templates.append({"role": "packaging", "id": packaging["id"],
|
|
189
|
+
"version": packaging["version"], "status": packaging["status"],
|
|
190
|
+
"canvas": packaging["canvas"]})
|
|
191
|
+
voice = resource("voice") if include_voice else None
|
|
192
|
+
if voice:
|
|
193
|
+
voice_updates, voice_env = _voice_updates(voice, args)
|
|
194
|
+
updates.update(voice_updates)
|
|
195
|
+
env.update(voice_env)
|
|
196
|
+
bgm = resource("bgm")
|
|
197
|
+
if bgm and getattr(args, "audio_mode", "narration") == "adopted-packet-copy":
|
|
198
|
+
raise BindingError("adopted-packet-copy 冻结原音轨,不能再绑定 BGM;去掉 bindings.bgm 或换声音模式")
|
|
199
|
+
if bgm:
|
|
200
|
+
env["BGM_PATH"] = bgm["files"][0]["path"]
|
|
201
|
+
material_env = environ.get("VIDEO_RECAP_MATERIAL_LIBRARY_DIR", "").strip()
|
|
202
|
+
if (material_env and not getattr(args, "material_library_dir", None)
|
|
203
|
+
and Path(material_env).expanduser().resolve() != library_root):
|
|
204
|
+
raise BindingError(
|
|
205
|
+
f"VIDEO_RECAP_MATERIAL_LIBRARY_DIR={material_env} 与项目的库 {library_root} 不是同一个目录;"
|
|
206
|
+
"素材库与资源库共用根目录,删掉其中一处"
|
|
207
|
+
)
|
|
208
|
+
_check_env_conflicts(env, environ)
|
|
209
|
+
_check_env_conflicts(
|
|
210
|
+
{name: updates[key] for key, name in (("mimo_tts_voice", "MIMO_TTS_VOICE"), ("voice_ref", "VOICE_REF"))
|
|
211
|
+
if key in updates},
|
|
212
|
+
environ,
|
|
213
|
+
)
|
|
214
|
+
_check_arg_conflicts(updates, args)
|
|
215
|
+
return {
|
|
216
|
+
"path": str(path),
|
|
217
|
+
"name": project.get("name", ""),
|
|
218
|
+
"library": str(library_root),
|
|
219
|
+
"env": env,
|
|
220
|
+
"arg_updates": updates,
|
|
221
|
+
"templates": used_templates,
|
|
222
|
+
"resources": [],
|
|
223
|
+
"packaging": packaging and {"record": _record(packaging), "resources": resources},
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def apply_project(resolved, args, environ=None) -> None:
|
|
228
|
+
"""Write the resolved settings where the stage skills will read them."""
|
|
229
|
+
environ = os.environ if environ is None else environ
|
|
230
|
+
for key, value in resolved["env"].items():
|
|
231
|
+
environ[key] = value
|
|
232
|
+
bound = set()
|
|
233
|
+
for key, value in resolved["arg_updates"].items():
|
|
234
|
+
if getattr(args, key, None) != value:
|
|
235
|
+
bound.add(key)
|
|
236
|
+
setattr(args, key, value)
|
|
237
|
+
if not getattr(args, "material_library_dir", None):
|
|
238
|
+
args.material_library_dir = resolved["library"]
|
|
239
|
+
bound.add("material_library_dir")
|
|
240
|
+
args._bound_from_project = frozenset(bound)
|
|
241
|
+
args.resolved_project = resolved
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def check_canvas(resolved, width, height) -> None:
|
|
245
|
+
"""Templates only apply to the canvas they were calibrated on."""
|
|
246
|
+
for template in resolved["templates"]:
|
|
247
|
+
canvas = template["canvas"]
|
|
248
|
+
if (canvas["width"], canvas["height"]) != (width, height):
|
|
249
|
+
raise BindingError(
|
|
250
|
+
f"{template['role']} 模板 {template['id']}@v{template['version']} 按 "
|
|
251
|
+
f"{canvas['width']}x{canvas['height']} 校准,成片画布是 {width}x{height};换一个同画幅的模板"
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
PACKAGING_LAYERS = "packaging_layers.json"
|
|
256
|
+
_WRITTEN_BY = "video-recap --project"
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def sync_packaging_layers(work_dir, resolved) -> None:
|
|
260
|
+
"""Write the bound packaging template's layers for video-assemble, or retire our old copy.
|
|
261
|
+
|
|
262
|
+
A caller-authored ``packaging_layers.json`` (no ``written_by`` marker) is left alone.
|
|
263
|
+
"""
|
|
264
|
+
path = Path(work_dir) / PACKAGING_LAYERS
|
|
265
|
+
packaging = (resolved or {}).get("packaging")
|
|
266
|
+
if not packaging:
|
|
267
|
+
if path.exists():
|
|
268
|
+
try:
|
|
269
|
+
plan = json.loads(path.read_text(encoding="utf-8"))
|
|
270
|
+
except ValueError:
|
|
271
|
+
plan = None
|
|
272
|
+
ours = isinstance(plan, dict) and plan.get("written_by") == _WRITTEN_BY
|
|
273
|
+
if ours:
|
|
274
|
+
path.unlink()
|
|
275
|
+
return
|
|
276
|
+
record, resources = packaging["record"], packaging["resources"]
|
|
277
|
+
layers = [{"name": layer["name"],
|
|
278
|
+
"path": resources[layer["image"]["resource"]]["files"][0]["path"],
|
|
279
|
+
"rect": layer["rect"]}
|
|
280
|
+
for layer in record["params"]["layers"]]
|
|
281
|
+
path.write_text(json.dumps({
|
|
282
|
+
"written_by": _WRITTEN_BY,
|
|
283
|
+
"template": {"id": record["id"], "version": record["version"]},
|
|
284
|
+
"canvas": record["canvas"],
|
|
285
|
+
"layers": layers,
|
|
286
|
+
}, ensure_ascii=False, indent=2), encoding="utf-8")
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""MiMo QC subpackage."""
|
|
@@ -4,7 +4,7 @@ import importlib.util
|
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
|
|
6
6
|
_SPEC = importlib.util.spec_from_file_location(
|
|
7
|
-
"video_recap_mimo_qc_lib", Path(__file__).
|
|
7
|
+
"video_recap_mimo_qc_lib", Path(__file__).resolve().parents[1] / "lib.py"
|
|
8
8
|
)
|
|
9
9
|
_LIB = importlib.util.module_from_spec(_SPEC)
|
|
10
10
|
_SPEC.loader.exec_module(_LIB)
|
package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_evidence.py → qc/mimo_evidence.py}
RENAMED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
import hashlib
|
|
6
5
|
import json
|
|
7
6
|
import os
|
|
8
7
|
from pathlib import Path
|
|
@@ -10,7 +9,7 @@ from typing import Any
|
|
|
10
9
|
from collections.abc import Mapping, Sequence
|
|
11
10
|
|
|
12
11
|
import qc_contract
|
|
13
|
-
from
|
|
12
|
+
from qc.mimo_client import DEFAULT_CONFIG
|
|
14
13
|
|
|
15
14
|
_JSON_ARTIFACTS = (
|
|
16
15
|
"narration.json",
|
|
@@ -48,14 +47,6 @@ _OPTIONAL_VISUAL_METADATA = (
|
|
|
48
47
|
)
|
|
49
48
|
|
|
50
49
|
|
|
51
|
-
def _stable_json(value: Any) -> str:
|
|
52
|
-
return json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
def _fingerprint_value(value: Any) -> str:
|
|
56
|
-
return hashlib.sha256(_stable_json(value).encode("utf-8")).hexdigest()
|
|
57
|
-
|
|
58
|
-
|
|
59
50
|
def _load_json(path: Path) -> Any:
|
|
60
51
|
return json.loads(path.read_text(encoding="utf-8"))
|
|
61
52
|
|
|
@@ -75,7 +66,7 @@ def _summarize(
|
|
|
75
66
|
) -> Any:
|
|
76
67
|
"""Keep request/report evidence bounded while retaining useful structure."""
|
|
77
68
|
if depth >= 4:
|
|
78
|
-
return {"type": type(value).__name__, "
|
|
69
|
+
return {"type": type(value).__name__, "omitted": True}
|
|
79
70
|
if isinstance(value, Mapping):
|
|
80
71
|
out = {
|
|
81
72
|
str(key): _summarize(
|
|
@@ -110,11 +101,12 @@ def _collect_file(work_dir: Path, name: str) -> dict[str, Any] | None:
|
|
|
110
101
|
kind, summary = "json", _summarize(_load_json(path))
|
|
111
102
|
else:
|
|
112
103
|
kind, summary = "text", _summarize(_read_text_sample(path))
|
|
104
|
+
stat = path.stat()
|
|
113
105
|
return {
|
|
114
106
|
"path": name,
|
|
115
107
|
"kind": kind,
|
|
116
|
-
"bytes":
|
|
117
|
-
"
|
|
108
|
+
"bytes": stat.st_size,
|
|
109
|
+
"mtime_ns": stat.st_mtime_ns,
|
|
118
110
|
"summary": summary,
|
|
119
111
|
}
|
|
120
112
|
|
|
@@ -128,21 +120,12 @@ def _collect_multi_source_asr(work_dir: Path) -> dict[str, Any]:
|
|
|
128
120
|
manifest_path = work_dir / "multi_source_manifest.json"
|
|
129
121
|
if not manifest_path.is_file():
|
|
130
122
|
return {}
|
|
131
|
-
root = work_dir.resolve(strict=False)
|
|
132
123
|
collected = {}
|
|
133
124
|
for source in _load_json(manifest_path)["sources"]:
|
|
134
|
-
relative_dir = source["source_work_dir"]
|
|
135
|
-
|
|
136
|
-
if not source_dir.is_relative_to(root):
|
|
137
|
-
continue
|
|
138
|
-
name = _first_existing(source_dir, _SOURCE_ASR_ARTIFACTS)
|
|
125
|
+
relative_dir = source["source_work_dir"] # recap writes sources/<source_id>
|
|
126
|
+
name = _first_existing(work_dir / relative_dir, _SOURCE_ASR_ARTIFACTS)
|
|
139
127
|
if name is not None:
|
|
140
|
-
|
|
141
|
-
if not evidence_path.is_relative_to(root):
|
|
142
|
-
continue
|
|
143
|
-
collected[source["source_id"]] = _collect_file(
|
|
144
|
-
root, str(evidence_path.relative_to(root))
|
|
145
|
-
)
|
|
128
|
+
collected[source["source_id"]] = _collect_file(work_dir, f"{relative_dir}/{name}")
|
|
146
129
|
return collected
|
|
147
130
|
|
|
148
131
|
|
|
@@ -151,55 +134,38 @@ def _resolve_candidate(work_dir: Path, candidate: str | Path) -> Path:
|
|
|
151
134
|
return path if path.is_absolute() else work_dir / path
|
|
152
135
|
|
|
153
136
|
|
|
154
|
-
def
|
|
137
|
+
def _final_output_path(
|
|
155
138
|
work_dir: Path, final_output: str | Path | None
|
|
156
|
-
) ->
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
result = []
|
|
166
|
-
for item in raw:
|
|
167
|
-
path = _resolve_candidate(work_dir, item)
|
|
168
|
-
key = str(path.resolve(strict=False))
|
|
169
|
-
if key not in seen:
|
|
170
|
-
seen.add(key)
|
|
171
|
-
result.append((path, str(item)))
|
|
172
|
-
return result
|
|
139
|
+
) -> tuple[Path, str] | None:
|
|
140
|
+
"""(path, display) of the final output: the caller's, else assembly_manifest.final_output
|
|
141
|
+
(video-assemble always writes it); None before the assembler has run."""
|
|
142
|
+
if final_output is None:
|
|
143
|
+
manifest_path = work_dir / "assembly_manifest.json"
|
|
144
|
+
if not manifest_path.is_file():
|
|
145
|
+
return None
|
|
146
|
+
final_output = _load_json(manifest_path)["final_output"]
|
|
147
|
+
return _resolve_candidate(work_dir, final_output), str(final_output)
|
|
173
148
|
|
|
174
149
|
|
|
175
150
|
def _final_output_metadata(
|
|
176
151
|
work_dir: Path, final_output: str | Path | None = None
|
|
177
152
|
) -> dict[str, Any]:
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
)
|
|
188
|
-
outputs.append(item)
|
|
189
|
-
return {"candidates": outputs}
|
|
153
|
+
resolved = _final_output_path(work_dir, final_output)
|
|
154
|
+
if resolved is None:
|
|
155
|
+
return {"candidates": []}
|
|
156
|
+
path, display = resolved
|
|
157
|
+
item: dict[str, Any] = {"path": display, "exists": path.is_file()}
|
|
158
|
+
if item["exists"]:
|
|
159
|
+
stat = path.stat()
|
|
160
|
+
item.update({"bytes": stat.st_size, "mtime_ns": stat.st_mtime_ns})
|
|
161
|
+
return {"candidates": [item]}
|
|
190
162
|
|
|
191
163
|
|
|
192
164
|
def _existing_final_output(
|
|
193
165
|
work_dir: Path, final_output: str | Path | None
|
|
194
166
|
) -> Path | None:
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
path
|
|
198
|
-
for path, _display in _final_output_candidates(work_dir, final_output)
|
|
199
|
-
if path.is_file()
|
|
200
|
-
),
|
|
201
|
-
None,
|
|
202
|
-
)
|
|
167
|
+
resolved = _final_output_path(work_dir, final_output)
|
|
168
|
+
return resolved[0] if resolved is not None and resolved[0].is_file() else None
|
|
203
169
|
|
|
204
170
|
|
|
205
171
|
def collect_evidence(
|
|
@@ -243,7 +209,6 @@ def collect_evidence(
|
|
|
243
209
|
}
|
|
244
210
|
if not evidence["source_asr"]:
|
|
245
211
|
evidence["source_asr"] = _collect_multi_source_asr(root)
|
|
246
|
-
evidence["fingerprint"] = _fingerprint_value(_cache_evidence(evidence))
|
|
247
212
|
# The evidence goes into the MiMo request as well as the persisted report, so it is
|
|
248
213
|
# redacted here, before either.
|
|
249
214
|
return qc_contract.redact_secrets(evidence)
|
|
@@ -251,19 +216,21 @@ def collect_evidence(
|
|
|
251
216
|
|
|
252
217
|
def _cache_file_group(group: Mapping[str, Any]) -> dict[str, Any]:
|
|
253
218
|
return {
|
|
254
|
-
name: {key: item[key] for key in ("kind", "bytes", "
|
|
219
|
+
name: {key: item[key] for key in ("kind", "bytes", "mtime_ns")}
|
|
255
220
|
for name, item in sorted(group.items())
|
|
256
221
|
}
|
|
257
222
|
|
|
258
223
|
|
|
259
224
|
def _cache_evidence(evidence: Mapping[str, Any]) -> dict[str, Any]:
|
|
225
|
+
"""The per-file identities ({kind, bytes, mtime_ns}) a cached stage report was built
|
|
226
|
+
from; work_dir is excluded so moving the directory is still a cache hit."""
|
|
260
227
|
return {
|
|
261
228
|
"artifacts": _cache_file_group(evidence["artifacts"]),
|
|
262
229
|
"source_asr": _cache_file_group(evidence["source_asr"]),
|
|
263
230
|
"generated_subtitles": _cache_file_group(evidence["generated_subtitles"]),
|
|
264
231
|
"visual_metadata": _cache_file_group(evidence["visual_metadata"]),
|
|
265
232
|
"final_output": [
|
|
266
|
-
{key: item[key] for key in ("exists", "bytes", "
|
|
233
|
+
{key: item[key] for key in ("exists", "bytes", "mtime_ns") if key in item}
|
|
267
234
|
for item in evidence["final_output"]["candidates"]
|
|
268
235
|
],
|
|
269
236
|
}
|
|
@@ -8,9 +8,9 @@ from typing import Any
|
|
|
8
8
|
from collections.abc import Mapping
|
|
9
9
|
|
|
10
10
|
import qc_contract
|
|
11
|
-
from
|
|
12
|
-
from
|
|
13
|
-
from
|
|
11
|
+
from qc.mimo_evidence import _summarize, safe_mimo_config
|
|
12
|
+
from qc.mimo_payload import _strip_json_fence
|
|
13
|
+
from qc.mimo_contract import (
|
|
14
14
|
ARTIFACT_NAME,
|
|
15
15
|
DEFAULT_STAGE,
|
|
16
16
|
MAX_MESSAGE_CHARS,
|
|
@@ -146,10 +146,6 @@ def normalize_observations(
|
|
|
146
146
|
**(raw_evidence if isinstance(raw_evidence, Mapping) else {}),
|
|
147
147
|
"model": model_used,
|
|
148
148
|
"config": cfg,
|
|
149
|
-
"fingerprint": {
|
|
150
|
-
"evidence": payload["evidence_fingerprint"],
|
|
151
|
-
"observation": _fingerprint_value(observation),
|
|
152
|
-
},
|
|
153
149
|
}
|
|
154
150
|
location = obs.get("location")
|
|
155
151
|
findings.append(
|
|
@@ -172,10 +168,6 @@ def normalize_observations(
|
|
|
172
168
|
location=location if isinstance(location, Mapping) else {},
|
|
173
169
|
evidence=evidence,
|
|
174
170
|
model_used=model_used,
|
|
175
|
-
artifact_fingerprints={
|
|
176
|
-
"payload": payload["payload_fingerprint"],
|
|
177
|
-
"evidence": payload["evidence_fingerprint"],
|
|
178
|
-
},
|
|
179
171
|
next_action="human_review",
|
|
180
172
|
decision_reason=message,
|
|
181
173
|
)
|
package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_payload.py → qc/mimo_payload.py}
RENAMED
|
@@ -6,8 +6,8 @@ import json
|
|
|
6
6
|
from typing import Any
|
|
7
7
|
from collections.abc import Mapping, Sequence
|
|
8
8
|
|
|
9
|
-
from
|
|
10
|
-
from
|
|
9
|
+
from qc.mimo_evidence import safe_mimo_config
|
|
10
|
+
from qc.mimo_contract import ARTIFACT_NAME, DEFAULT_STAGE
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
def _semantic_evidence(evidence: Mapping[str, Any], *, stage: str) -> dict[str, Any]:
|
|
@@ -16,12 +16,11 @@ def _semantic_evidence(evidence: Mapping[str, Any], *, stage: str) -> dict[str,
|
|
|
16
16
|
``collect_evidence`` already bounds every artifact independently. The additional
|
|
17
17
|
summarization here therefore needs enough depth to retain the narration/ASR/TTS
|
|
18
18
|
scalars nested inside those summaries. A shallow second pass used to replace the
|
|
19
|
-
values with
|
|
19
|
+
values with opaque placeholders, which made live QC invent missing-script and failed-TTS
|
|
20
20
|
findings from evidence it could no longer read.
|
|
21
21
|
"""
|
|
22
22
|
semantic = dict(evidence)
|
|
23
23
|
semantic.pop("work_dir", None)
|
|
24
|
-
semantic.pop("fingerprint", None)
|
|
25
24
|
semantic["evidence_roles"] = {
|
|
26
25
|
"narration.json": (
|
|
27
26
|
"Planned recap voiceover on the OUTPUT timeline; judge its factual and temporal "
|
|
@@ -104,7 +103,7 @@ def build_payload(
|
|
|
104
103
|
"instructions": (
|
|
105
104
|
"你是 video-recap 的建议性质量审阅器。结合解说、剪辑计划、字幕/ASR、TTS、"
|
|
106
105
|
"组装元数据和抽样画面,指出语义或审美问题。只返回主观观察,不做自动修复,"
|
|
107
|
-
"
|
|
106
|
+
"只根据可见的实际字段判断,不得从文件字节数、截断或省略标记推断内容缺失;"
|
|
108
107
|
"source_asr 是源素材原声证据,不是生成后 TTS 的逐字转录;generated_subtitles 是本轮生成字幕,"
|
|
109
108
|
"二者角色不可混淆,解说改写与源台词不一致本身不是问题;"
|
|
110
109
|
"空 failures 表示没有失败,storyboard 的 labels_burned 仅表示诊断图上的时间标签,"
|
|
@@ -131,9 +130,7 @@ def build_payload(
|
|
|
131
130
|
'"semantic|aesthetic|sampled","evidence":{...}}]}。最多 12 条。'
|
|
132
131
|
),
|
|
133
132
|
"evidence": _semantic_evidence(evidence, stage=stage),
|
|
134
|
-
"evidence_fingerprint": evidence["fingerprint"],
|
|
135
133
|
}
|
|
136
|
-
payload["payload_fingerprint"] = _fingerprint_value(payload)
|
|
137
134
|
return payload
|
|
138
135
|
|
|
139
136
|
|