@oh-story/dsh 0.1.9 → 0.1.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -14
- package/lib/client.js +209 -73
- package/lib/client.js.map +3 -3
- package/lib/drama/manifest.json +248 -168
- package/lib/drama/skills/short-drama/SKILL.md +16 -4
- package/lib/drama/skills/short-drama/assets/project-template/short-drama.json +3 -0
- package/lib/drama/skills/short-drama/references/creator-documents.md +3 -0
- package/lib/drama/skills/short-drama/references/knowhow-index.md +15 -0
- package/lib/drama/skills/short-drama/scripts/creator_markdown_check.py +14 -0
- package/lib/drama/skills/short-drama/scripts/project_tool.py +131 -0
- package/lib/drama/skills/short-drama-assets/SKILL.md +2 -1
- package/lib/drama/skills/short-drama-assets/references/asset-review-checklist.md +2 -0
- package/lib/drama/skills/short-drama-assets/references/character-and-look.md +10 -0
- package/lib/drama/skills/short-drama-assets/references/continuity-lock.md +6 -9
- package/lib/drama/skills/short-drama-assets/references/era-anchors.md +37 -0
- package/lib/drama/skills/short-drama-assets/references/stage-contract.md +2 -0
- package/lib/drama/skills/short-drama-assets/references/voice-direction.md +28 -0
- package/lib/drama/skills/short-drama-develop/SKILL.md +10 -4
- package/lib/drama/skills/short-drama-develop/assets/adaptation-map.example.jsonl +1 -1
- package/lib/drama/skills/short-drama-develop/assets/creative-brief.md +8 -0
- package/lib/drama/skills/short-drama-develop/references/adaptation-craft.md +80 -13
- package/lib/drama/skills/short-drama-develop/references/episode-design.md +95 -12
- package/lib/drama/skills/short-drama-develop/references/genre-and-hook-playbook.md +9 -5
- package/lib/drama/skills/short-drama-develop/references/genre-cards//345/244/215/344/273/207/346/211/223/350/204/270.md +1 -1
- package/lib/drama/skills/short-drama-develop/references/genre-cards//347/263/273/347/273/237/346/265/201.md +51 -0
- package/lib/drama/skills/short-drama-develop/references/genre-cards//351/200/206/350/242/255.md +51 -0
- package/lib/drama/skills/short-drama-develop/references/genre-cards//351/207/215/347/224/237/347/251/277/350/266/212.md +51 -0
- package/lib/drama/skills/short-drama-develop/references/genre-cards.md +10 -7
- package/lib/drama/skills/short-drama-develop/references/premise-devices.md +15 -9
- package/lib/drama/skills/short-drama-develop/references/serial-character-and-memory.md +3 -2
- package/lib/drama/skills/short-drama-develop/references/stage-contract.md +14 -7
- package/lib/drama/skills/short-drama-develop/references/story-craft.md +7 -3
- package/lib/drama/skills/short-drama-edit/SKILL.md +65 -44
- package/lib/drama/skills/short-drama-edit/assets/remotion/README.md +49 -13
- package/lib/drama/skills/short-drama-edit/assets/remotion/package.json +4 -2
- package/lib/drama/skills/short-drama-edit/assets/remotion/src/Overlay.tsx +31 -0
- package/lib/drama/skills/short-drama-edit/assets/remotion/src/Root.tsx +5 -5
- package/lib/drama/skills/short-drama-edit/assets/remotion/src/Subtitles.tsx +68 -69
- package/lib/drama/skills/short-drama-edit/assets/remotion/src/font.ts +39 -29
- package/lib/drama/skills/short-drama-edit/assets/remotion/src/rules.mjs +188 -0
- package/lib/drama/skills/short-drama-edit/assets/remotion/src/schema.ts +26 -13
- package/lib/drama/skills/short-drama-edit/assets/remotion/src/screen/Card.tsx +72 -0
- package/lib/drama/skills/short-drama-edit/assets/remotion/src/screen/CornerChip.tsx +39 -0
- package/lib/drama/skills/short-drama-edit/assets/remotion/src/screen/ScreenText.tsx +36 -0
- package/lib/drama/skills/short-drama-edit/assets/remotion/src/screen/SystemPanel.tsx +44 -0
- package/lib/drama/skills/short-drama-edit/assets/remotion/src/screen/TaskPanel.tsx +100 -0
- package/lib/drama/skills/short-drama-edit/assets/remotion/src/screen/chrome.tsx +197 -0
- package/lib/drama/skills/short-drama-edit/assets/remotion/src/screen/tokens.ts +62 -0
- package/lib/drama/skills/short-drama-edit/assets/remotion/tsconfig.json +1 -0
- package/lib/drama/skills/short-drama-edit/assets//345/211/252/350/276/221/345/215/225.md +6 -4
- package/lib/drama/skills/short-drama-edit/references/cut-craft.md +26 -67
- package/lib/drama/skills/short-drama-edit/references/delivery-verify.md +31 -56
- package/lib/drama/skills/short-drama-edit/references/generated-footage.md +42 -78
- package/lib/drama/skills/short-drama-edit/references/sound-and-subtitles.md +44 -92
- package/lib/drama/skills/short-drama-edit/references/stage-contract.md +14 -16
- package/lib/drama/skills/short-drama-edit/scripts/edit_tool.py +1272 -125
- package/lib/drama/skills/short-drama-edit/scripts/selftest.py +113 -9
- package/lib/drama/skills/short-drama-image-prompts/SKILL.md +2 -0
- package/lib/drama/skills/short-drama-image-prompts/references/common-recipe.md +11 -0
- package/lib/drama/skills/short-drama-novel-analyze/SKILL.md +3 -3
- package/lib/drama/skills/short-drama-novel-analyze/assets/episode-candidate.example.jsonl +2 -2
- package/lib/drama/skills/short-drama-novel-analyze/references/adaptation-triage.md +8 -4
- package/lib/drama/skills/short-drama-novel-analyze/references/adaptation-value.md +10 -3
- package/lib/drama/skills/short-drama-novel-analyze/references/aggregation-and-entities.md +25 -1
- package/lib/drama/skills/short-drama-novel-analyze/references/stage-contract.md +1 -0
- package/lib/drama/skills/short-drama-produce/SKILL.md +5 -1
- package/lib/drama/skills/short-drama-produce/references/adapter-contract.md +14 -20
- package/lib/drama/skills/short-drama-produce/references/providers/minimax-h3-video.md +8 -26
- package/lib/drama/skills/short-drama-produce/references/providers/minimax-speech.md +41 -0
- package/lib/drama/skills/short-drama-produce/scripts/provider_adapters.py +142 -1
- package/lib/drama/skills/short-drama-review/SKILL.md +4 -5
- package/lib/drama/skills/short-drama-review/references/production-quality-gates.md +6 -19
- package/lib/drama/skills/short-drama-review/references/review-method.md +6 -1
- package/lib/drama/skills/short-drama-review/references/rubric-assets-prompts.md +11 -0
- package/lib/drama/skills/short-drama-review/references/rubric-story-script.md +77 -3
- package/lib/drama/skills/short-drama-review/references/rubric-visual-motion.md +24 -35
- package/lib/drama/skills/short-drama-review/references/stage-contract.md +3 -0
- package/lib/drama/skills/short-drama-storyboard/SKILL.md +23 -11
- package/lib/drama/skills/short-drama-storyboard/references/blocking-playbooks.md +1 -1
- package/lib/drama/skills/short-drama-storyboard/references/lighting-craft.md +39 -0
- package/lib/drama/skills/short-drama-storyboard/references/production-shot-grammar.md +2 -2
- package/lib/drama/skills/short-drama-storyboard/references/shot-craft.md +139 -34
- package/lib/drama/skills/short-drama-storyboard/references/stage-contract.md +5 -3
- package/lib/drama/skills/short-drama-video-prompts/SKILL.md +15 -12
- package/lib/drama/skills/short-drama-video-prompts/references/camera-audio-continuity.md +29 -0
- package/lib/drama/skills/short-drama-video-prompts/references/delivery-profile.md +4 -5
- package/lib/drama/skills/short-drama-video-prompts/references/generability.md +4 -2
- package/lib/drama/skills/short-drama-video-prompts/references/minimax-h3.md +52 -39
- package/lib/drama/skills/short-drama-video-prompts/references/motion-recipe.md +24 -21
- package/lib/drama/skills/short-drama-video-prompts/references/performance-action-timing.md +9 -23
- package/lib/drama/skills/short-drama-video-prompts/references/production-prompt-grammar.md +14 -26
- package/lib/drama/skills/short-drama-video-prompts/references/review-and-fixtures.md +1 -1
- package/lib/drama/skills/short-drama-video-prompts/references/seedance-2.0.md +2 -3
- package/lib/drama/skills/short-drama-video-prompts/references/seedance-2.5.md +34 -9
- package/lib/drama/skills/short-drama-video-prompts/references/stage-contract.md +3 -2
- package/lib/drama/skills/short-drama-video-prompts/references/target-model-profile.md +8 -1
- package/lib/drama/skills/short-drama-video-prompts/references/wan-3.0.md +103 -0
- package/lib/drama/skills/short-drama-video-prompts/scripts/motion_timing_check.py +8 -7
- package/lib/drama/skills/short-drama-write/SKILL.md +10 -7
- package/lib/drama/skills/short-drama-write/references/dialogue-craft.md +1 -1
- package/lib/drama/skills/short-drama-write/references/script-craft.md +56 -3
- package/lib/drama/skills/short-drama-write/references/stage-contract.md +4 -0
- package/lib/drama/skills/short-drama-write/scripts/duration_estimate.py +48 -2
- package/lib/index.js +346 -98
- package/lib/index.js.map +4 -4
- package/lib/novel-to-game/README.md +299 -59
- package/lib/novel-to-game/README_ZH.md +299 -56
- package/lib/novel-to-game/VERSION +1 -1
- package/lib/novel-to-game/examples/jin-ping-mei/build/BUILD_BRIEF.md +3 -1
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/RUN.md +11 -3
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/css/new-guofeng.css +397 -8
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/css/style.css +0 -31
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/data.js +71 -73
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/engine.js +78 -89
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/main.js +37 -14
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/text.js +5 -5
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/package.json +2 -1
- package/lib/novel-to-game/examples/jin-ping-mei/qa/README.md +11 -16
- package/lib/novel-to-game/examples/jin-ping-mei/qa/verification.json +11 -11
- package/lib/novel-to-game/examples/jin-ping-mei/qa/verify_playable_model.mjs +23 -0
- package/lib/novel-to-game/manifest.json +77 -87
- package/lib/novel-to-game/skills/game-art-direction/SKILL.md +7 -12
- package/lib/novel-to-game/skills/game-art-direction/references/art-direction-method.md +27 -106
- package/lib/novel-to-game/skills/game-build/SKILL.md +13 -14
- package/lib/novel-to-game/skills/game-build/references/blender-asset-workflow.md +24 -0
- package/lib/novel-to-game/skills/game-build/references/build-brief-contract.md +45 -82
- package/lib/novel-to-game/skills/game-build/references/generative-media-pipeline.md +14 -45
- package/lib/novel-to-game/skills/game-build/references/playable-model-contract.md +30 -65
- package/lib/novel-to-game/skills/game-build/references/tts-production-contract.md +13 -27
- package/lib/novel-to-game/skills/game-concept/SKILL.md +13 -15
- package/lib/novel-to-game/skills/game-concept/references/concept-method.md +28 -75
- package/lib/novel-to-game/skills/game-qa/SKILL.md +6 -7
- package/lib/novel-to-game/skills/game-qa/references/test-design-method.md +9 -5
- package/lib/novel-to-game/skills/game-world-design/SKILL.md +12 -16
- package/lib/novel-to-game/skills/game-world-design/references/narrative-design-method.md +23 -72
- package/lib/novel-to-game/skills/game-world-design/references/numeric-design-method.md +14 -48
- package/lib/novel-to-game/skills/game-world-design/references/world-design-method.md +36 -103
- package/lib/novel-to-game/skills/novel-game-analyze/SKILL.md +4 -10
- package/lib/novel-to-game/skills/novel-game-analyze/references/gameability-protocol.md +4 -4
- package/lib/novel-to-game/skills/novel-to-game/SKILL.md +9 -21
- package/lib/novel-to-game/skills/novel-to-game/references/intake-method.md +5 -4
- package/lib/novel-to-game/skills/novel-to-game/references/pipeline-contract.md +5 -8
- package/lib/oh-story/manifest.json +607 -327
- package/lib/oh-story/roles/chapter-extractor.md +177 -237
- package/lib/oh-story/roles/character-designer.md +1 -2
- package/lib/oh-story/roles/consistency-checker.md +15 -13
- package/lib/oh-story/roles/narrative-writer.md +36 -38
- package/lib/oh-story/roles/story-architect.md +9 -61
- package/lib/oh-story/roles/story-explorer.md +20 -125
- package/lib/oh-story/roles/story-researcher.md +0 -1
- package/lib/oh-story/skills/story/SKILL.md +15 -9
- package/lib/oh-story/skills/story/VERSION +1 -1
- package/lib/oh-story/skills/story/references/author-memory-maintenance.md +75 -0
- package/lib/oh-story/skills/story/references/author-memory.md +46 -94
- package/lib/oh-story/skills/story/scripts/author_memory_commit.py +934 -170
- package/lib/oh-story/skills/story-cover/SKILL.md +10 -1
- package/lib/oh-story/skills/story-deslop/SKILL.md +59 -73
- package/lib/oh-story/skills/story-deslop/references/agent-calls.md +5 -0
- package/lib/oh-story/skills/story-deslop/references/anti-ai-writing.md +12 -11
- package/lib/oh-story/skills/story-deslop/references/author-memory-maintenance.md +75 -0
- package/lib/oh-story/skills/story-deslop/references/author-memory.md +46 -94
- package/lib/oh-story/skills/story-deslop/references/banned-words.md +2 -2
- package/lib/oh-story/skills/story-deslop/references/deslop-gates.md +22 -5
- package/lib/oh-story/skills/story-deslop/references/style-resolution.md +4 -4
- package/lib/oh-story/skills/story-deslop/scripts/author_memory_commit.py +934 -170
- package/lib/oh-story/skills/story-deslop/scripts/check-ai-patterns.js +155 -8
- package/lib/oh-story/skills/story-import/SKILL.md +64 -584
- package/lib/oh-story/skills/story-import/references/character-state-reverse.md +1 -1
- package/lib/oh-story/skills/story-import/references/deep-analysis.md +47 -0
- package/lib/oh-story/skills/story-import/references/format-and-structure.md +1 -1
- package/lib/oh-story/skills/story-import/references/import-report.md +64 -0
- package/lib/oh-story/skills/story-import/references/import-special-cases.md +34 -0
- package/lib/oh-story/skills/story-import/references/import-tracking.md +95 -0
- package/lib/oh-story/skills/story-import/references/length-routing.md +1 -1
- package/lib/oh-story/skills/story-import/references/outline-reverse-rules.md +79 -0
- package/lib/oh-story/skills/story-import/references/outline-reverse.md +24 -0
- package/lib/oh-story/skills/story-import/references/state-tracking.md +4 -69
- package/lib/oh-story/skills/story-import/references/structure-mapping-long.md +98 -92
- package/lib/oh-story/skills/story-import/references/structure-mapping-short.md +24 -12
- package/lib/oh-story/skills/story-import/references/tracking-initialization.md +1 -1
- package/lib/oh-story/skills/story-import/references/tracking-transaction.md +15 -11
- package/lib/oh-story/skills/story-import/scripts/build_outline_brief.py +332 -0
- package/lib/oh-story/skills/story-import/scripts/check-outline-contract.js +132 -27
- package/lib/oh-story/skills/story-import/scripts/tracking_commit.py +478 -15
- package/lib/oh-story/skills/story-import/scripts/wordcount_core.py +182 -23
- package/lib/oh-story/skills/story-long-analyze/SKILL.md +81 -270
- package/lib/oh-story/skills/story-long-analyze/references/author-facing.md +252 -0
- package/lib/oh-story/skills/story-long-analyze/references/deconstruction-notes.md +3 -3
- package/lib/oh-story/skills/story-long-analyze/references/final-checks.md +19 -0
- package/lib/oh-story/skills/story-long-analyze/references/index-rebuild.md +11 -0
- package/lib/oh-story/skills/story-long-analyze/references/inspiration-library.md +105 -0
- package/lib/oh-story/skills/story-long-analyze/references/pipeline-ops.md +138 -64
- package/lib/oh-story/skills/story-long-analyze/references/semantic-acceptance-fixtures.md +84 -0
- package/lib/oh-story/skills/story-long-analyze/references/stage1-golden-chapters.md +128 -0
- package/lib/oh-story/skills/story-long-analyze/references/stage2-extraction.md +229 -0
- package/lib/oh-story/skills/story-long-analyze/references/stage3-plot-rhythm.md +480 -0
- package/lib/oh-story/skills/story-long-analyze/references/stage4-characters-settings.md +195 -0
- package/lib/oh-story/skills/story-long-analyze/references/stage5-report.md +33 -0
- package/lib/oh-story/skills/story-long-analyze/references/style-profile-generator.md +55 -128
- package/lib/oh-story/skills/story-long-analyze/references/style-profile-protocol.md +6 -6
- package/lib/oh-story/skills/story-long-analyze/references/synthesis-inputs.md +39 -0
- package/lib/oh-story/skills/story-long-analyze/scripts/build_chapter_index.py +466 -0
- package/lib/oh-story/skills/story-long-analyze/scripts/inspect_existing_assets.py +702 -0
- package/lib/oh-story/skills/story-long-analyze/scripts/inspiration_index.py +864 -0
- package/lib/oh-story/skills/story-long-analyze/scripts/manage_analysis_run.py +1145 -0
- package/lib/oh-story/skills/story-long-analyze/scripts/render_relation_chart.py +425 -0
- package/lib/oh-story/skills/story-long-scan/SKILL.md +45 -194
- package/lib/oh-story/skills/story-long-scan/references/platform-ciweimao.md +19 -0
- package/lib/oh-story/skills/story-long-scan/references/platform-fanqie.md +45 -0
- package/lib/oh-story/skills/story-long-scan/references/platform-jjwxc.md +28 -0
- package/lib/oh-story/skills/story-long-scan/references/platform-qidian.md +35 -0
- package/lib/oh-story/skills/story-long-scan/references/platform-qimao.md +27 -0
- package/lib/oh-story/skills/story-long-scan/references/topic-decision.md +6 -2
- package/lib/oh-story/skills/story-long-write/SKILL.md +49 -81
- package/lib/oh-story/skills/story-long-write/references/agent-calls.md +47 -0
- package/lib/oh-story/skills/story-long-write/references/anti-ai-writing.md +12 -11
- package/lib/oh-story/skills/story-long-write/references/artifact-protocols.md +24 -202
- package/lib/oh-story/skills/story-long-write/references/author-memory-maintenance.md +75 -0
- package/lib/oh-story/skills/story-long-write/references/author-memory.md +46 -94
- package/lib/oh-story/skills/story-long-write/references/banned-words.md +2 -2
- package/lib/oh-story/skills/story-long-write/references/benchmark-recall.md +7 -11
- package/lib/oh-story/skills/story-long-write/references/character-relations.md +49 -1
- package/lib/oh-story/skills/story-long-write/references/cross-book-recall.md +26 -0
- package/lib/oh-story/skills/story-long-write/references/deslop-gates.md +171 -0
- package/lib/oh-story/skills/story-long-write/references/dialogue-mastery.md +4 -0
- package/lib/oh-story/skills/story-long-write/references/emotion-on-page.md +116 -0
- package/lib/oh-story/skills/story-long-write/references/emotional-arc-design.md +1 -114
- package/lib/oh-story/skills/story-long-write/references/emotional-methods.md +2 -2
- package/lib/oh-story/skills/story-long-write/references/genre-prose-cards.md +1 -1
- package/lib/oh-story/skills/story-long-write/references/long-chapter-quality.md +7 -15
- package/lib/oh-story/skills/story-long-write/references/long-format.md +0 -8
- package/lib/oh-story/skills/story-long-write/references/long-genre-catalog.md +1 -1
- package/lib/oh-story/skills/story-long-write/references/long-genre-mechanics.md +3 -3
- package/lib/oh-story/skills/story-long-write/references/opening-design.md +2 -2
- package/lib/oh-story/skills/story-long-write/references/outline-conflict.md +4 -4
- package/lib/oh-story/skills/story-long-write/references/outline-methods.md +7 -7
- package/lib/oh-story/skills/story-long-write/references/outline-rhythm.md +3 -3
- package/lib/oh-story/skills/story-long-write/references/outline-structure-theory.md +14 -69
- package/lib/oh-story/skills/story-long-write/references/plot-frameworks.md +1 -1
- package/lib/oh-story/skills/story-long-write/references/plot-special-topics.md +2 -2
- package/lib/oh-story/skills/story-long-write/references/project-files.md +19 -13
- package/lib/oh-story/skills/story-long-write/references/reader-contract-and-progression.md +13 -5
- package/lib/oh-story/skills/story-long-write/references/reference-index.md +5 -6
- package/lib/oh-story/skills/story-long-write/references/stop-and-fix.md +9 -0
- package/lib/oh-story/skills/story-long-write/references/style-combat-face.md +1 -58
- package/lib/oh-story/skills/story-long-write/references/style-genre-modules.md +1 -1
- package/lib/oh-story/skills/story-long-write/references/style-resolution.md +4 -4
- package/lib/oh-story/skills/story-long-write/references/tracking-initialization.md +1 -1
- package/lib/oh-story/skills/story-long-write/references/tracking-transaction.md +15 -11
- package/lib/oh-story/skills/story-long-write/references/workflow-chapter.md +81 -121
- package/lib/oh-story/skills/story-long-write/references/workflow-daily.md +41 -139
- package/lib/oh-story/skills/story-long-write/references/workflow-outline.md +157 -0
- package/lib/oh-story/skills/story-long-write/references/workflow-revision.md +57 -67
- package/lib/oh-story/skills/story-long-write/references/workflow-setup.md +112 -253
- package/lib/oh-story/skills/story-long-write/references/workflow-volume.md +142 -0
- package/lib/oh-story/skills/story-long-write/references/writer-prompt-fallback.md +6 -4
- package/lib/oh-story/skills/story-long-write/references/writing-craft.md +19 -65
- package/lib/oh-story/skills/story-long-write/scripts/author_memory_commit.py +934 -170
- package/lib/oh-story/skills/story-long-write/scripts/build_architect_brief.py +260 -0
- package/lib/oh-story/skills/story-long-write/scripts/build_writer_prompt.py +226 -25
- package/lib/oh-story/skills/story-long-write/scripts/check-ai-patterns.js +155 -8
- package/lib/oh-story/skills/story-long-write/scripts/check-outline-contract.js +132 -27
- package/lib/oh-story/skills/story-long-write/scripts/check-outline-copy.js +7 -2
- package/lib/oh-story/skills/story-long-write/scripts/outline_view.py +7 -4
- package/lib/oh-story/skills/story-long-write/scripts/storyctl.py +304 -46
- package/lib/oh-story/skills/story-long-write/scripts/tracking_commit.py +478 -15
- package/lib/oh-story/skills/story-long-write/scripts/wordcount_core.py +182 -23
- package/lib/oh-story/skills/story-review/SKILL.md +31 -302
- package/lib/oh-story/skills/story-review/references/agent-prompts.md +184 -0
- package/lib/oh-story/skills/story-review/references/anti-ai-writing.md +12 -11
- package/lib/oh-story/skills/story-review/references/author-memory-maintenance.md +75 -0
- package/lib/oh-story/skills/story-review/references/author-memory.md +46 -94
- package/lib/oh-story/skills/story-review/references/banned-words.md +2 -2
- package/lib/oh-story/skills/story-review/references/batch-review.md +19 -0
- package/lib/oh-story/skills/story-review/references/character-relations.md +49 -1
- package/lib/oh-story/skills/story-review/references/dialogue-mastery.md +4 -0
- package/lib/oh-story/skills/story-review/references/review-quality.md +6 -11
- package/lib/oh-story/skills/story-review/references/review-tracking.md +10 -0
- package/lib/oh-story/skills/story-review/references/solo.md +52 -0
- package/lib/oh-story/skills/story-review/references/style-resolution.md +4 -4
- package/lib/oh-story/skills/story-review/references/tracking-initialization.md +1 -1
- package/lib/oh-story/skills/story-review/references/tracking-transaction.md +15 -11
- package/lib/oh-story/skills/story-review/scripts/author_memory_commit.py +934 -170
- package/lib/oh-story/skills/story-review/scripts/check-ai-patterns.js +155 -8
- package/lib/oh-story/skills/story-review/scripts/tracking_commit.py +478 -15
- package/lib/oh-story/skills/story-review/scripts/wordcount_core.py +182 -23
- package/lib/oh-story/skills/story-setup/SKILL.md +71 -452
- package/lib/oh-story/skills/story-setup/references/agent-references/agent-quality.md +4 -4
- package/lib/oh-story/skills/story-setup/references/agent-references/anti-ai-writing.md +12 -11
- package/lib/oh-story/skills/story-setup/references/agent-references/banned-words.md +2 -2
- package/lib/oh-story/skills/story-setup/references/agent-references/benchmark-style-load.md +107 -0
- package/lib/oh-story/skills/story-setup/references/agent-references/character-relations.md +49 -1
- package/lib/oh-story/skills/story-setup/references/agent-references/deslop-gates.md +22 -5
- package/lib/oh-story/skills/story-setup/references/agent-references/dialogue-mastery.md +4 -0
- package/lib/oh-story/skills/story-setup/references/agent-references/emotion-on-page.md +116 -0
- package/lib/oh-story/skills/story-setup/references/agent-references/emotional-arc-design.md +1 -114
- package/lib/oh-story/skills/story-setup/references/agent-references/format-and-structure.md +1 -1
- package/lib/oh-story/skills/story-setup/references/agent-references/genre-prose-cards.md +1 -1
- package/lib/oh-story/skills/story-setup/references/agent-references/long-emotional-methods.md +2 -2
- package/lib/oh-story/skills/story-setup/references/agent-references/long-genre-catalog.md +1 -1
- package/lib/oh-story/skills/story-setup/references/agent-references/long-genre-mechanics.md +3 -3
- package/lib/oh-story/skills/story-setup/references/agent-references/long-quality.md +7 -5
- package/lib/oh-story/skills/story-setup/references/agent-references/opening-design.md +2 -2
- package/lib/oh-story/skills/story-setup/references/agent-references/outline-conflict.md +4 -4
- package/lib/oh-story/skills/story-setup/references/agent-references/outline-methods.md +7 -7
- package/lib/oh-story/skills/story-setup/references/agent-references/outline-rhythm.md +3 -3
- package/lib/oh-story/skills/story-setup/references/agent-references/short-emotional-methods.md +1 -1
- package/lib/oh-story/skills/story-setup/references/agent-references/short-genre-formulas.md +0 -15
- package/lib/oh-story/skills/story-setup/references/agent-references/short-reversal.md +1 -1
- package/lib/oh-story/skills/story-setup/references/agent-references/style-combat-face.md +1 -58
- package/lib/oh-story/skills/story-setup/references/agent-references/style-genre-modules.md +1 -1
- package/lib/oh-story/skills/story-setup/references/agent-references/style-resolution.md +4 -4
- package/lib/oh-story/skills/story-setup/references/agent-references/writing-craft.md +19 -65
- package/lib/oh-story/skills/story-setup/references/diagnostics.md +4 -4
- package/lib/oh-story/skills/story-short-analyze/SKILL.md +56 -110
- package/lib/oh-story/skills/story-short-analyze/references/analysis-character-relations.md +1 -1
- package/lib/oh-story/skills/story-short-analyze/references/analysis-method.md +42 -0
- package/lib/oh-story/skills/story-short-analyze/references/optional-modules.md +97 -0
- package/lib/oh-story/skills/story-short-analyze/references/output-contract.md +9 -47
- package/lib/oh-story/skills/story-short-analyze/references/quality-checklist.md +80 -0
- package/lib/oh-story/skills/story-short-analyze/references/source-story-quality.md +4 -4
- package/lib/oh-story/skills/story-short-analyze/references/stage2-3-structure-emotion.md +308 -0
- package/lib/oh-story/skills/story-short-analyze/references/stage4-6-reversal-summary.md +611 -0
- package/lib/oh-story/skills/story-short-scan/SKILL.md +35 -65
- package/lib/oh-story/skills/story-short-scan/references/manual-rank-input.md +36 -0
- package/lib/oh-story/skills/story-short-scan/references/platform-dianzhong.md +24 -0
- package/lib/oh-story/skills/story-short-scan/references/platform-heiyan.md +21 -0
- package/lib/oh-story/skills/story-short-scan/references/topic-match.md +38 -0
- package/lib/oh-story/skills/story-short-write/SKILL.md +23 -175
- package/lib/oh-story/skills/story-short-write/references/agent-calls.md +13 -0
- package/lib/oh-story/skills/story-short-write/references/author-memory-maintenance.md +75 -0
- package/lib/oh-story/skills/story-short-write/references/author-memory.md +46 -94
- package/lib/oh-story/skills/story-short-write/references/banned-words.md +2 -2
- package/lib/oh-story/skills/story-short-write/references/benchmark-recall.md +1 -21
- package/lib/oh-story/skills/story-short-write/references/dialogue-mastery.md +4 -0
- package/lib/oh-story/skills/story-short-write/references/emotional-methods.md +1 -1
- package/lib/oh-story/skills/story-short-write/references/genre-writing-formulas.md +0 -15
- package/lib/oh-story/skills/story-short-write/references/output-contract.md +9 -47
- package/lib/oh-story/skills/story-short-write/references/reference-index.md +17 -47
- package/lib/oh-story/skills/story-short-write/references/scene-craft.md +40 -0
- package/lib/oh-story/skills/story-short-write/references/short-craft.md +7 -43
- package/lib/oh-story/skills/story-short-write/references/short-format.md +3 -3
- package/lib/oh-story/skills/story-short-write/references/short-prose-quality.md +2 -1
- package/lib/oh-story/skills/story-short-write/references/short-reversal.md +1 -1
- package/lib/oh-story/skills/story-short-write/references/style-resolution.md +4 -4
- package/lib/oh-story/skills/story-short-write/references/submission-craft.md +1 -1
- package/lib/oh-story/skills/story-short-write/references/villain-and-reveal.md +5 -10
- package/lib/oh-story/skills/story-short-write/references/workflow-design.md +21 -13
- package/lib/oh-story/skills/story-short-write/references/workflow-draft.md +116 -0
- package/lib/oh-story/skills/story-short-write/references/workflow-revision.md +99 -0
- package/lib/oh-story/skills/story-short-write/references/writing-workflow.md +7 -119
- package/lib/oh-story/skills/story-short-write/scripts/author_memory_commit.py +934 -170
- package/lib/oh-story/skills/story-short-write/scripts/check-ai-patterns.js +155 -8
- package/lib/oh-story/skills/story-short-write/scripts/check-outline-copy.js +7 -2
- package/lib/types/client/drama-production-view.d.ts.map +1 -1
- package/lib/types/client/file-activity.d.ts +19 -1
- package/lib/types/client/file-activity.d.ts.map +1 -1
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/production-prompts.d.ts +7 -0
- package/lib/types/client/production-prompts.d.ts.map +1 -1
- package/lib/types/client/production-runtime.d.ts +16 -0
- package/lib/types/client/production-runtime.d.ts.map +1 -1
- package/lib/types/client/video-studio.d.ts.map +1 -1
- package/lib/types/drama-adapters.d.ts +5 -3
- package/lib/types/drama-adapters.d.ts.map +1 -1
- package/lib/types/native-hooks.d.ts +53 -1
- package/lib/types/native-hooks.d.ts.map +1 -1
- package/lib/types/role-identity.d.ts +17 -0
- package/lib/types/role-identity.d.ts.map +1 -0
- package/lib/types/role-tool.d.ts.map +1 -1
- package/lib/types/skill-provider.d.ts.map +1 -1
- package/lib/types/video-project.d.ts.map +1 -1
- package/lib/types/workspace-route.d.ts.map +1 -1
- package/lib/video-recap/CHANGELOG.md +60 -1
- package/lib/video-recap/README.en.md +348 -137
- package/lib/video-recap/README.md +340 -136
- package/lib/video-recap/manifest.json +406 -251
- package/lib/video-recap/skills/video-assemble/SKILL.md +30 -15
- package/lib/video-recap/skills/video-assemble/references/audio-modes.md +63 -0
- package/lib/video-recap/skills/video-assemble/references/explicit-audio-mix.md +75 -0
- package/lib/video-recap/skills/video-assemble/references/foreground-compose.md +118 -0
- package/lib/video-recap/skills/video-assemble/references/narration-adoption.md +103 -0
- package/lib/video-recap/skills/video-assemble/references/packaging.md +45 -0
- package/lib/video-recap/skills/video-assemble/references/pair-media.md +74 -0
- package/lib/video-recap/skills/video-assemble/references/source-score.md +147 -0
- package/lib/video-recap/skills/video-assemble/references/subtitle-track.md +202 -0
- package/lib/video-recap/skills/video-assemble/scripts/adoption/__init__.py +1 -0
- package/lib/video-recap/skills/video-assemble/scripts/adoption/audio_mix_binding.py +279 -0
- package/lib/video-recap/skills/video-assemble/scripts/adoption/frozen_audio.py +187 -0
- package/lib/video-recap/skills/video-assemble/scripts/adoption/narration_binding.py +323 -0
- package/lib/video-recap/skills/video-assemble/scripts/adoption/strict_inputs.py +109 -0
- package/lib/video-recap/skills/video-assemble/scripts/adoption/strict_publish.py +130 -0
- package/lib/video-recap/skills/video-assemble/scripts/artifacts.py +10 -46
- package/lib/video-recap/skills/video-assemble/scripts/assemble.py +378 -119
- package/lib/video-recap/skills/video-assemble/scripts/assemble_constants.py +29 -12
- package/lib/video-recap/skills/video-assemble/scripts/assembly_contract.py +95 -44
- package/lib/video-recap/skills/video-assemble/scripts/assembly_settings.py +75 -32
- package/lib/video-recap/skills/video-assemble/scripts/audio_mix.py +11 -11
- package/lib/video-recap/skills/video-assemble/scripts/compose_foreground.py +293 -0
- package/lib/video-recap/skills/video-assemble/scripts/export_jianying.py +71 -78
- package/lib/video-recap/skills/video-assemble/scripts/jianying/__init__.py +1 -0
- package/lib/video-recap/skills/video-assemble/scripts/{jianying_builders.py → jianying/builders.py} +39 -67
- package/lib/video-recap/skills/video-assemble/scripts/{jianying_model.py → jianying/model.py} +3 -11
- package/lib/video-recap/skills/video-assemble/scripts/{jianying_optional.py → jianying/optional.py} +1 -1
- package/lib/video-recap/skills/video-assemble/scripts/jianying/schema.py +53 -0
- package/lib/video-recap/skills/video-assemble/scripts/{jianying_templates.py → jianying/templates.py} +1 -1
- package/lib/video-recap/skills/video-assemble/scripts/{jianying_timeline_contract.py → jianying/timeline_contract.py} +34 -13
- package/lib/video-recap/skills/video-assemble/scripts/{jianying_writer.py → jianying/writer.py} +7 -17
- package/lib/video-recap/skills/video-assemble/scripts/lib.py +37 -4
- package/lib/video-recap/skills/video-assemble/scripts/media.py +12 -21
- package/lib/video-recap/skills/video-assemble/scripts/narration_audio.py +64 -34
- package/lib/video-recap/skills/video-assemble/scripts/packaging.py +122 -0
- package/lib/video-recap/skills/video-assemble/scripts/pair_media.py +216 -0
- package/lib/video-recap/skills/video-assemble/scripts/render_preflight.py +1 -3
- package/lib/video-recap/skills/video-assemble/scripts/source_score.py +469 -0
- package/lib/video-recap/skills/video-assemble/scripts/source_subtitles.py +25 -9
- package/lib/video-recap/skills/video-assemble/scripts/subtitles/__init__.py +1 -0
- package/lib/video-recap/skills/video-assemble/scripts/{subtitle_core.py → subtitles/core.py} +13 -6
- package/lib/video-recap/skills/video-assemble/scripts/{subtitle_render.py → subtitles/render.py} +5 -4
- package/lib/video-recap/skills/video-assemble/scripts/subtitles/track.py +324 -0
- package/lib/video-recap/skills/video-assemble/scripts/subtitles/track_binding.py +200 -0
- package/lib/video-recap/skills/video-assemble/scripts/timeline.py +30 -46
- package/lib/video-recap/skills/video-assemble/scripts/timeline_emit.py +91 -7
- package/lib/video-recap/skills/video-assemble/scripts/visual_render.py +22 -8
- package/lib/video-recap/skills/video-cut/SKILL.md +34 -16
- package/lib/video-recap/skills/video-cut/references/shot-review.md +50 -0
- package/lib/video-recap/skills/video-cut/scripts/cut.py +2 -28
- package/lib/video-recap/skills/video-cut/scripts/cut_cli.py +58 -62
- package/lib/video-recap/skills/video-cut/scripts/cut_contract.py +21 -106
- package/lib/video-recap/skills/video-cut/scripts/cut_render.py +14 -9
- package/lib/video-recap/skills/video-cut/scripts/lib.py +39 -0
- package/lib/video-recap/skills/video-cut/scripts/narration_mapping.py +2 -155
- package/lib/video-recap/skills/video-cut/scripts/narrative_selection.py +244 -0
- package/lib/video-recap/skills/video-cut/scripts/shot_review.py +357 -0
- package/lib/video-recap/skills/video-recap/SKILL.md +86 -42
- package/lib/video-recap/skills/video-recap/references/audio-routing.md +126 -0
- package/lib/video-recap/skills/video-recap/references/config-playbook.md +12 -8
- package/lib/video-recap/skills/video-recap/references/data-schema.md +44 -33
- package/lib/video-recap/skills/video-recap/references/resource-library.md +151 -0
- package/lib/video-recap/skills/video-recap/references/shift-left-qc-schema.md +1 -1
- package/lib/video-recap/skills/video-recap/scripts/doctor.py +123 -30
- package/lib/video-recap/skills/video-recap/scripts/final_qc.py +22 -75
- package/lib/video-recap/skills/video-recap/scripts/lib.py +3 -3
- package/lib/video-recap/skills/video-recap/scripts/library.py +509 -0
- package/lib/video-recap/skills/video-recap/scripts/materials.py +63 -91
- package/lib/video-recap/skills/video-recap/scripts/mimo_qc.py +8 -70
- package/lib/video-recap/skills/video-recap/scripts/project_binding.py +286 -0
- package/lib/video-recap/skills/video-recap/scripts/qc/__init__.py +1 -0
- package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_client.py → qc/mimo_client.py} +1 -1
- package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_evidence.py → qc/mimo_evidence.py} +33 -66
- package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_observations.py → qc/mimo_observations.py} +3 -11
- package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_payload.py → qc/mimo_payload.py} +4 -7
- package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_report.py → qc/mimo_report.py} +17 -28
- package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_runner.py → qc/mimo_runner.py} +2 -2
- package/lib/video-recap/skills/video-recap/scripts/qc_contract.py +0 -19
- package/lib/video-recap/skills/video-recap/scripts/recap_cli.py +119 -50
- package/lib/video-recap/skills/video-recap/scripts/recap_inspect.py +32 -114
- package/lib/video-recap/skills/video-recap/scripts/recap_review.py +9 -23
- package/lib/video-recap/skills/video-recap/scripts/recap_runner.py +387 -334
- package/lib/video-recap/skills/video-recap/scripts/recap_runtime.py +23 -23
- package/lib/video-recap/skills/video-recap/scripts/recap_source.py +278 -0
- package/lib/video-recap/skills/video-recap/scripts/recap_stage_qc.py +25 -12
- package/lib/video-recap/skills/video-recap/scripts/recap_timeline.py +59 -52
- package/lib/video-recap/skills/video-recap/scripts/resource_lock.py +162 -0
- package/lib/video-recap/skills/video-script/SKILL.md +16 -13
- package/lib/video-recap/skills/video-script/references/creative-editing-playbook.md +9 -5
- package/lib/video-recap/skills/video-script/references/promotional-copy.md +68 -0
- package/lib/video-recap/skills/video-script/references/research-guide.md +1 -1
- package/lib/video-recap/skills/video-script/scripts/evidence_bundle.py +24 -76
- package/lib/video-recap/skills/video-script/scripts/lib.py +10 -87
- package/lib/video-recap/skills/video-script/scripts/narration_lint.py +63 -39
- package/lib/video-recap/skills/video-script/scripts/review.py +0 -2
- package/lib/video-recap/skills/video-script/scripts/review_grounding.py +35 -59
- package/lib/video-recap/skills/video-script/scripts/review_response.py +66 -128
- package/lib/video-recap/skills/video-script/scripts/review_runner.py +12 -20
- package/lib/video-recap/skills/video-script/scripts/speech_ownership.py +4 -4
- package/lib/video-recap/skills/video-script/scripts/validate.py +53 -42
- package/lib/video-recap/skills/video-understanding/SKILL.md +6 -4
- package/lib/video-recap/skills/video-understanding/references/data-schema.md +50 -156
- package/lib/video-recap/skills/video-understanding/references/research-guide.md +4 -4
- package/lib/video-recap/skills/video-understanding/scripts/asr.py +85 -65
- package/lib/video-recap/skills/video-understanding/scripts/asr_timing_evidence.py +270 -0
- package/lib/video-recap/skills/video-understanding/scripts/brief.py +2 -5
- package/lib/video-recap/skills/video-understanding/scripts/briefing/__init__.py +1 -0
- package/lib/video-recap/skills/{video-script/scripts/agent_brief.py → video-understanding/scripts/briefing/builder.py} +38 -35
- package/lib/video-recap/skills/video-understanding/scripts/{brief_context.py → briefing/context.py} +4 -35
- package/lib/video-recap/skills/{video-script/scripts/brief_inputs.py → video-understanding/scripts/briefing/inputs.py} +9 -31
- package/lib/video-recap/skills/{video-script/scripts/brief_timeline.py → video-understanding/scripts/briefing/timeline.py} +5 -6
- package/lib/video-recap/skills/video-understanding/scripts/consolidate.py +65 -116
- package/lib/video-recap/skills/video-understanding/scripts/detect.py +31 -40
- package/lib/video-recap/skills/video-understanding/scripts/lib.py +30 -86
- package/lib/video-recap/skills/video-understanding/scripts/narration_lint.py +63 -39
- package/lib/video-recap/skills/video-understanding/scripts/speech_ownership.py +4 -4
- package/lib/video-recap/skills/video-understanding/scripts/storyboard.py +17 -79
- package/lib/video-recap/skills/video-understanding/scripts/understanding_brief.py +14 -40
- package/lib/video-recap/skills/video-understanding/scripts/understanding_cache.py +64 -46
- package/lib/video-recap/skills/video-understanding/scripts/understanding_runner.py +31 -11
- package/lib/video-recap/skills/video-understanding/scripts/understanding_storyboard.py +19 -14
- package/lib/video-recap/skills/video-understanding/scripts/vlm.py +80 -108
- package/lib/video-recap/skills/video-voiceover/SKILL.md +29 -19
- package/lib/video-recap/skills/video-voiceover/references/index-tts.md +31 -0
- package/lib/video-recap/skills/video-voiceover/scripts/approved_text_policy.py +123 -0
- package/lib/video-recap/skills/video-voiceover/scripts/dub.py +51 -59
- package/lib/video-recap/skills/video-voiceover/scripts/lib.py +8 -44
- package/lib/video-recap/skills/video-voiceover/scripts/providers/__init__.py +1 -0
- package/lib/video-recap/skills/video-voiceover/scripts/providers/index_tts.py +184 -0
- package/lib/video-recap/skills/video-voiceover/scripts/voiceover.py +212 -134
- package/package.json +45 -45
- package/lib/novel-to-game/skills/game-world-design/references/dialogue-design-method.md +0 -58
- package/lib/novel-to-game/skills/game-world-design/references/game-writing-craft.md +0 -53
- package/lib/novel-to-game/skills/novel-to-game/references/intake-benchmark-reference.md +0 -38
- package/lib/oh-story/skills/story-long-analyze/references/material-decomposition.md +0 -530
- package/lib/oh-story/skills/story-long-analyze/references/output-templates.md +0 -559
- package/lib/oh-story/skills/story-long-scan/references/scan-output-format.md +0 -207
- package/lib/oh-story/skills/story-long-write/references/state-tracking.md +0 -108
- package/lib/oh-story/skills/story-short-analyze/references/material-decomposition.md +0 -678
- package/lib/oh-story/skills/story-short-analyze/references/output-templates.md +0 -561
- package/lib/video-recap/skills/video-assemble/scripts/jianying_schema.py +0 -98
- package/lib/video-recap/skills/video-recap/references/creative-editing-playbook.md +0 -225
- package/lib/video-recap/skills/video-recap/references/research-guide.md +0 -74
- package/lib/video-recap/skills/video-recap/references/timeline-and-jianying.md +0 -250
- package/lib/video-recap/skills/video-script/scripts/brief_context.py +0 -348
- package/lib/video-recap/skills/video-script/scripts/narration.py +0 -12
- package/lib/video-recap/skills/video-understanding/scripts/agent_brief.py +0 -399
- package/lib/video-recap/skills/video-understanding/scripts/brief_inputs.py +0 -290
- package/lib/video-recap/skills/video-understanding/scripts/brief_timeline.py +0 -327
- /package/lib/video-recap/skills/video-assemble/scripts/{jianying_tracks.py → jianying/tracks.py} +0 -0
- /package/lib/video-recap/skills/video-recap/scripts/{mimo_qc_contract.py → qc/mimo_contract.py} +0 -0
- /package/lib/video-recap/skills/video-voiceover/scripts/{fish_audio.py → providers/fish_audio.py} +0 -0
package/lib/video-recap/skills/video-assemble/scripts/{jianying_builders.py → jianying/builders.py}
RENAMED
|
@@ -4,9 +4,9 @@ import json
|
|
|
4
4
|
import os
|
|
5
5
|
from copy import deepcopy
|
|
6
6
|
|
|
7
|
-
from
|
|
8
|
-
from
|
|
9
|
-
from
|
|
7
|
+
from jianying.schema import scrub_platform_identity, us
|
|
8
|
+
from jianying.templates import template
|
|
9
|
+
from jianying.tracks import SEGMENT_RENDER_INDEX
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
def timerange(start_us, dur_us):
|
|
@@ -83,9 +83,10 @@ def windowed_volume_keyframes(keyframes, seg_start_s, seg_end_s, default_gain, n
|
|
|
83
83
|
|
|
84
84
|
|
|
85
85
|
def clip_from_segment(segment):
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
86
|
+
"""Map optional authoring transforms (validated as objects by the timeline contract)."""
|
|
87
|
+
scale = segment.get("scale", {})
|
|
88
|
+
position = segment.get("position", {})
|
|
89
|
+
flip = segment.get("flip", {})
|
|
89
90
|
return {
|
|
90
91
|
"alpha": round(float(segment.get("opacity", 1.0)), 4),
|
|
91
92
|
"flip": {
|
|
@@ -220,13 +221,6 @@ def rich_text_content(text, base_style=None, words=None, style_presets=None):
|
|
|
220
221
|
return content
|
|
221
222
|
|
|
222
223
|
|
|
223
|
-
def unsupported_track_note(kind):
|
|
224
|
-
info = validate_material_category(kind)
|
|
225
|
-
if info["supported"]:
|
|
226
|
-
return None
|
|
227
|
-
return info.get("note")
|
|
228
|
-
|
|
229
|
-
|
|
230
224
|
RESOURCE_TRACKS = {
|
|
231
225
|
"sound": "audios",
|
|
232
226
|
"sticker": "stickers",
|
|
@@ -237,17 +231,12 @@ RESOURCE_TRACKS = {
|
|
|
237
231
|
|
|
238
232
|
|
|
239
233
|
def _resource_material(segment, kind, new_id):
|
|
240
|
-
|
|
234
|
+
"""The contract guarantees exactly one of material / resource_config (resolved package)."""
|
|
241
235
|
config = segment.get("resource_config")
|
|
242
|
-
if
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
raise ValueError(f"{kind} resource_config must be an object")
|
|
247
|
-
raw = config.get("main_config")
|
|
248
|
-
if not isinstance(raw, dict):
|
|
249
|
-
raise ValueError(f"{kind} resource_config.main_config must be an object")
|
|
250
|
-
raw = deepcopy(raw)
|
|
236
|
+
if config is None:
|
|
237
|
+
raw = deepcopy(segment["material"])
|
|
238
|
+
else:
|
|
239
|
+
raw = deepcopy(config["main_config"])
|
|
251
240
|
resource_id = config.get("resource_id")
|
|
252
241
|
if resource_id is not None:
|
|
253
242
|
raw.setdefault("resource_id", resource_id)
|
|
@@ -257,10 +246,6 @@ def _resource_material(segment, kind, new_id):
|
|
|
257
246
|
if kind == "sticker" and cover_img:
|
|
258
247
|
raw["icon_url"] = cover_img
|
|
259
248
|
raw["preview_cover_url"] = cover_img
|
|
260
|
-
elif not isinstance(raw, dict):
|
|
261
|
-
raise ValueError(f"{kind} segment requires material or resource_config")
|
|
262
|
-
else:
|
|
263
|
-
raw = deepcopy(raw)
|
|
264
249
|
raw["id"] = new_id()
|
|
265
250
|
return raw
|
|
266
251
|
|
|
@@ -269,7 +254,7 @@ def build_resource_track(ctx, timeline_track):
|
|
|
269
254
|
kind = timeline_track["kind"]
|
|
270
255
|
materials_key = RESOURCE_TRACKS[kind]
|
|
271
256
|
track_name = timeline_track.get("name", kind)
|
|
272
|
-
for item in timeline_track
|
|
257
|
+
for item in timeline_track["segments"]:
|
|
273
258
|
ts, te = float(item["timeline_start"]), float(item["timeline_end"])
|
|
274
259
|
duration_us = us(te - ts)
|
|
275
260
|
authored_item = item
|
|
@@ -295,8 +280,6 @@ def build_resource_track(ctx, timeline_track):
|
|
|
295
280
|
ctx.materials["effects"].extend(subordinate_effects)
|
|
296
281
|
seg = base_segment(material["id"], us(ts), duration_us, 1.0, [], ctx.new_id)
|
|
297
282
|
speed_value = float(item.get("speed", 1.0))
|
|
298
|
-
if speed_value <= 0:
|
|
299
|
-
raise ValueError(f"{kind} segment speed must be greater than 0")
|
|
300
283
|
seg["source_timerange"] = timerange(0, round(duration_us * speed_value))
|
|
301
284
|
seg["track_render_index"] = 0
|
|
302
285
|
seg["extra_material_refs"] = []
|
|
@@ -310,8 +293,6 @@ def build_resource_track(ctx, timeline_track):
|
|
|
310
293
|
|
|
311
294
|
|
|
312
295
|
def _attachment_material(spec, kind, new_id):
|
|
313
|
-
if not isinstance(spec, dict):
|
|
314
|
-
raise ValueError(f"video {kind} must be an object")
|
|
315
296
|
material = deepcopy(spec)
|
|
316
297
|
config = material.pop("main_config", None)
|
|
317
298
|
resources = material.pop("resources", None)
|
|
@@ -400,18 +381,14 @@ def _track_object(ctx, name, track_type, segments, flag):
|
|
|
400
381
|
|
|
401
382
|
def build_compound_video(ctx, clip, material, foreground_segment, track_name, ts, te):
|
|
402
383
|
"""Build duo-video's nested green-screen/compound draft structure."""
|
|
403
|
-
background_spec = clip
|
|
404
|
-
chroma_spec = _resolve_attachment_spec(ctx, clip
|
|
405
|
-
if not isinstance(background_spec, dict) or not isinstance(chroma_spec, dict):
|
|
406
|
-
raise ValueError("compound green-screen clips require green_background and chroma objects")
|
|
384
|
+
background_spec = clip["green_background"]
|
|
385
|
+
chroma_spec = _resolve_attachment_spec(ctx, clip["chroma"], "chroma")
|
|
407
386
|
|
|
408
387
|
duration_us = us(te - ts)
|
|
409
|
-
full_material_duration_us =
|
|
388
|
+
full_material_duration_us = material["duration"]
|
|
410
389
|
outer_source_timerange = deepcopy(foreground_segment["source_timerange"])
|
|
411
390
|
outer_target_timerange = deepcopy(foreground_segment["target_timerange"])
|
|
412
|
-
background_path = background_spec
|
|
413
|
-
if not isinstance(background_path, str) or not background_path:
|
|
414
|
-
raise ValueError("green_background.source_path must be a non-empty string")
|
|
391
|
+
background_path = background_spec["source_path"]
|
|
415
392
|
|
|
416
393
|
background_id = ctx.new_id()
|
|
417
394
|
background = template("video")
|
|
@@ -495,20 +472,21 @@ def build_compound_video(ctx, clip, material, foreground_segment, track_name, ts
|
|
|
495
472
|
|
|
496
473
|
def build_video_track(ctx, timeline_track):
|
|
497
474
|
track_name = timeline_track.get("name", "video")
|
|
498
|
-
for clip in timeline_track
|
|
475
|
+
for clip in timeline_track["clips"]:
|
|
499
476
|
ts, te = float(clip["timeline_start"]), float(clip["timeline_end"])
|
|
500
477
|
ss, se = float(clip["source_start"]), float(clip["source_end"])
|
|
501
478
|
path = clip["source_path"]
|
|
502
479
|
speed_value = float(clip.get("speed", 1.0))
|
|
503
|
-
if speed_value <= 0:
|
|
504
|
-
raise ValueError("video clip speed must be greater than 0")
|
|
505
480
|
reverse = bool(clip.get("reverse", False))
|
|
506
481
|
if reverse:
|
|
507
|
-
reverse_path
|
|
508
|
-
|
|
482
|
+
# The contract allows omitting reverse_path so export_timeline_to_jianying
|
|
483
|
+
# can generate it; building directly from such a clip is an authoring error.
|
|
484
|
+
if "reverse_path" not in clip:
|
|
509
485
|
raise ValueError("reverse video clips require a local reverse_path")
|
|
510
|
-
path = reverse_path
|
|
511
|
-
src_dur_us, width, height = ctx.
|
|
486
|
+
path = clip["reverse_path"]
|
|
487
|
+
src_dur_us, width, height = ctx.probe(path)
|
|
488
|
+
if src_dur_us <= 0:
|
|
489
|
+
raise ValueError(f"JianYing video source has no probed duration: {path}")
|
|
512
490
|
mat_id = ctx.new_id()
|
|
513
491
|
material = template("video")
|
|
514
492
|
material.update({
|
|
@@ -546,17 +524,17 @@ def build_video_track(ctx, timeline_track):
|
|
|
546
524
|
def build_audio_track(ctx, timeline_track):
|
|
547
525
|
role = timeline_track.get("role", timeline_track.get("name", "audio"))
|
|
548
526
|
track_name = timeline_track.get("name", role)
|
|
549
|
-
for segment in timeline_track
|
|
527
|
+
for segment in timeline_track["segments"]:
|
|
550
528
|
ts, te = float(segment["timeline_start"]), float(segment["timeline_end"])
|
|
551
529
|
path = segment["source_path"]
|
|
552
|
-
mat_dur_us, _width, _height = ctx.
|
|
530
|
+
mat_dur_us, _width, _height = ctx.probe(path)
|
|
531
|
+
if mat_dur_us <= 0:
|
|
532
|
+
raise ValueError(f"JianYing audio source has no probed duration: {path}")
|
|
553
533
|
want_us = us(te - ts)
|
|
554
534
|
speed_value = float(segment.get("speed", 1.0))
|
|
555
|
-
if speed_value <= 0:
|
|
556
|
-
raise ValueError("audio segment speed must be greater than 0")
|
|
557
535
|
place_us = want_us
|
|
558
536
|
required_source_us = int(round(want_us * speed_value))
|
|
559
|
-
if
|
|
537
|
+
if required_source_us > mat_dur_us:
|
|
560
538
|
if role == "bgm" and timeline_track.get("loop"):
|
|
561
539
|
place_us = want_us
|
|
562
540
|
else:
|
|
@@ -569,14 +547,14 @@ def build_audio_track(ctx, timeline_track):
|
|
|
569
547
|
mat_id = ctx.new_id()
|
|
570
548
|
material = template("audio")
|
|
571
549
|
material.update({
|
|
572
|
-
"duration": int(mat_dur_us
|
|
550
|
+
"duration": int(mat_dur_us),
|
|
573
551
|
"id": mat_id,
|
|
574
552
|
"path": path,
|
|
575
553
|
})
|
|
576
554
|
ctx.materials["audios"].append(material)
|
|
577
555
|
keyframes = volume_keyframes(segment.get("volume_keyframes"), ts, ctx.new_id)
|
|
578
556
|
volume = segment.get("gain", 1.0) if not keyframes else 1.0
|
|
579
|
-
if role == "bgm" and timeline_track.get("loop") and
|
|
557
|
+
if role == "bgm" and timeline_track.get("loop") and required_source_us > mat_dur_us:
|
|
580
558
|
cursor = 0
|
|
581
559
|
while cursor < want_us:
|
|
582
560
|
piece = min(int(mat_dur_us / speed_value), want_us - cursor)
|
|
@@ -613,9 +591,9 @@ def build_audio_track(ctx, timeline_track):
|
|
|
613
591
|
def build_text_track(ctx, timeline_track):
|
|
614
592
|
track_name = timeline_track.get("name", "text")
|
|
615
593
|
track_kind = "subtitle" if track_name == "subtitle" else "text"
|
|
616
|
-
for segment in timeline_track
|
|
594
|
+
for segment in timeline_track["segments"]:
|
|
617
595
|
ts, te = float(segment["timeline_start"]), float(segment["timeline_end"])
|
|
618
|
-
text = segment
|
|
596
|
+
text = segment["text"]
|
|
619
597
|
mat_id = ctx.new_id()
|
|
620
598
|
authored_style = dict(ctx.style_presets.get(str(segment.get("style_id")), {}))
|
|
621
599
|
authored_style.update(segment.get("style") or {})
|
|
@@ -691,11 +669,11 @@ def build_text_track(ctx, timeline_track):
|
|
|
691
669
|
def build_image_track(ctx, timeline_track):
|
|
692
670
|
"""Build local image overlays as JianYing photo materials on video tracks."""
|
|
693
671
|
track_name = timeline_track.get("name", "image")
|
|
694
|
-
for segment in timeline_track
|
|
672
|
+
for segment in timeline_track["segments"]:
|
|
695
673
|
ts, te = float(segment["timeline_start"]), float(segment["timeline_end"])
|
|
696
674
|
duration_us = us(te - ts)
|
|
697
675
|
path = segment["source_path"]
|
|
698
|
-
|
|
676
|
+
_still_image_duration, width, height = ctx.probe(path)
|
|
699
677
|
mat_id = ctx.new_id()
|
|
700
678
|
material = template("video")
|
|
701
679
|
material.update({
|
|
@@ -710,8 +688,6 @@ def build_image_track(ctx, timeline_track):
|
|
|
710
688
|
ctx.materials["videos"].append(material)
|
|
711
689
|
seg = base_segment(mat_id, us(ts), duration_us, 1.0, [], ctx.new_id)
|
|
712
690
|
speed_value = float(segment.get("speed", 1.0))
|
|
713
|
-
if speed_value <= 0:
|
|
714
|
-
raise ValueError("image segment speed must be greater than 0")
|
|
715
691
|
seg["source_timerange"] = timerange(0, round(duration_us * speed_value))
|
|
716
692
|
seg["clip"] = clip_from_segment(segment)
|
|
717
693
|
seg["speed"] = speed_value
|
|
@@ -727,8 +703,8 @@ def build_image_track(ctx, timeline_track):
|
|
|
727
703
|
|
|
728
704
|
|
|
729
705
|
def build_timeline_track(ctx, timeline_track):
|
|
730
|
-
kind = timeline_track
|
|
731
|
-
if kind in ("audio", "text") and not timeline_track
|
|
706
|
+
kind = timeline_track["kind"]
|
|
707
|
+
if kind in ("audio", "text") and not timeline_track["segments"]:
|
|
732
708
|
return
|
|
733
709
|
if kind == "video":
|
|
734
710
|
build_video_track(ctx, timeline_track)
|
|
@@ -738,9 +714,5 @@ def build_timeline_track(ctx, timeline_track):
|
|
|
738
714
|
build_text_track(ctx, timeline_track)
|
|
739
715
|
elif kind == "image":
|
|
740
716
|
build_image_track(ctx, timeline_track)
|
|
741
|
-
|
|
717
|
+
else: # normalize_timeline admits only RESOURCE_TRACKS kinds past this point
|
|
742
718
|
build_resource_track(ctx, timeline_track)
|
|
743
|
-
else:
|
|
744
|
-
note = unsupported_track_note(kind)
|
|
745
|
-
if note:
|
|
746
|
-
ctx.note(note)
|
package/lib/video-recap/skills/video-assemble/scripts/{jianying_model.py → jianying/model.py}
RENAMED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
"""Thin internal model used at the JianYing adapter boundary."""
|
|
2
2
|
|
|
3
|
-
import os
|
|
4
3
|
from dataclasses import dataclass, field
|
|
5
4
|
from collections.abc import Callable
|
|
6
5
|
|
|
7
|
-
from
|
|
8
|
-
from
|
|
6
|
+
from jianying.schema import us
|
|
7
|
+
from jianying.tracks import TrackAllocator
|
|
9
8
|
|
|
10
9
|
|
|
11
10
|
ProbeFn = Callable[[str], tuple[int, int, int]]
|
|
@@ -50,7 +49,7 @@ class DraftBuildContext:
|
|
|
50
49
|
|
|
51
50
|
@classmethod
|
|
52
51
|
def from_timeline(cls, timeline, new_id, probe):
|
|
53
|
-
"""Build from a timeline already validated by
|
|
52
|
+
"""Build from a timeline already validated by jianying.timeline_contract."""
|
|
54
53
|
canvas = timeline["canvas"]
|
|
55
54
|
return cls(
|
|
56
55
|
width=canvas["width"],
|
|
@@ -63,13 +62,6 @@ class DraftBuildContext:
|
|
|
63
62
|
style_presets=timeline.get("style_presets", {}),
|
|
64
63
|
)
|
|
65
64
|
|
|
66
|
-
def media_duration(self, path, fallback_us):
|
|
67
|
-
"""(duration_us, width, height) probed from the file; images and missing files fall back."""
|
|
68
|
-
if os.path.exists(path):
|
|
69
|
-
duration_us, width, height = self.probe(path)
|
|
70
|
-
return duration_us or fallback_us, width, height
|
|
71
|
-
return fallback_us, 0, 0
|
|
72
|
-
|
|
73
65
|
def add_segment(self, kind, base_name, start_us, duration_us, segment):
|
|
74
66
|
allocated = self.track_allocator.allocate(kind, base_name, start_us, duration_us)
|
|
75
67
|
track = next(
|
package/lib/video-recap/skills/video-assemble/scripts/{jianying_optional.py → jianying/optional.py}
RENAMED
|
@@ -4,7 +4,7 @@ from pathlib import Path
|
|
|
4
4
|
|
|
5
5
|
from lib import CONFIG, log
|
|
6
6
|
|
|
7
|
-
def
|
|
7
|
+
def maybe_export_jianying(work_dir, out_dir, stem):
|
|
8
8
|
"""Lazy-import the optional 剪映 exporter and write a draft from timeline.json.
|
|
9
9
|
|
|
10
10
|
The export is a documented fail-open sidecar: any failure is logged and never
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""Schema constants and skeleton factories for JianYing export.
|
|
2
|
+
|
|
3
|
+
This module is intentionally data-oriented: it owns draft version metadata and
|
|
4
|
+
the full `materials` parallel-array shape.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from jianying.templates import template
|
|
8
|
+
|
|
9
|
+
# The full 剪映 materials object: ~45 parallel arrays. Only arrays backed by a
|
|
10
|
+
# production builder are populated; retaining the full shape preserves compatibility.
|
|
11
|
+
MATERIAL_KEYS = ["ai_translates", "audio_balances", "audio_effects", "audio_fades", "audio_track_indexes", "audios", "beats", "canvases", "chromas", "color_curves", "digital_humans", "drafts", "effects", "flowers", "green_screens", "handwrites", "hsl", "images", "log_color_wheels", "loudnesses", "manual_deformations", "masks", "common_mask", "material_animations", "material_colors", "multi_language_refs", "placeholders", "plugin_effects", "primary_color_wheels", "realtime_denoises", "shapes", "smart_crops", "smart_relights", "sound_channel_mappings", "speeds", "stickers", "tail_leaders", "text_templates", "texts", "time_marks", "transitions", "video_effects", "video_trackings", "videos", "vocal_beautifys", "vocal_separations"]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def us(seconds):
|
|
15
|
+
"""Seconds (float) -> integer microseconds. The single seconds->µs boundary."""
|
|
16
|
+
return int(round(float(seconds) * 1_000_000))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def full_materials(filled):
|
|
20
|
+
"""Return a complete JianYing `materials` object with all known arrays."""
|
|
21
|
+
out = {k: [] for k in MATERIAL_KEYS}
|
|
22
|
+
out.update(filled)
|
|
23
|
+
return out
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def scrub_platform_identity(project):
|
|
27
|
+
"""Remove hardware fingerprints carried by the pinned upstream templates."""
|
|
28
|
+
for platform_key in ("last_modified_platform", "platform"):
|
|
29
|
+
for identity_key in ("device_id", "hard_disk_id", "mac_address"):
|
|
30
|
+
project[platform_key][identity_key] = ""
|
|
31
|
+
return project
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def draft_content_skeleton(draft_id, width, height, fps, total_us, materials, tracks):
|
|
35
|
+
"""Build the root `draft_content.json` / `draft_info.json` skeleton."""
|
|
36
|
+
content = template("project")
|
|
37
|
+
scrub_platform_identity(content)
|
|
38
|
+
content["canvas_config"] = {"width": width, "height": height, "ratio": "original"}
|
|
39
|
+
content["duration"] = int(total_us)
|
|
40
|
+
content["fps"] = float(fps)
|
|
41
|
+
content["id"] = draft_id
|
|
42
|
+
content["materials"] = full_materials(materials)
|
|
43
|
+
content["tracks"] = tracks
|
|
44
|
+
return content
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def meta_info(draft_id, total_us):
|
|
48
|
+
"""Build the companion `draft_meta_info.json` skeleton."""
|
|
49
|
+
meta = template("meta")
|
|
50
|
+
meta["draft_id"] = draft_id
|
|
51
|
+
meta["draft_timeline_materials_size_"] = 0
|
|
52
|
+
meta["tm_duration"] = int(total_us)
|
|
53
|
+
return meta
|
|
@@ -6,7 +6,7 @@ from functools import lru_cache
|
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
_TEMPLATE_DIR = Path(__file__).resolve().parent.parent / "references" / "jianying"
|
|
9
|
+
_TEMPLATE_DIR = Path(__file__).resolve().parent.parent.parent / "references" / "jianying"
|
|
10
10
|
_TEMPLATE_FILES = {
|
|
11
11
|
"project": "empty_jy_project_info.json",
|
|
12
12
|
"video": "empty_jy_material_video.json",
|
|
@@ -45,6 +45,19 @@ def _validate_span(item, path, start_key="timeline_start", end_key="timeline_end
|
|
|
45
45
|
_error(f"{path}.{end_key}", f"must be greater than {start_key}")
|
|
46
46
|
|
|
47
47
|
|
|
48
|
+
def _validate_transform(item, path):
|
|
49
|
+
for field in ("scale", "position", "flip"):
|
|
50
|
+
if field in item and not isinstance(item[field], dict):
|
|
51
|
+
_error(f"{path}.{field}", "must be an object")
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _validate_resources(resources, path):
|
|
55
|
+
if not isinstance(resources, list) or any(not isinstance(item, dict) for item in resources):
|
|
56
|
+
_error(path, "must contain source_path objects")
|
|
57
|
+
for index, item in enumerate(resources):
|
|
58
|
+
_require_string(item, "source_path", f"{path}[{index}]")
|
|
59
|
+
|
|
60
|
+
|
|
48
61
|
def _validate_video_clip(clip, path):
|
|
49
62
|
if not isinstance(clip, dict):
|
|
50
63
|
_error(path, "must be an object")
|
|
@@ -68,18 +81,30 @@ def _validate_video_clip(clip, path):
|
|
|
68
81
|
)
|
|
69
82
|
if "reverse" in clip and not isinstance(clip["reverse"], bool):
|
|
70
83
|
_error(f"{path}.reverse", "must be a boolean")
|
|
71
|
-
|
|
84
|
+
# A reversed clip may omit reverse_path: export_timeline_to_jianying generates it.
|
|
85
|
+
if "reverse_path" in clip:
|
|
72
86
|
_require_string(clip, "reverse_path", path)
|
|
73
|
-
|
|
74
|
-
if field in clip and not isinstance(clip[field], dict):
|
|
75
|
-
_error(f"{path}.{field}", "must be an object")
|
|
87
|
+
_validate_transform(clip, path)
|
|
76
88
|
for field in ("transition", "mask", "lut", "chroma"):
|
|
77
|
-
if field in clip
|
|
89
|
+
if field not in clip:
|
|
90
|
+
continue
|
|
91
|
+
spec = clip[field]
|
|
92
|
+
if isinstance(spec, dict):
|
|
93
|
+
if "resources" in spec:
|
|
94
|
+
_validate_resources(spec["resources"], f"{path}.{field}.resources")
|
|
95
|
+
elif not isinstance(spec, str):
|
|
78
96
|
_error(f"{path}.{field}", "must be an object or resource-package name")
|
|
79
|
-
if "green_background" in clip and not isinstance(clip["green_background"], dict):
|
|
80
|
-
_error(f"{path}.green_background", "must be a local media object")
|
|
81
97
|
if "compound" in clip and not isinstance(clip["compound"], bool):
|
|
82
98
|
_error(f"{path}.compound", "must be a boolean")
|
|
99
|
+
if clip.get("compound") or "green_background" in clip or "chroma" in clip:
|
|
100
|
+
# Any one of these makes the clip a green-screen compound, which needs both.
|
|
101
|
+
background = clip.get("green_background")
|
|
102
|
+
if not isinstance(background, dict):
|
|
103
|
+
_error(f"{path}.green_background", "must be a local media object")
|
|
104
|
+
_require_string(background, "source_path", f"{path}.green_background")
|
|
105
|
+
_validate_transform(background, f"{path}.green_background")
|
|
106
|
+
if "chroma" not in clip:
|
|
107
|
+
_error(f"{path}.chroma", "compound green-screen clips require a chroma object")
|
|
83
108
|
|
|
84
109
|
|
|
85
110
|
def _validate_resource_config(config, path):
|
|
@@ -87,18 +112,14 @@ def _validate_resource_config(config, path):
|
|
|
87
112
|
_error(path, "must be an object")
|
|
88
113
|
if not isinstance(config.get("main_config"), dict):
|
|
89
114
|
_error(f"{path}.main_config", "must be an object")
|
|
90
|
-
|
|
91
|
-
if not isinstance(resources, list) or any(
|
|
92
|
-
not isinstance(item, dict) or not isinstance(item.get("source_path"), str)
|
|
93
|
-
for item in resources
|
|
94
|
-
):
|
|
95
|
-
_error(f"{path}.resources", "must contain source_path objects")
|
|
115
|
+
_validate_resources(config.get("resources", []), f"{path}.resources")
|
|
96
116
|
|
|
97
117
|
|
|
98
118
|
def _validate_segment(segment, path, kind):
|
|
99
119
|
if not isinstance(segment, dict):
|
|
100
120
|
_error(path, "must be an object")
|
|
101
121
|
_validate_span(segment, path)
|
|
122
|
+
_validate_transform(segment, path)
|
|
102
123
|
if "speed" in segment:
|
|
103
124
|
speed = _require_number(segment, "speed", path)
|
|
104
125
|
if speed <= 0:
|
package/lib/video-recap/skills/video-assemble/scripts/{jianying_writer.py → jianying/writer.py}
RENAMED
|
@@ -9,7 +9,7 @@ import time
|
|
|
9
9
|
import uuid
|
|
10
10
|
import zipfile
|
|
11
11
|
|
|
12
|
-
from
|
|
12
|
+
from jianying.templates import template
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
DRAFT_PATH_PLACEHOLDER = "##_draftpath_placeholder_0E685133-18CE-45ED-8CB8-2904A212EC80_##"
|
|
@@ -98,14 +98,10 @@ def _meta_value(material, relative_path, metetype, copied_path, timestamp_ms):
|
|
|
98
98
|
|
|
99
99
|
def _material_sets(content):
|
|
100
100
|
"""Yield root and nested compound-draft material dictionaries."""
|
|
101
|
-
materials = content
|
|
102
|
-
if not isinstance(materials, dict):
|
|
103
|
-
return
|
|
101
|
+
materials = content["materials"]
|
|
104
102
|
yield materials
|
|
105
|
-
for draft in materials
|
|
106
|
-
|
|
107
|
-
if isinstance(nested, dict):
|
|
108
|
-
yield from _material_sets(nested)
|
|
103
|
+
for draft in materials["drafts"]:
|
|
104
|
+
yield from _material_sets(draft["draft"])
|
|
109
105
|
|
|
110
106
|
|
|
111
107
|
def _replace_value(value, old, new):
|
|
@@ -184,13 +180,10 @@ def _is_packaged_path(value):
|
|
|
184
180
|
|
|
185
181
|
|
|
186
182
|
def _descriptor(raw, default_kind, *, required):
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
source = raw.get("source_path")
|
|
183
|
+
"""`raw` is a contract-validated resource entry: an object with a non-empty source_path."""
|
|
184
|
+
source = raw["source_path"]
|
|
190
185
|
resource_kind = raw.get("resource_kind", default_kind)
|
|
191
186
|
target_path = raw.get("target_path")
|
|
192
|
-
if not isinstance(source, str) or not source:
|
|
193
|
-
raise ValueError("JianYing resource source_path must be a non-empty string")
|
|
194
187
|
if not isinstance(resource_kind, str) or resource_kind not in {
|
|
195
188
|
"audio", "effect", "fonts", "image", "lut", "mask", "sticker",
|
|
196
189
|
"text", "text_template", "transition", "video",
|
|
@@ -321,11 +314,8 @@ def bundle_media(content, meta, draft_dir):
|
|
|
321
314
|
def strip_internal_resource_fields(content):
|
|
322
315
|
for materials in _material_sets(content):
|
|
323
316
|
for entries in materials.values():
|
|
324
|
-
if not isinstance(entries, list):
|
|
325
|
-
continue
|
|
326
317
|
for material in entries:
|
|
327
|
-
|
|
328
|
-
material.pop("_bundle_resources", None)
|
|
318
|
+
material.pop("_bundle_resources", None)
|
|
329
319
|
|
|
330
320
|
|
|
331
321
|
def draft_dir_has_user_content(draft_dir):
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
"""Self-contained config + utilities for this skill (no cross-skill imports)."""
|
|
2
|
+
import functools
|
|
2
3
|
import math
|
|
3
4
|
import os
|
|
5
|
+
import shutil
|
|
4
6
|
import subprocess
|
|
5
|
-
|
|
7
|
+
import tempfile
|
|
6
8
|
|
|
7
9
|
|
|
8
10
|
# ── 配置 ──────────────────────────────────────────────────────────────
|
|
@@ -53,7 +55,6 @@ _foreign_under_narration_volume = 0.05 # original volume under narration when s
|
|
|
53
55
|
|
|
54
56
|
CONFIG = {
|
|
55
57
|
"fade_ms": env_int("FADE_MS", 120, minimum=0), # 每段 TTS 淡入淡出(ms);过大会让紧凑的句子一顿一顿,120ms 防爆音又不发闷
|
|
56
|
-
"breath_ms": 250, # 段间呼吸空间(ms);block recap 块内连贯、块间留原声呼吸
|
|
57
58
|
"ducking_mode": "fixed", # fixed | sidechaincompress | none
|
|
58
59
|
"ducking_threshold": 0.15,
|
|
59
60
|
"ducking_ratio": 3,
|
|
@@ -73,6 +74,7 @@ CONFIG = {
|
|
|
73
74
|
"duck_bridge_seconds": env_float("DUCK_BRIDGE_SECONDS", 1.5, minimum=0.0), # 仅把间隔小于此值的相邻解说窗口并成一段压低;超过则视为作者特意留的"原声块",原声放回满音量。默认 1.5s:解说块内部连续压低,块与块之间的留白放出满音量原声。该值只控制短间隔合并,不设定旁白/原声配额。调大→更连续铺底、原声块更少;调小→更碎
|
|
74
75
|
"bgm_path": os.environ.get("BGM_PATH", "").strip(), # 背景音乐文件(可选),留空则不加 BGM
|
|
75
76
|
"source_video": os.environ.get("SOURCE_VIDEO", "").strip(), # 剪辑模式下的原始视频(可选),用于时间线/剪映导出引用原片片段
|
|
77
|
+
"source_video_explicit": False, # 仅 assemble.py --source-video 显式传入时为 True;环境变量 SOURCE_VIDEO 不算显式
|
|
76
78
|
"export_jianying": env_bool("EXPORT_JIANYING", False), # 渲染后可选导出剪映草稿(默认关;与核心解耦)
|
|
77
79
|
"jianying_draft_dir": os.environ.get("JIANYING_DRAFT_DIR", "").strip(), # 剪映草稿输出父目录(留空=work_dir)
|
|
78
80
|
"jianying_bundle_media": env_bool("JIANYING_BUNDLE_MEDIA", True), # 默认开:macOS 剪映沙箱读不到外部路径,须把素材拷进草稿目录
|
|
@@ -117,6 +119,8 @@ CONFIG = {
|
|
|
117
119
|
"target_lra": env_float("TARGET_LRA", 11.0), # 目标响度范围 (LU)
|
|
118
120
|
"final_limiter_peak": env_float("FINAL_LIMITER_PEAK", 0.98, minimum=0.1), # loudnorm 后峰值保护 limiter
|
|
119
121
|
"subtitle_font_name": os.environ.get("SUBTITLE_FONT_NAME", "Arial"),
|
|
122
|
+
# 可选字体文件:ASS 烧录经 fontsdir 加载,画面文字经 drawtext fontfile 使用;family 名仍由 SUBTITLE_FONT_NAME 指定
|
|
123
|
+
"subtitle_font_file": os.environ.get("SUBTITLE_FONT_FILE", "").strip(),
|
|
120
124
|
"subtitle_font_size": env_int("SUBTITLE_FONT_SIZE", 42, minimum=8),
|
|
121
125
|
"subtitle_primary_color": os.environ.get("SUBTITLE_PRIMARY_COLOR", "&H00FFFFFF"),
|
|
122
126
|
"subtitle_outline_color": os.environ.get("SUBTITLE_OUTLINE_COLOR", "&H00000000"),
|
|
@@ -136,8 +140,6 @@ if isinstance(_EXISTING_CONFIG_REF, dict):
|
|
|
136
140
|
_EXISTING_CONFIG_REF.update(CONFIG)
|
|
137
141
|
CONFIG = _EXISTING_CONFIG_REF
|
|
138
142
|
|
|
139
|
-
SCRIPT_DIR = Path(__file__).parent
|
|
140
|
-
|
|
141
143
|
def narration_tempo_budget(tts_rate_offset=0.0):
|
|
142
144
|
"""Return the canonical tempo budget shared by voiceover and assemble.
|
|
143
145
|
|
|
@@ -175,6 +177,37 @@ def run_cmd(cmd, **kwargs):
|
|
|
175
177
|
log(f"运行: {display}")
|
|
176
178
|
return subprocess.run(cmd, capture_output=True, text=True, **kwargs)
|
|
177
179
|
|
|
180
|
+
|
|
181
|
+
# ffmpeg 7 added `-/option path` to read any option's value from a file; ffmpeg 9 removed the
|
|
182
|
+
# older `-filter_complex_script` / `-filter_script` spellings, which are all ffmpeg <= 6 knows.
|
|
183
|
+
_LEGACY_FILTER_FILE_OPTIONS = {
|
|
184
|
+
"filter_complex": "-filter_complex_script",
|
|
185
|
+
"filter:v:0": "-filter_script:v:0",
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
@functools.lru_cache(maxsize=None)
|
|
190
|
+
def _ffmpeg_reads_option_files():
|
|
191
|
+
"""Whether the ffmpeg on PATH accepts `-/option path` (asked once per process)."""
|
|
192
|
+
if shutil.which("ffmpeg") is None:
|
|
193
|
+
return False
|
|
194
|
+
with tempfile.TemporaryDirectory() as tmp:
|
|
195
|
+
graph = os.path.join(tmp, "probe_filter.txt")
|
|
196
|
+
with open(graph, "w", encoding="utf-8") as fh:
|
|
197
|
+
fh.write("null")
|
|
198
|
+
result = subprocess.run(["ffmpeg", "-hide_banner", "-/filter_complex", graph],
|
|
199
|
+
stdin=subprocess.DEVNULL, capture_output=True, text=True,
|
|
200
|
+
timeout=20)
|
|
201
|
+
return "Unrecognized option" not in result.stderr
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def filter_file_args(option, path):
|
|
205
|
+
"""ffmpeg arguments that load `option`'s filtergraph from `path`, spelled for this ffmpeg."""
|
|
206
|
+
if _ffmpeg_reads_option_files():
|
|
207
|
+
return [f"-/{option}", str(path)]
|
|
208
|
+
return [_LEGACY_FILTER_FILE_OPTIONS[option], str(path)]
|
|
209
|
+
|
|
210
|
+
|
|
178
211
|
def get_video_duration(video_path):
|
|
179
212
|
"""获取视频时长(秒)"""
|
|
180
213
|
cmd = ["ffprobe", "-v", "quiet", "-show_entries", "format=duration",
|