@oh-story/dsh 0.1.8 → 0.1.9
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 +35 -14
- package/lib/client.js.map +2 -2
- package/lib/drama/manifest.json +165 -69
- package/lib/drama/skills/short-drama/SKILL.md +5 -1
- package/lib/drama/skills/short-drama/scripts/creator_markdown_check.py +126 -9
- package/lib/drama/skills/short-drama/scripts/project_tool.py +11 -0
- package/lib/drama/skills/short-drama-assets/references/character-and-look.md +18 -0
- package/lib/drama/skills/short-drama-assets/references/continuity-lock.md +10 -1
- package/lib/drama/skills/short-drama-edit/SKILL.md +148 -0
- package/lib/drama/skills/short-drama-edit/agents/openai.yaml +4 -0
- package/lib/drama/skills/short-drama-edit/assets/remotion/README.md +60 -0
- package/lib/drama/skills/short-drama-edit/assets/remotion/package.json +15 -0
- package/lib/drama/skills/short-drama-edit/assets/remotion/remotion.config.ts +6 -0
- package/lib/drama/skills/short-drama-edit/assets/remotion/src/Root.tsx +26 -0
- package/lib/drama/skills/short-drama-edit/assets/remotion/src/Subtitles.tsx +88 -0
- package/lib/drama/skills/short-drama-edit/assets/remotion/src/font.ts +46 -0
- package/lib/drama/skills/short-drama-edit/assets/remotion/src/index.ts +4 -0
- package/lib/drama/skills/short-drama-edit/assets/remotion/src/schema.ts +32 -0
- package/lib/drama/skills/short-drama-edit/assets/remotion/tsconfig.json +13 -0
- package/lib/drama/skills/short-drama-edit/assets//345/211/252/350/276/221/345/215/225.md +43 -0
- package/lib/drama/skills/short-drama-edit/references/cut-craft.md +81 -0
- package/lib/drama/skills/short-drama-edit/references/delivery-verify.md +58 -0
- package/lib/drama/skills/short-drama-edit/references/generated-footage.md +96 -0
- package/lib/drama/skills/short-drama-edit/references/sound-and-subtitles.md +113 -0
- package/lib/drama/skills/short-drama-edit/references/stage-contract.md +55 -0
- package/lib/drama/skills/short-drama-edit/scripts/edit_tool.py +1041 -0
- package/lib/drama/skills/short-drama-edit/scripts/selftest.py +456 -0
- package/lib/drama/skills/short-drama-image-prompts/assets/image-prompts.md +5 -0
- package/lib/drama/skills/short-drama-image-prompts/assets/lookdev-prompts.md +3 -0
- package/lib/drama/skills/short-drama-novel-analyze/references/aggregation-and-entities.md +2 -2
- package/lib/drama/skills/short-drama-novel-analyze/references/chapter-extraction.md +4 -1
- package/lib/drama/skills/short-drama-produce/SKILL.md +24 -3
- package/lib/drama/skills/short-drama-produce/references/adapter-contract.md +28 -0
- package/lib/drama/skills/short-drama-produce/references/providers/minimax-h3-video.md +24 -5
- package/lib/drama/skills/short-drama-produce/references/providers/seedance.md +7 -0
- package/lib/drama/skills/short-drama-produce/scripts/fixture_adapter.py +18 -1
- package/lib/drama/skills/short-drama-produce/scripts/production_tool.py +181 -2
- package/lib/drama/skills/short-drama-produce/scripts/provider_adapters.py +57 -2
- package/lib/drama/skills/short-drama-review/SKILL.md +3 -0
- package/lib/drama/skills/short-drama-review/references/production-quality-gates.md +20 -0
- package/lib/drama/skills/short-drama-review/references/rubric-visual-motion.md +36 -0
- package/lib/drama/skills/short-drama-storyboard/SKILL.md +7 -0
- package/lib/drama/skills/short-drama-storyboard/assets/keyframe-prompts.md +4 -0
- package/lib/drama/skills/short-drama-storyboard/references/shot-craft.md +39 -0
- package/lib/drama/skills/short-drama-storyboard/references/stage-contract.md +2 -0
- package/lib/drama/skills/short-drama-video-prompts/SKILL.md +7 -1
- package/lib/drama/skills/short-drama-video-prompts/references/minimax-h3.md +101 -6
- package/lib/drama/skills/short-drama-video-prompts/references/motion-recipe.md +7 -1
- package/lib/drama/skills/short-drama-video-prompts/references/performance-action-timing.md +35 -0
- package/lib/drama/skills/short-drama-video-prompts/references/production-prompt-grammar.md +3 -1
- package/lib/drama/skills/short-drama-video-prompts/references/seedance-2.0.md +13 -3
- package/lib/drama/skills/short-drama-video-prompts/references/seedance-2.5.md +35 -4
- package/lib/drama/skills/short-drama-video-prompts/references/stage-contract.md +1 -1
- package/lib/drama/skills/short-drama-write/SKILL.md +13 -2
- package/lib/drama/skills/short-drama-write/references/screenplay-format.md +6 -0
- package/lib/drama/skills/short-drama-write/references/stage-contract.md +1 -0
- package/lib/index.js +4 -2
- package/lib/index.js.map +2 -2
- package/lib/novel-to-game/VERSION +1 -1
- package/lib/novel-to-game/manifest.json +28 -28
- package/lib/novel-to-game/skills/game-art-direction/SKILL.md +6 -27
- package/lib/novel-to-game/skills/game-build/SKILL.md +12 -29
- package/lib/novel-to-game/skills/game-concept/SKILL.md +10 -26
- package/lib/novel-to-game/skills/game-qa/SKILL.md +5 -15
- package/lib/novel-to-game/skills/game-qa/references/test-design-method.md +1 -1
- package/lib/novel-to-game/skills/game-world-design/SKILL.md +19 -28
- package/lib/novel-to-game/skills/game-world-design/references/dialogue-design-method.md +1 -2
- package/lib/novel-to-game/skills/novel-game-analyze/SKILL.md +6 -9
- package/lib/novel-to-game/skills/novel-to-game/SKILL.md +6 -11
- package/lib/novel-to-game/skills/novel-to-game/references/intake-benchmark-reference.md +0 -4
- package/lib/novel-to-game/skills/novel-to-game/references/intake-method.md +1 -3
- package/lib/novel-to-game/skills/novel-to-game/references/pipeline-contract.md +3 -5
- package/lib/oh-story/manifest.json +292 -172
- package/lib/oh-story/roles/character-designer.md +1 -1
- package/lib/oh-story/roles/narrative-writer.md +56 -242
- package/lib/oh-story/roles/story-architect.md +1 -1
- package/lib/oh-story/skills/story/SKILL.md +1 -1
- package/lib/oh-story/skills/story/VERSION +1 -1
- package/lib/oh-story/skills/story/references/author-memory.md +10 -8
- package/lib/oh-story/skills/story-deslop/SKILL.md +20 -182
- package/lib/oh-story/skills/story-deslop/references/anti-ai-writing.md +19 -47
- package/lib/oh-story/skills/story-deslop/references/author-memory.md +10 -8
- package/lib/oh-story/skills/story-deslop/references/banned-words.md +3 -1
- package/lib/oh-story/skills/story-deslop/references/deslop-gates.md +154 -0
- package/lib/oh-story/skills/story-deslop/references/style-resolution.md +27 -0
- package/lib/oh-story/skills/story-deslop/scripts/check-ai-patterns.js +6 -1
- package/lib/oh-story/skills/story-deslop/scripts/check-degeneration.js +39 -20
- package/lib/oh-story/skills/story-deslop/scripts/normalize-punctuation.js +10 -5
- package/lib/oh-story/skills/story-deslop/scripts/style-whitelist.js +39 -0
- package/lib/oh-story/skills/story-import/SKILL.md +2 -2
- package/lib/oh-story/skills/story-import/references/format-and-structure.md +2 -2
- package/lib/oh-story/skills/story-import/references/tracking-initialization.md +31 -0
- package/lib/oh-story/skills/story-import/references/tracking-transaction.md +2 -29
- package/lib/oh-story/skills/story-import/scripts/check-outline-contract.js +176 -12
- package/lib/oh-story/skills/story-long-analyze/SKILL.md +1 -1
- package/lib/oh-story/skills/story-long-write/SKILL.md +20 -193
- package/lib/oh-story/skills/story-long-write/references/anti-ai-writing.md +19 -47
- package/lib/oh-story/skills/story-long-write/references/artifact-protocols.md +62 -7
- package/lib/oh-story/skills/story-long-write/references/author-memory.md +10 -8
- package/lib/oh-story/skills/story-long-write/references/banned-words.md +3 -1
- package/lib/oh-story/skills/story-long-write/references/benchmark-recall.md +20 -0
- package/lib/oh-story/skills/story-long-write/references/dialogue-mastery.md +6 -4
- package/lib/oh-story/skills/story-long-write/references/long-chapter-quality.md +2 -2
- package/lib/oh-story/skills/story-long-write/references/long-format.md +2 -0
- package/lib/oh-story/skills/story-long-write/references/outline-structure-theory.md +2 -0
- package/lib/oh-story/skills/story-long-write/references/project-files.md +108 -0
- package/lib/oh-story/skills/story-long-write/references/reference-index.md +87 -0
- package/lib/oh-story/skills/story-long-write/references/style-combat-face.md +1 -1
- package/lib/oh-story/skills/story-long-write/references/style-craft.md +9 -29
- 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 +27 -0
- package/lib/oh-story/skills/story-long-write/references/tracking-initialization.md +31 -0
- package/lib/oh-story/skills/story-long-write/references/tracking-transaction.md +2 -29
- package/lib/oh-story/skills/story-long-write/references/workflow-chapter.md +51 -52
- package/lib/oh-story/skills/story-long-write/references/workflow-daily.md +8 -7
- package/lib/oh-story/skills/story-long-write/references/workflow-setup.md +36 -15
- package/lib/oh-story/skills/story-long-write/references/writer-prompt-fallback.md +22 -0
- package/lib/oh-story/skills/story-long-write/references/writing-craft.md +69 -2
- package/lib/oh-story/skills/story-long-write/scripts/build_writer_prompt.py +410 -0
- package/lib/oh-story/skills/story-long-write/scripts/check-ai-patterns.js +6 -1
- package/lib/oh-story/skills/story-long-write/scripts/check-degeneration.js +39 -20
- package/lib/oh-story/skills/story-long-write/scripts/check-outline-contract.js +176 -12
- package/lib/oh-story/skills/story-long-write/scripts/check-outline-copy.js +90 -23
- package/lib/oh-story/skills/story-long-write/scripts/normalize-punctuation.js +10 -5
- package/lib/oh-story/skills/story-long-write/scripts/outline_view.py +283 -0
- package/lib/oh-story/skills/story-long-write/scripts/style-whitelist.js +39 -0
- package/lib/oh-story/skills/story-review/SKILL.md +13 -3
- package/lib/oh-story/skills/story-review/references/anti-ai-writing.md +19 -47
- package/lib/oh-story/skills/story-review/references/author-memory.md +10 -8
- package/lib/oh-story/skills/story-review/references/banned-words.md +3 -1
- package/lib/oh-story/skills/story-review/references/dialogue-mastery.md +6 -4
- package/lib/oh-story/skills/story-review/references/review-quality.md +2 -2
- package/lib/oh-story/skills/story-review/references/style-resolution.md +27 -0
- package/lib/oh-story/skills/story-review/references/tracking-initialization.md +31 -0
- package/lib/oh-story/skills/story-review/references/tracking-transaction.md +2 -29
- package/lib/oh-story/skills/story-review/scripts/check-ai-patterns.js +6 -1
- package/lib/oh-story/skills/story-review/scripts/check-degeneration.js +39 -20
- package/lib/oh-story/skills/story-review/scripts/normalize-punctuation.js +10 -5
- package/lib/oh-story/skills/story-review/scripts/style-whitelist.js +39 -0
- package/lib/oh-story/skills/story-setup/SKILL.md +17 -10
- package/lib/oh-story/skills/story-setup/references/agent-references/anti-ai-writing.md +19 -47
- package/lib/oh-story/skills/story-setup/references/agent-references/banned-words.md +3 -1
- package/lib/oh-story/skills/story-setup/references/agent-references/deslop-gates.md +154 -0
- package/lib/oh-story/skills/story-setup/references/agent-references/dialogue-mastery.md +6 -4
- package/lib/oh-story/skills/story-setup/references/agent-references/format-and-structure.md +2 -2
- package/lib/oh-story/skills/story-setup/references/agent-references/short-genre-formulas.md +1 -1
- package/lib/oh-story/skills/story-setup/references/agent-references/style-combat-face.md +1 -1
- 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 +27 -0
- package/lib/oh-story/skills/story-setup/references/agent-references/writing-craft.md +69 -2
- package/lib/oh-story/skills/story-setup/references/diagnostics.md +38 -0
- package/lib/oh-story/skills/story-short-analyze/references/analysis-writing-techniques.md +1 -1
- package/lib/oh-story/skills/story-short-analyze/references/source-story-quality.md +2 -2
- package/lib/oh-story/skills/story-short-write/SKILL.md +18 -186
- package/lib/oh-story/skills/story-short-write/references/author-memory.md +10 -8
- package/lib/oh-story/skills/story-short-write/references/banned-words.md +3 -1
- package/lib/oh-story/skills/story-short-write/references/benchmark-recall.md +60 -0
- package/lib/oh-story/skills/story-short-write/references/dialogue-mastery.md +6 -4
- package/lib/oh-story/skills/story-short-write/references/genre-styles//344/270/226/346/203/205/346/211/223/350/204/270.md +2 -2
- package/lib/oh-story/skills/story-short-write/references/genre-styles//345/217/214/347/224/267/344/270/273.md +1 -1
- package/lib/oh-story/skills/story-short-write/references/genre-styles//345/244/215/344/273/207/346/211/223/350/204/270.md +3 -3
- package/lib/oh-story/skills/story-short-write/references/genre-styles//345/256/205/346/226/227/345/256/253/346/226/227.md +3 -3
- package/lib/oh-story/skills/story-short-write/references/genre-styles//346/200/273/350/243/201/350/261/252/351/227/250.md +3 -3
- package/lib/oh-story/skills/story-short-write/references/genre-styles//346/202/254/347/226/221.md +5 -5
- package/lib/oh-story/skills/story-short-write/references/genre-styles//346/260/221/344/277/227/346/200/252/350/260/210.md +4 -4
- package/lib/oh-story/skills/story-short-write/references/genre-styles//347/224/234/345/256/240.md +5 -5
- package/lib/oh-story/skills/story-short-write/references/genre-styles//350/277/275/345/246/273/347/201/253/350/221/254/345/234/272.md +3 -3
- package/lib/oh-story/skills/story-short-write/references/genre-writing-formulas.md +1 -1
- package/lib/oh-story/skills/story-short-write/references/genre-writing-techniques.md +1 -1
- package/lib/oh-story/skills/story-short-write/references/reference-index.md +53 -0
- package/lib/oh-story/skills/story-short-write/references/short-craft.md +12 -14
- package/lib/oh-story/skills/story-short-write/references/short-deslop.md +10 -10
- package/lib/oh-story/skills/story-short-write/references/short-format.md +3 -1
- package/lib/oh-story/skills/story-short-write/references/short-prose-quality.md +2 -2
- package/lib/oh-story/skills/story-short-write/references/style-resolution.md +27 -0
- package/lib/oh-story/skills/story-short-write/references/workflow-design.md +77 -0
- package/lib/oh-story/skills/story-short-write/references/writing-workflow.md +5 -5
- package/lib/oh-story/skills/story-short-write/scripts/check-ai-patterns.js +6 -1
- package/lib/oh-story/skills/story-short-write/scripts/check-degeneration.js +39 -20
- package/lib/oh-story/skills/story-short-write/scripts/check-delivery-contract.js +34 -5
- package/lib/oh-story/skills/story-short-write/scripts/check-outline-copy.js +90 -23
- package/lib/oh-story/skills/story-short-write/scripts/normalize-punctuation.js +10 -5
- package/lib/oh-story/skills/story-short-write/scripts/style-whitelist.js +39 -0
- package/lib/types/client/drama-production-view.d.ts.map +1 -1
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/production-prompts.d.ts.map +1 -1
- package/lib/types/client/production-runtime.d.ts +17 -0
- package/lib/types/client/production-runtime.d.ts.map +1 -1
- package/lib/types/skill-provider.d.ts.map +1 -1
- package/lib/video-recap/CHANGELOG.md +31 -1
- package/lib/video-recap/manifest.json +162 -162
- package/lib/video-recap/skills/video-assemble/scripts/artifacts.py +4 -9
- package/lib/video-recap/skills/video-assemble/scripts/assemble.py +47 -60
- package/lib/video-recap/skills/video-assemble/scripts/assembly_contract.py +77 -115
- package/lib/video-recap/skills/video-assemble/scripts/assembly_settings.py +41 -40
- package/lib/video-recap/skills/video-assemble/scripts/audio_automation.py +22 -32
- package/lib/video-recap/skills/video-assemble/scripts/audio_mix.py +102 -192
- package/lib/video-recap/skills/video-assemble/scripts/jianying_builders.py +14 -35
- package/lib/video-recap/skills/video-assemble/scripts/jianying_model.py +14 -12
- package/lib/video-recap/skills/video-assemble/scripts/jianying_optional.py +8 -9
- package/lib/video-recap/skills/video-assemble/scripts/jianying_schema.py +3 -14
- package/lib/video-recap/skills/video-assemble/scripts/jianying_timeline_contract.py +23 -12
- package/lib/video-recap/skills/video-assemble/scripts/jianying_tracks.py +1 -4
- package/lib/video-recap/skills/video-assemble/scripts/jianying_writer.py +24 -25
- package/lib/video-recap/skills/video-assemble/scripts/lib.py +31 -85
- package/lib/video-recap/skills/video-assemble/scripts/media.py +100 -144
- package/lib/video-recap/skills/video-assemble/scripts/narration_audio.py +101 -195
- package/lib/video-recap/skills/video-assemble/scripts/render_preflight.py +10 -21
- package/lib/video-recap/skills/video-assemble/scripts/source_subtitles.py +90 -169
- package/lib/video-recap/skills/video-assemble/scripts/subtitle_core.py +69 -122
- package/lib/video-recap/skills/video-assemble/scripts/subtitle_render.py +10 -17
- package/lib/video-recap/skills/video-assemble/scripts/timeline.py +33 -48
- package/lib/video-recap/skills/video-assemble/scripts/timeline_emit.py +14 -20
- package/lib/video-recap/skills/video-assemble/scripts/visual_render.py +135 -213
- package/lib/video-recap/skills/video-cut/scripts/cut_cli.py +49 -57
- package/lib/video-recap/skills/video-cut/scripts/cut_contract.py +38 -74
- package/lib/video-recap/skills/video-cut/scripts/cut_render.py +46 -95
- package/lib/video-recap/skills/video-cut/scripts/lib.py +23 -25
- package/lib/video-recap/skills/video-cut/scripts/media_geometry.py +70 -173
- package/lib/video-recap/skills/video-cut/scripts/narration_mapping.py +28 -53
- package/lib/video-recap/skills/video-cut/scripts/sentence_boundaries.py +162 -322
- package/lib/video-recap/skills/video-recap/references/timeline-and-jianying.md +6 -7
- package/lib/video-recap/skills/video-recap/scripts/doctor.py +104 -124
- package/lib/video-recap/skills/video-recap/scripts/final_qc.py +255 -412
- package/lib/video-recap/skills/video-recap/scripts/lib.py +30 -52
- package/lib/video-recap/skills/video-recap/scripts/materials.py +97 -120
- package/lib/video-recap/skills/video-recap/scripts/mimo_qc_client.py +7 -12
- package/lib/video-recap/skills/video-recap/scripts/mimo_qc_evidence.py +46 -97
- package/lib/video-recap/skills/video-recap/scripts/mimo_qc_observations.py +18 -36
- package/lib/video-recap/skills/video-recap/scripts/mimo_qc_payload.py +29 -47
- package/lib/video-recap/skills/video-recap/scripts/mimo_qc_report.py +43 -76
- package/lib/video-recap/skills/video-recap/scripts/mimo_qc_runner.py +2 -1
- package/lib/video-recap/skills/video-recap/scripts/qc_contract.py +2 -1
- package/lib/video-recap/skills/video-recap/scripts/recap_cli.py +2 -2
- package/lib/video-recap/skills/video-recap/scripts/recap_inspect.py +129 -145
- package/lib/video-recap/skills/video-recap/scripts/recap_review.py +27 -20
- package/lib/video-recap/skills/video-recap/scripts/recap_runner.py +20 -31
- package/lib/video-recap/skills/video-recap/scripts/recap_runtime.py +31 -106
- package/lib/video-recap/skills/video-recap/scripts/recap_stage_qc.py +43 -113
- package/lib/video-recap/skills/video-recap/scripts/recap_timeline.py +196 -377
- package/lib/video-recap/skills/video-script/scripts/agent_brief.py +1 -1
- package/lib/video-recap/skills/video-script/scripts/agent_text.py +63 -172
- package/lib/video-recap/skills/video-script/scripts/brief_context.py +80 -135
- package/lib/video-recap/skills/video-script/scripts/brief_inputs.py +125 -167
- package/lib/video-recap/skills/video-script/scripts/brief_timeline.py +93 -208
- package/lib/video-recap/skills/video-script/scripts/deslop_qc.py +20 -46
- package/lib/video-recap/skills/video-script/scripts/lib.py +30 -26
- package/lib/video-recap/skills/video-script/scripts/narration_lint.py +145 -140
- package/lib/video-recap/skills/video-script/scripts/review_response.py +12 -42
- package/lib/video-recap/skills/video-script/scripts/review_runner.py +0 -6
- package/lib/video-recap/skills/video-script/scripts/speech_ownership.py +39 -100
- package/lib/video-recap/skills/video-script/scripts/timeline_fusion.py +33 -124
- package/lib/video-recap/skills/video-understanding/scripts/agent_brief.py +1 -1
- package/lib/video-recap/skills/video-understanding/scripts/agent_text.py +63 -172
- package/lib/video-recap/skills/video-understanding/scripts/asr.py +1 -2
- package/lib/video-recap/skills/video-understanding/scripts/brief_context.py +80 -135
- package/lib/video-recap/skills/video-understanding/scripts/brief_inputs.py +125 -167
- package/lib/video-recap/skills/video-understanding/scripts/brief_timeline.py +93 -208
- package/lib/video-recap/skills/video-understanding/scripts/consolidate.py +1 -1
- package/lib/video-recap/skills/video-understanding/scripts/deslop_qc.py +20 -46
- package/lib/video-recap/skills/video-understanding/scripts/lib.py +31 -26
- package/lib/video-recap/skills/video-understanding/scripts/narration_lint.py +145 -140
- package/lib/video-recap/skills/video-understanding/scripts/speech_ownership.py +39 -100
- package/lib/video-recap/skills/video-understanding/scripts/timeline_fusion.py +33 -124
- package/lib/video-recap/skills/video-understanding/scripts/vlm.py +1 -4
- package/lib/video-recap/skills/video-voiceover/scripts/dub.py +7 -13
- package/lib/video-recap/skills/video-voiceover/scripts/fish_audio.py +12 -30
- package/lib/video-recap/skills/video-voiceover/scripts/lib.py +98 -131
- package/lib/video-recap/skills/video-voiceover/scripts/tts_audio.py +17 -28
- package/lib/video-recap/skills/video-voiceover/scripts/voiceover.py +151 -240
- package/package.json +41 -41
|
@@ -5,20 +5,19 @@ from pathlib import Path
|
|
|
5
5
|
from lib import CONFIG, log
|
|
6
6
|
|
|
7
7
|
def _maybe_export_jianying(work_dir, out_dir, stem):
|
|
8
|
-
"""Lazy-import the optional 剪映 exporter and write a draft from timeline.json.
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
return
|
|
8
|
+
"""Lazy-import the optional 剪映 exporter and write a draft from timeline.json.
|
|
9
|
+
|
|
10
|
+
The export is a documented fail-open sidecar: any failure is logged and never
|
|
11
|
+
fails the already-rendered recap."""
|
|
13
12
|
try:
|
|
14
13
|
from export_jianying import export_timeline_to_jianying
|
|
15
14
|
from timeline import load_timeline
|
|
16
|
-
parent = out_dir or CONFIG
|
|
15
|
+
parent = out_dir or CONFIG["jianying_draft_dir"] or str(work_dir)
|
|
17
16
|
draft_dir, notes = export_timeline_to_jianying(
|
|
18
|
-
load_timeline(
|
|
19
|
-
bundle_media=CONFIG
|
|
17
|
+
load_timeline(Path(work_dir) / "timeline.json"), parent, draft_name=f"recap_{stem}",
|
|
18
|
+
bundle_media=CONFIG["jianying_bundle_media"])
|
|
20
19
|
for n in notes:
|
|
21
20
|
log(f" 注意: {n}")
|
|
22
21
|
log(f"剪映草稿已导出: {draft_dir}")
|
|
23
|
-
except Exception as exc:
|
|
22
|
+
except Exception as exc:
|
|
24
23
|
log(f" ⚠️ 剪映导出失败(不影响成片): {exc}")
|
|
@@ -8,15 +8,7 @@ from jianying_templates import template
|
|
|
8
8
|
|
|
9
9
|
# The full 剪映 materials object: ~45 parallel arrays. Only arrays backed by a
|
|
10
10
|
# production builder are populated; retaining the full shape preserves compatibility.
|
|
11
|
-
MATERIAL_KEYS =
|
|
12
|
-
"ai_translates audio_balances audio_effects audio_fades audio_track_indexes audios "
|
|
13
|
-
"beats canvases chromas color_curves digital_humans drafts effects flowers green_screens "
|
|
14
|
-
"handwrites hsl images log_color_wheels loudnesses manual_deformations masks common_mask "
|
|
15
|
-
"material_animations material_colors multi_language_refs placeholders plugin_effects "
|
|
16
|
-
"primary_color_wheels realtime_denoises shapes smart_crops smart_relights "
|
|
17
|
-
"sound_channel_mappings speeds stickers tail_leaders text_templates texts time_marks "
|
|
18
|
-
"transitions video_effects video_trackings videos vocal_beautifys vocal_separations"
|
|
19
|
-
).split()
|
|
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"]
|
|
20
12
|
|
|
21
13
|
|
|
22
14
|
def us(seconds):
|
|
@@ -34,11 +26,8 @@ def full_materials(filled):
|
|
|
34
26
|
def scrub_platform_identity(project):
|
|
35
27
|
"""Remove hardware fingerprints carried by the pinned upstream templates."""
|
|
36
28
|
for platform_key in ("last_modified_platform", "platform"):
|
|
37
|
-
platform = project.get(platform_key)
|
|
38
|
-
if not isinstance(platform, dict):
|
|
39
|
-
continue
|
|
40
29
|
for identity_key in ("device_id", "hard_disk_id", "mac_address"):
|
|
41
|
-
|
|
30
|
+
project[platform_key][identity_key] = ""
|
|
42
31
|
return project
|
|
43
32
|
|
|
44
33
|
|
|
@@ -96,7 +85,7 @@ def material_category_registry():
|
|
|
96
85
|
|
|
97
86
|
def validate_material_category(category):
|
|
98
87
|
"""Return deterministic support metadata for a public or future material kind."""
|
|
99
|
-
normalized =
|
|
88
|
+
normalized = category.strip().lower()
|
|
100
89
|
registry = material_category_registry()
|
|
101
90
|
info = dict(registry.get(normalized, {"status": "unsupported", "materials_key": None, "track_type": None}))
|
|
102
91
|
info["category"] = normalized
|
|
@@ -9,18 +9,12 @@ RESOURCE_TRACK_KINDS = {
|
|
|
9
9
|
"face_effect", "sound", "sticker", "text_template", "video_effect",
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
|
|
13
12
|
def _error(path, expectation):
|
|
14
13
|
raise ValueError(f"invalid timeline {path}: {expectation}")
|
|
15
14
|
|
|
16
15
|
|
|
17
16
|
def _is_number(value):
|
|
18
|
-
|
|
19
|
-
return False
|
|
20
|
-
try:
|
|
21
|
-
return math.isfinite(value)
|
|
22
|
-
except OverflowError:
|
|
23
|
-
return False
|
|
17
|
+
return isinstance(value, (int, float)) and not isinstance(value, bool) and math.isfinite(value)
|
|
24
18
|
|
|
25
19
|
|
|
26
20
|
def _field_path(path, key):
|
|
@@ -88,6 +82,19 @@ def _validate_video_clip(clip, path):
|
|
|
88
82
|
_error(f"{path}.compound", "must be a boolean")
|
|
89
83
|
|
|
90
84
|
|
|
85
|
+
def _validate_resource_config(config, path):
|
|
86
|
+
if not isinstance(config, dict):
|
|
87
|
+
_error(path, "must be an object")
|
|
88
|
+
if not isinstance(config.get("main_config"), dict):
|
|
89
|
+
_error(f"{path}.main_config", "must be an object")
|
|
90
|
+
resources = config.get("resources", [])
|
|
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")
|
|
96
|
+
|
|
97
|
+
|
|
91
98
|
def _validate_segment(segment, path, kind):
|
|
92
99
|
if not isinstance(segment, dict):
|
|
93
100
|
_error(path, "must be an object")
|
|
@@ -110,8 +117,8 @@ def _validate_segment(segment, path, kind):
|
|
|
110
117
|
_error(f"{path}.resource_package", "must be a non-empty string")
|
|
111
118
|
elif sources[0] == "material" and not isinstance(source, dict):
|
|
112
119
|
_error(f"{path}.material", "must be an object")
|
|
113
|
-
elif sources[0] == "resource_config"
|
|
114
|
-
|
|
120
|
+
elif sources[0] == "resource_config":
|
|
121
|
+
_validate_resource_config(source, f"{path}.resource_config")
|
|
115
122
|
|
|
116
123
|
|
|
117
124
|
def _validate_track(track, path):
|
|
@@ -158,9 +165,13 @@ def _validate_v2(timeline):
|
|
|
158
165
|
_error("canvas.fps", "must be greater than 0")
|
|
159
166
|
|
|
160
167
|
_require_number(timeline, "duration", "", minimum=0)
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
168
|
+
resource_packages = timeline.get("resource_packages", {})
|
|
169
|
+
if not isinstance(resource_packages, dict):
|
|
170
|
+
_error("resource_packages", "must be an object")
|
|
171
|
+
for name, config in resource_packages.items():
|
|
172
|
+
_validate_resource_config(config, f"resource_packages.{name}")
|
|
173
|
+
if "style_presets" in timeline and not isinstance(timeline["style_presets"], dict):
|
|
174
|
+
_error("style_presets", "must be an object")
|
|
164
175
|
tracks = timeline.get("tracks")
|
|
165
176
|
if not isinstance(tracks, list):
|
|
166
177
|
_error("tracks", "must be an array")
|
|
@@ -60,10 +60,7 @@ class TrackAllocator:
|
|
|
60
60
|
return any(int(start_us) < old_end and end_us > old_start for old_start, old_end in existing)
|
|
61
61
|
|
|
62
62
|
def allocate(self, kind, base_name, start_us, duration_us):
|
|
63
|
-
band = TRACK_LAYOUT_BANDS
|
|
64
|
-
if band is None:
|
|
65
|
-
band = TrackBand(kind, "video", 120_000, "unregistered fallback")
|
|
66
|
-
base_name = base_name or kind
|
|
63
|
+
band = TRACK_LAYOUT_BANDS[kind]
|
|
67
64
|
suffix = 0
|
|
68
65
|
while True:
|
|
69
66
|
name = base_name if suffix == 0 else f"{base_name}-{suffix}"
|
|
@@ -118,17 +118,19 @@ def _replace_value(value, old, new):
|
|
|
118
118
|
elif isinstance(value, str):
|
|
119
119
|
if value == old:
|
|
120
120
|
return new
|
|
121
|
-
stripped = value.lstrip()
|
|
122
|
-
if stripped.startswith(("{", "[")):
|
|
123
|
-
try:
|
|
124
|
-
parsed = json.loads(value)
|
|
125
|
-
except json.JSONDecodeError:
|
|
126
|
-
return value
|
|
127
|
-
replaced = _replace_value(parsed, old, new)
|
|
128
|
-
return json.dumps(replaced, ensure_ascii=False, separators=(",", ":"))
|
|
129
121
|
return value
|
|
130
122
|
|
|
131
123
|
|
|
124
|
+
def _replace_material_resource_path(material, materials_key, old, new):
|
|
125
|
+
"""Rewrite one declared resource, including the known rich-text JSON field."""
|
|
126
|
+
_replace_value(material, old, new)
|
|
127
|
+
if materials_key != "texts" or not isinstance(material.get("content"), str):
|
|
128
|
+
return
|
|
129
|
+
content = json.loads(material["content"])
|
|
130
|
+
_replace_value(content, old, new)
|
|
131
|
+
material["content"] = json.dumps(content, ensure_ascii=False, separators=(",", ":"))
|
|
132
|
+
|
|
133
|
+
|
|
132
134
|
def _safe_resource_target(target_path):
|
|
133
135
|
normalized = os.path.normpath(str(target_path).replace("\\", "/"))
|
|
134
136
|
if normalized in {"", "."} or os.path.isabs(normalized):
|
|
@@ -182,16 +184,11 @@ def _is_packaged_path(value):
|
|
|
182
184
|
|
|
183
185
|
|
|
184
186
|
def _descriptor(raw, default_kind, *, required):
|
|
185
|
-
if isinstance(raw,
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
source = raw.get("source_path") or raw.get("source")
|
|
191
|
-
resource_kind = raw.get("resource_kind") or raw.get("kind") or default_kind
|
|
192
|
-
target_path = raw.get("target_path")
|
|
193
|
-
else:
|
|
194
|
-
raise ValueError("JianYing resources entries must be paths or objects")
|
|
187
|
+
if not isinstance(raw, dict):
|
|
188
|
+
raise ValueError("JianYing resources entries must be objects")
|
|
189
|
+
source = raw.get("source_path")
|
|
190
|
+
resource_kind = raw.get("resource_kind", default_kind)
|
|
191
|
+
target_path = raw.get("target_path")
|
|
195
192
|
if not isinstance(source, str) or not source:
|
|
196
193
|
raise ValueError("JianYing resource source_path must be a non-empty string")
|
|
197
194
|
if not isinstance(resource_kind, str) or resource_kind not in {
|
|
@@ -199,11 +196,6 @@ def _descriptor(raw, default_kind, *, required):
|
|
|
199
196
|
"text", "text_template", "transition", "video",
|
|
200
197
|
}:
|
|
201
198
|
raise ValueError(f"invalid JianYing resource kind: {resource_kind}")
|
|
202
|
-
suffix = os.path.splitext(source)[1].lower()
|
|
203
|
-
if suffix == ".cube":
|
|
204
|
-
resource_kind = "lut"
|
|
205
|
-
elif suffix in {".ttf", ".otf"}:
|
|
206
|
-
resource_kind = "fonts"
|
|
207
199
|
return {
|
|
208
200
|
"source_path": source,
|
|
209
201
|
"resource_kind": resource_kind,
|
|
@@ -220,7 +212,12 @@ def _material_resource_descriptors(material, default_kind):
|
|
|
220
212
|
path = material.get("path")
|
|
221
213
|
if isinstance(path, str) and path and not _is_packaged_path(path):
|
|
222
214
|
if not any(item["source_path"] == path for item in descriptors):
|
|
223
|
-
descriptors.append(
|
|
215
|
+
descriptors.append({
|
|
216
|
+
"source_path": path,
|
|
217
|
+
"resource_kind": default_kind,
|
|
218
|
+
"target_path": None,
|
|
219
|
+
"required": False,
|
|
220
|
+
})
|
|
224
221
|
return descriptors
|
|
225
222
|
|
|
226
223
|
|
|
@@ -308,7 +305,9 @@ def bundle_media(content, meta, draft_dir):
|
|
|
308
305
|
copied[source_key] = {"draft_path": draft_path}
|
|
309
306
|
else:
|
|
310
307
|
draft_path = existing["draft_path"]
|
|
311
|
-
|
|
308
|
+
_replace_material_resource_path(
|
|
309
|
+
material, materials_key, src, draft_path
|
|
310
|
+
)
|
|
312
311
|
|
|
313
312
|
material_group = next(group for group in meta["draft_materials"] if group["type"] == 0)
|
|
314
313
|
material_group["value"] = meta_values
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"""Self-contained config + utilities for this skill (no cross-skill imports)."""
|
|
2
|
+
import math
|
|
2
3
|
import os
|
|
3
4
|
import subprocess
|
|
4
5
|
from pathlib import Path
|
|
@@ -7,85 +8,40 @@ from pathlib import Path
|
|
|
7
8
|
# ── 配置 ──────────────────────────────────────────────────────────────
|
|
8
9
|
_EXISTING_CONFIG_REF = globals().get("CONFIG")
|
|
9
10
|
|
|
10
|
-
DEFAULT_MIMO_API_URL = "https://api.xiaomimimo.com/v1"
|
|
11
|
-
DEFAULT_MIMO_TOKEN_PLAN_CLUSTER = "cn"
|
|
12
|
-
MIMO_TOKEN_PLAN_API_URLS = {
|
|
13
|
-
"cn": "https://token-plan-cn.xiaomimimo.com/v1",
|
|
14
|
-
"sgp": "https://token-plan-sgp.xiaomimimo.com/v1",
|
|
15
|
-
"ams": "https://token-plan-ams.xiaomimimo.com/v1",
|
|
16
|
-
}
|
|
17
|
-
DEFAULT_MIMO_MODEL = "mimo-v2.5" # VLM / chat (vision understanding)
|
|
18
|
-
DEFAULT_MIMO_ASR_MODEL = "mimo-v2.5-asr" # speech-to-text
|
|
19
|
-
DEFAULT_MIMO_TTS_MODEL = "mimo-v2.5-tts" # text-to-speech
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def normalize_api_url(raw_url):
|
|
23
|
-
"""Normalize a MiMo (OpenAI-compatible) base URL or chat/completions endpoint."""
|
|
24
|
-
url = (raw_url or DEFAULT_MIMO_API_URL).rstrip("/")
|
|
25
|
-
if url.endswith("/chat/completions"):
|
|
26
|
-
return url
|
|
27
|
-
return f"{url}/chat/completions"
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
def is_mimo_token_plan_key(api_key):
|
|
31
|
-
"""Return True for Xiaomi MiMo Token Plan keys, which use token-plan base URLs."""
|
|
32
|
-
return str(api_key or "").strip().startswith("tp-")
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
def default_mimo_api_url(api_key="", cluster=None):
|
|
36
|
-
"""Pick the correct MiMo base URL for pay-as-you-go vs Token Plan keys.
|
|
37
|
-
|
|
38
|
-
MiMo uses independent credentials for pay-as-you-go (`sk-*`) and Token Plan
|
|
39
|
-
(`tp-*`). Token Plan keys must be sent to the Token Plan cluster base URL,
|
|
40
|
-
not the pay-as-you-go `api.xiaomimimo.com` endpoint.
|
|
41
|
-
"""
|
|
42
|
-
if is_mimo_token_plan_key(api_key):
|
|
43
|
-
cluster_name = (cluster or os.environ.get("MIMO_TOKEN_PLAN_CLUSTER") or DEFAULT_MIMO_TOKEN_PLAN_CLUSTER)
|
|
44
|
-
cluster_name = str(cluster_name).strip().lower()
|
|
45
|
-
return MIMO_TOKEN_PLAN_API_URLS.get(cluster_name, MIMO_TOKEN_PLAN_API_URLS[DEFAULT_MIMO_TOKEN_PLAN_CLUSTER])
|
|
46
|
-
return DEFAULT_MIMO_API_URL
|
|
47
|
-
|
|
48
11
|
|
|
49
12
|
def env_int(name, default, *, minimum=None):
|
|
50
|
-
"""Read an integer env var;
|
|
51
|
-
raw = os.environ.get(name)
|
|
52
|
-
if raw
|
|
13
|
+
"""Read an integer env var; an unset/empty value yields `default`, a malformed one is an error."""
|
|
14
|
+
raw = os.environ.get(name, "")
|
|
15
|
+
if raw == "":
|
|
53
16
|
return default
|
|
54
17
|
try:
|
|
55
18
|
value = int(raw)
|
|
56
|
-
except
|
|
57
|
-
|
|
58
|
-
if minimum is
|
|
59
|
-
value = max(minimum, value)
|
|
60
|
-
return value
|
|
19
|
+
except ValueError as exc:
|
|
20
|
+
raise ValueError(f"环境变量 {name}={raw!r} 不是整数") from exc
|
|
21
|
+
return value if minimum is None else max(minimum, value)
|
|
61
22
|
|
|
62
23
|
|
|
63
24
|
def env_bool(name, default=False):
|
|
64
25
|
"""Read common boolean env var forms."""
|
|
65
|
-
raw = os.environ.get(name)
|
|
66
|
-
if raw
|
|
26
|
+
raw = os.environ.get(name, "")
|
|
27
|
+
if raw == "":
|
|
67
28
|
return default
|
|
68
29
|
return raw.strip().lower() in {"1", "true", "yes", "y", "on"}
|
|
69
30
|
|
|
70
31
|
|
|
71
32
|
def env_float(name, default, *, minimum=None):
|
|
72
|
-
"""Read a float env var;
|
|
73
|
-
raw = os.environ.get(name)
|
|
74
|
-
if raw
|
|
33
|
+
"""Read a float env var; an unset/empty value yields `default`, a malformed one is an error."""
|
|
34
|
+
raw = os.environ.get(name, "")
|
|
35
|
+
if raw == "":
|
|
75
36
|
return default
|
|
76
37
|
try:
|
|
77
38
|
value = float(raw)
|
|
78
|
-
except
|
|
79
|
-
|
|
80
|
-
if
|
|
81
|
-
|
|
82
|
-
return value
|
|
83
|
-
|
|
39
|
+
except ValueError as exc:
|
|
40
|
+
raise ValueError(f"环境变量 {name}={raw!r} 不是数字") from exc
|
|
41
|
+
if not math.isfinite(value):
|
|
42
|
+
raise ValueError(f"环境变量 {name}={raw!r} 必须是有限数")
|
|
43
|
+
return value if minimum is None else max(minimum, value)
|
|
84
44
|
|
|
85
|
-
# Single MiMo credential powers ASR + VLM + TTS. Per-capability overrides
|
|
86
|
-
# (MIMO_VIDEO_API_KEY / MIMO_TTS_API_KEY / MIMO_ASR_API_KEY and their *_API_URL forms)
|
|
87
|
-
# are optional and fall back to MIMO_API_KEY / MIMO_API_URL. Token-Plan keys (tp-*) auto-
|
|
88
|
-
# route to the Token-Plan cluster base URL; pay-as-you-go keys use api.xiaomimimo.com.
|
|
89
45
|
|
|
90
46
|
# Cross-language source: when the original audio is in a language the narration is NOT in
|
|
91
47
|
# (e.g. a Japanese drama recapped in Chinese), the original speech bleeding under the narration
|
|
@@ -181,9 +137,8 @@ if isinstance(_EXISTING_CONFIG_REF, dict):
|
|
|
181
137
|
CONFIG = _EXISTING_CONFIG_REF
|
|
182
138
|
|
|
183
139
|
SCRIPT_DIR = Path(__file__).parent
|
|
184
|
-
PROMPTS_DIR = SCRIPT_DIR.parent / "references"
|
|
185
140
|
|
|
186
|
-
def narration_tempo_budget(tts_rate_offset=0.0
|
|
141
|
+
def narration_tempo_budget(tts_rate_offset=0.0):
|
|
187
142
|
"""Return the canonical tempo budget shared by voiceover and assemble.
|
|
188
143
|
|
|
189
144
|
`effective_tempo` is the user-perceived cumulative compression:
|
|
@@ -192,13 +147,13 @@ def narration_tempo_budget(tts_rate_offset=0.0, *, config=None):
|
|
|
192
147
|
offset; callers must fail/shorten instead of time-trimming speech when the
|
|
193
148
|
needed ratio exceeds `segment_tempo_max`.
|
|
194
149
|
"""
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
150
|
+
global_speed = CONFIG["narration_speed"]
|
|
151
|
+
rate_factor = max(0.01, 1.0 + float(tts_rate_offset))
|
|
152
|
+
cumulative_max = CONFIG["narration_cumulative_tempo_max"]
|
|
153
|
+
hard_max = max(cumulative_max, CONFIG["narration_cumulative_tempo_hard_max"])
|
|
154
|
+
segment_tempo_max = max(1.0, min(
|
|
155
|
+
CONFIG["tts_segment_tempo_max"], cumulative_max / (global_speed * rate_factor)
|
|
156
|
+
))
|
|
202
157
|
return {
|
|
203
158
|
"global_narration_speed": global_speed,
|
|
204
159
|
"tts_rate_factor": rate_factor,
|
|
@@ -213,16 +168,10 @@ def log(msg):
|
|
|
213
168
|
|
|
214
169
|
def run_cmd(cmd, **kwargs):
|
|
215
170
|
"""运行命令,返回 CompletedProcess"""
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
for
|
|
219
|
-
|
|
220
|
-
display_parts.append(text if len(text) <= 240 else text[:237] + "...")
|
|
221
|
-
display = " ".join(display_parts)
|
|
222
|
-
else:
|
|
223
|
-
display = str(cmd)
|
|
224
|
-
if len(display) > 2000:
|
|
225
|
-
display = display[:1997] + "..."
|
|
171
|
+
display = " ".join(
|
|
172
|
+
text if len(text) <= 240 else text[:237] + "..."
|
|
173
|
+
for text in map(str, cmd)
|
|
174
|
+
)
|
|
226
175
|
log(f"运行: {display}")
|
|
227
176
|
return subprocess.run(cmd, capture_output=True, text=True, **kwargs)
|
|
228
177
|
|
|
@@ -232,8 +181,5 @@ def get_video_duration(video_path):
|
|
|
232
181
|
"-of", "csv=p=0", str(video_path)]
|
|
233
182
|
result = run_cmd(cmd)
|
|
234
183
|
if result.returncode != 0:
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
return float(result.stdout.strip())
|
|
238
|
-
except (TypeError, ValueError):
|
|
239
|
-
return 0.0
|
|
184
|
+
raise RuntimeError(f"ffprobe 无法读取时长 {video_path}: {result.stderr}")
|
|
185
|
+
return float(result.stdout.strip())
|