@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
|
@@ -13,7 +13,7 @@ from extract import (
|
|
|
13
13
|
parse_frame_number,
|
|
14
14
|
)
|
|
15
15
|
from lib import CONFIG
|
|
16
|
-
from lib import log, api_call, load_prompt, mimo_video_api_call, run_cmd,
|
|
16
|
+
from lib import log, api_call, load_prompt, mimo_video_api_call, run_cmd, file_identity
|
|
17
17
|
|
|
18
18
|
# ── Step 4: VLM 视觉分析 ─────────────────────────────────────────────
|
|
19
19
|
|
|
@@ -55,8 +55,8 @@ def _parse_vlm_depth_response(raw_text):
|
|
|
55
55
|
def _max_frames_for_duration(duration):
|
|
56
56
|
"""Frames the VLM sees for one scene: ~1 per `vlm_seconds_per_frame`, floor 3, capped by
|
|
57
57
|
`vlm_max_frames`. Replaces the old hard cap of 6 that starved long/merged scenes."""
|
|
58
|
-
spf = float(CONFIG
|
|
59
|
-
ceiling = int(CONFIG
|
|
58
|
+
spf = float(CONFIG["vlm_seconds_per_frame"])
|
|
59
|
+
ceiling = int(CONFIG["vlm_max_frames"])
|
|
60
60
|
return max(3, min(ceiling, round(max(0.0, float(duration)) / spf)))
|
|
61
61
|
|
|
62
62
|
|
|
@@ -64,24 +64,33 @@ def _vlm_scene_cache_path(work_dir):
|
|
|
64
64
|
return Path(work_dir) / "vlm_scene_cache.json"
|
|
65
65
|
|
|
66
66
|
|
|
67
|
-
def _load_vlm_scene_cache(work_dir):
|
|
68
|
-
"""Per-scene VLM resume cache (scene_key -> analysis)
|
|
67
|
+
def _load_vlm_scene_cache(work_dir, settings):
|
|
68
|
+
"""Per-scene VLM resume cache (scene_key -> analysis) written under `settings`.
|
|
69
|
+
|
|
70
|
+
Tolerant: {} if absent/corrupt, and {} when the stored request settings differ, so a
|
|
71
|
+
partial-failure cache is never resumed after an output-affecting setting changed."""
|
|
69
72
|
path = _vlm_scene_cache_path(work_dir)
|
|
70
73
|
if not path.exists():
|
|
71
74
|
return {}
|
|
72
75
|
try:
|
|
73
76
|
data = json.loads(path.read_text(encoding="utf-8"))
|
|
74
|
-
return data if isinstance(data, dict) else {}
|
|
75
77
|
except (OSError, ValueError):
|
|
76
78
|
return {}
|
|
79
|
+
if not isinstance(data, dict) or data.get("settings") != settings:
|
|
80
|
+
return {}
|
|
81
|
+
scenes = data.get("scenes")
|
|
82
|
+
return scenes if isinstance(scenes, dict) else {}
|
|
77
83
|
|
|
78
84
|
|
|
79
|
-
def _flush_vlm_scene_cache(work_dir, cache):
|
|
85
|
+
def _flush_vlm_scene_cache(work_dir, settings, cache):
|
|
80
86
|
"""Persist the resume cache atomically (temp + rename) so an abort/crash never corrupts it."""
|
|
81
87
|
path = _vlm_scene_cache_path(work_dir)
|
|
82
88
|
try:
|
|
83
89
|
tmp = path.with_name(path.name + ".tmp")
|
|
84
|
-
tmp.write_text(
|
|
90
|
+
tmp.write_text(
|
|
91
|
+
json.dumps({"settings": settings, "scenes": cache}, ensure_ascii=False),
|
|
92
|
+
encoding="utf-8",
|
|
93
|
+
)
|
|
85
94
|
tmp.replace(path)
|
|
86
95
|
except OSError as exc:
|
|
87
96
|
log(f"VLM 场景缓存写入失败(忽略): {exc}")
|
|
@@ -114,7 +123,7 @@ def analyze_scenes(scenes, frames, work_dir, *, resume=True):
|
|
|
114
123
|
if not vlm_prompt:
|
|
115
124
|
vlm_prompt = "仔细观察这些视频帧。分两部分输出:\n【描述】不超过80字,描述画面中正在发生什么。\n【深层分析】不超过120字,分析角色情绪、关系动态、潜台词。"
|
|
116
125
|
|
|
117
|
-
ctx = CONFIG
|
|
126
|
+
ctx = CONFIG["context_info"]
|
|
118
127
|
if ctx:
|
|
119
128
|
vlm_prompt = f"已知信息:{ctx}\n\n{vlm_prompt}"
|
|
120
129
|
|
|
@@ -132,7 +141,7 @@ def analyze_scenes(scenes, frames, work_dir, *, resume=True):
|
|
|
132
141
|
# 相邻场景才会复用同一帧,容量取「并发数 × 单场景最大帧数」就够,再多也命中不了。
|
|
133
142
|
b64_capacity = max(
|
|
134
143
|
8,
|
|
135
|
-
int(CONFIG
|
|
144
|
+
int(CONFIG["vlm_max_frames"]) * int(CONFIG["vlm_workers"]),
|
|
136
145
|
)
|
|
137
146
|
b64_cache = OrderedDict()
|
|
138
147
|
b64_lock = Lock()
|
|
@@ -184,7 +193,7 @@ def analyze_scenes(scenes, frames, work_dir, *, resume=True):
|
|
|
184
193
|
payload = {
|
|
185
194
|
"model": CONFIG["vlm_model"],
|
|
186
195
|
"messages": [{"role": "user", "content": content_parts}],
|
|
187
|
-
"max_tokens": int(CONFIG
|
|
196
|
+
"max_tokens": int(CONFIG["vlm_max_tokens"]),
|
|
188
197
|
}
|
|
189
198
|
|
|
190
199
|
log(f"VLM 分析场景 {i+1}/{len(scenes)} ({len(scene_frames)} 帧)...")
|
|
@@ -208,7 +217,7 @@ def analyze_scenes(scenes, frames, work_dir, *, resume=True):
|
|
|
208
217
|
payload = {
|
|
209
218
|
"model": CONFIG["vlm_model"],
|
|
210
219
|
"messages": [{"role": "user", "content": retry_parts}],
|
|
211
|
-
"max_tokens": int(CONFIG
|
|
220
|
+
"max_tokens": int(CONFIG["vlm_max_tokens"]),
|
|
212
221
|
}
|
|
213
222
|
|
|
214
223
|
if not raw_response.strip():
|
|
@@ -230,29 +239,34 @@ def analyze_scenes(scenes, frames, work_dir, *, resume=True):
|
|
|
230
239
|
return i, result
|
|
231
240
|
|
|
232
241
|
# 断点续传:逐场景持久化分析结果,避免少数场景失败(多为 429 限流)时整批画面理解全部作废。
|
|
233
|
-
cache
|
|
234
|
-
|
|
242
|
+
# The resume cache is valid only under the SAME output-affecting settings the outer stage
|
|
243
|
+
# gate tracks (understanding_cache._vlm_cache_payload). vlm_prompt already folds in
|
|
244
|
+
# context_info (and background_research). The request/endpoint settings below are NOT in
|
|
245
|
+
# vlm_prompt, so they are stored explicitly — otherwise a partial-failure cache reused after
|
|
246
|
+
# flipping e.g. mimo_disable_thinking would yield a stale/mixed analysis.
|
|
247
|
+
cache_settings = {
|
|
248
|
+
"vlm_prompt": vlm_prompt,
|
|
249
|
+
"vlm_model": CONFIG.get("vlm_model"),
|
|
250
|
+
"vlm_max_tokens": CONFIG.get("vlm_max_tokens"),
|
|
251
|
+
"vlm_seconds_per_frame": CONFIG.get("vlm_seconds_per_frame"),
|
|
252
|
+
"vlm_max_frames": CONFIG.get("vlm_max_frames"),
|
|
253
|
+
"fps": round(float(fps), 3),
|
|
254
|
+
"frame_time_convention": FRAME_TIME_CONVENTION_VERSION,
|
|
255
|
+
"api_url": CONFIG.get("api_url"),
|
|
256
|
+
"mimo_disable_thinking": CONFIG.get("mimo_disable_thinking", True),
|
|
257
|
+
"mimo_media_resolution": CONFIG.get("mimo_media_resolution"),
|
|
258
|
+
}
|
|
259
|
+
cache = _load_vlm_scene_cache(work_dir, cache_settings) if resume else {}
|
|
235
260
|
|
|
236
261
|
def _scene_cache_key(i, scene):
|
|
237
|
-
# Must invalidate on the SAME output-affecting settings the outer stage gate tracks
|
|
238
|
-
# (understand.py:_vlm_cache_payload). prompt_fp already covers vlm_prompt + context_info
|
|
239
|
-
# (which folds in background_research). The three below change the request/endpoint but
|
|
240
|
-
# are NOT in vlm_prompt, so they must be keyed explicitly — otherwise a partial-failure
|
|
241
|
-
# cache reused after flipping e.g. mimo_disable_thinking yields a stale/mixed analysis.
|
|
242
262
|
return "|".join(str(x) for x in (
|
|
243
263
|
i, round(float(scene["start"]), 3), round(float(scene["end"]), 3),
|
|
244
|
-
CONFIG.get("vlm_model"), prompt_fp, CONFIG.get("vlm_max_tokens"),
|
|
245
|
-
CONFIG.get("vlm_seconds_per_frame"), CONFIG.get("vlm_max_frames"),
|
|
246
|
-
round(float(fps), 3), FRAME_TIME_CONVENTION_VERSION,
|
|
247
|
-
CONFIG.get("api_url"), CONFIG.get("mimo_disable_thinking", True),
|
|
248
|
-
CONFIG.get("mimo_media_resolution"),
|
|
249
264
|
))
|
|
250
265
|
|
|
251
266
|
analyses = [None] * len(scenes)
|
|
252
267
|
todo = []
|
|
253
268
|
for i, s in enumerate(scenes):
|
|
254
|
-
|
|
255
|
-
analyses[i] = cached if isinstance(cached, dict) else None
|
|
269
|
+
analyses[i] = cache.get(_scene_cache_key(i, s))
|
|
256
270
|
if analyses[i] is None:
|
|
257
271
|
todo.append(i)
|
|
258
272
|
if todo and len(todo) < len(scenes):
|
|
@@ -282,7 +296,7 @@ def analyze_scenes(scenes, frames, work_dir, *, resume=True):
|
|
|
282
296
|
cache[_scene_cache_key(idx, scenes[idx])] = result
|
|
283
297
|
pending += 1
|
|
284
298
|
if pending >= flush_every:
|
|
285
|
-
_flush_vlm_scene_cache(work_dir, cache)
|
|
299
|
+
_flush_vlm_scene_cache(work_dir, cache_settings, cache)
|
|
286
300
|
pending = 0
|
|
287
301
|
except Exception as e: # noqa: BLE001 - 单个场景失败不能拖垮其余已完成的
|
|
288
302
|
log(f"VLM 场景 {i+1} 分析失败: {e}")
|
|
@@ -290,10 +304,10 @@ def analyze_scenes(scenes, frames, work_dir, *, resume=True):
|
|
|
290
304
|
finally:
|
|
291
305
|
# 无论正常结束、失败还是中断,都把这一轮已完成的场景落盘。
|
|
292
306
|
if pending:
|
|
293
|
-
_flush_vlm_scene_cache(work_dir, cache)
|
|
307
|
+
_flush_vlm_scene_cache(work_dir, cache_settings, cache)
|
|
294
308
|
return failures
|
|
295
309
|
|
|
296
|
-
base_workers = min(len(todo) or 1, int(CONFIG
|
|
310
|
+
base_workers = min(len(todo) or 1, int(CONFIG["vlm_workers"]))
|
|
297
311
|
log(f"VLM 并行分析 {len(todo)} 个场景 (workers={base_workers})...")
|
|
298
312
|
failures = _run_pass(todo, base_workers)
|
|
299
313
|
if failures:
|
|
@@ -323,7 +337,7 @@ def analyze_scenes(scenes, frames, work_dir, *, resume=True):
|
|
|
323
337
|
|
|
324
338
|
def _video_data_url(video_path):
|
|
325
339
|
"""Return a MiMo-compatible data URL for a local video chunk, or None when too large."""
|
|
326
|
-
max_bytes = int(float(CONFIG
|
|
340
|
+
max_bytes = int(float(CONFIG["mimo_video_base64_max_mb"]) * 1024 * 1024)
|
|
327
341
|
encoded_size = int(video_path.stat().st_size * 4 / 3) + 128
|
|
328
342
|
if encoded_size > max_bytes:
|
|
329
343
|
log(
|
|
@@ -342,18 +356,15 @@ def _mimo_video_chunks(scenes):
|
|
|
342
356
|
if not scenes:
|
|
343
357
|
raise RuntimeError("MiMo 视频分片理解需要 scenes;请先运行 ffmpeg scene/scdet 场景检测")
|
|
344
358
|
|
|
345
|
-
max_seconds = float(CONFIG
|
|
346
|
-
min_seconds = float(CONFIG
|
|
359
|
+
max_seconds = float(CONFIG["mimo_video_chunk_max_seconds"])
|
|
360
|
+
min_seconds = float(CONFIG["mimo_video_chunk_min_seconds"])
|
|
347
361
|
chunks = []
|
|
348
362
|
for scene_index, scene in enumerate(scenes):
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
end = float(scene.get("end", start))
|
|
352
|
-
except (TypeError, ValueError, AttributeError):
|
|
353
|
-
continue
|
|
363
|
+
start = float(scene["start"])
|
|
364
|
+
end = float(scene["end"])
|
|
354
365
|
if end <= start:
|
|
355
366
|
continue
|
|
356
|
-
scene_id = scene.get("scene_id", scene_index)
|
|
367
|
+
scene_id = scene.get("scene_id", scene_index)
|
|
357
368
|
cursor = start
|
|
358
369
|
while cursor < end:
|
|
359
370
|
chunk_end = min(end, cursor + max_seconds)
|
|
@@ -376,7 +387,7 @@ def _extract_video_chunk(video_path, chunk, output_path):
|
|
|
376
387
|
"""Cut one scene-based chunk into a compact local MP4 for MiMo video_url data URL."""
|
|
377
388
|
start = float(chunk["start"])
|
|
378
389
|
duration = max(0.1, float(chunk["end"]) - start)
|
|
379
|
-
fps = float(CONFIG
|
|
390
|
+
fps = float(CONFIG["mimo_video_fps"])
|
|
380
391
|
cmd = [
|
|
381
392
|
"ffmpeg", "-y",
|
|
382
393
|
"-ss", f"{start:.3f}",
|
|
@@ -392,7 +403,7 @@ def _extract_video_chunk(video_path, chunk, output_path):
|
|
|
392
403
|
"-movflags", "+faststart",
|
|
393
404
|
str(output_path),
|
|
394
405
|
]
|
|
395
|
-
result = run_cmd(cmd, timeout=CONFIG
|
|
406
|
+
result = run_cmd(cmd, timeout=CONFIG["mimo_video_chunk_timeout"])
|
|
396
407
|
if result.returncode != 0:
|
|
397
408
|
raise RuntimeError(f"MiMo 视频分片裁剪失败: {result.stderr[-500:]}")
|
|
398
409
|
return output_path
|
|
@@ -402,7 +413,7 @@ def _mimo_chunk_prompt(chunk):
|
|
|
402
413
|
return (
|
|
403
414
|
f"这是原视频 {chunk['start']:.1f}s-{chunk['end']:.1f}s 的场景分片,"
|
|
404
415
|
f"scene_id={chunk['scene_id']}。"
|
|
405
|
-
f"{CONFIG
|
|
416
|
+
f"{CONFIG['mimo_video_prompt']}"
|
|
406
417
|
)
|
|
407
418
|
|
|
408
419
|
|
|
@@ -411,7 +422,7 @@ def _mimo_video_model():
|
|
|
411
422
|
return CONFIG.get("mimo_video_model") or CONFIG.get("mimo_model") or CONFIG["vlm_model"]
|
|
412
423
|
|
|
413
424
|
|
|
414
|
-
def
|
|
425
|
+
def mimo_video_settings():
|
|
415
426
|
"""Return non-secret MiMo video-overview settings that affect generated content."""
|
|
416
427
|
return {
|
|
417
428
|
"model": _mimo_video_model(),
|
|
@@ -434,19 +445,9 @@ def _mimo_chunk_cache_key(chunk):
|
|
|
434
445
|
)
|
|
435
446
|
|
|
436
447
|
|
|
437
|
-
def _mimo_cached_chunks_fingerprint(done):
|
|
438
|
-
return stable_hash(done)
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
def _mimo_overview_payload_fingerprint(overview):
|
|
442
|
-
payload = dict(overview)
|
|
443
|
-
payload.pop("overview_fingerprint", None)
|
|
444
|
-
return stable_hash(payload)
|
|
445
|
-
|
|
446
|
-
|
|
447
448
|
def _mimo_partial_provenance(video_path, scenes):
|
|
448
449
|
return {
|
|
449
|
-
"
|
|
450
|
+
"source_video_identity": file_identity(video_path),
|
|
450
451
|
"chunks": [_mimo_chunk_cache_key(chunk) for chunk in _mimo_video_chunks(scenes)],
|
|
451
452
|
}
|
|
452
453
|
|
|
@@ -465,7 +466,7 @@ def _load_mimo_partial(partial_path, video_path=None, scenes=None):
|
|
|
465
466
|
return {}
|
|
466
467
|
if not isinstance(partial, dict):
|
|
467
468
|
return {}
|
|
468
|
-
if partial.get("settings") !=
|
|
469
|
+
if partial.get("settings") != mimo_video_settings():
|
|
469
470
|
return {}
|
|
470
471
|
if video_path is not None or scenes is not None:
|
|
471
472
|
try:
|
|
@@ -474,20 +475,14 @@ def _load_mimo_partial(partial_path, video_path=None, scenes=None):
|
|
|
474
475
|
except (OSError, RuntimeError, TypeError, ValueError):
|
|
475
476
|
return {}
|
|
476
477
|
done = partial.get("chunks")
|
|
477
|
-
if
|
|
478
|
-
return {}
|
|
479
|
-
recorded = partial.get("chunks_fingerprint")
|
|
480
|
-
if not recorded or recorded != _mimo_cached_chunks_fingerprint(done):
|
|
481
|
-
return {}
|
|
482
|
-
return done
|
|
478
|
+
return done if isinstance(done, dict) else {}
|
|
483
479
|
|
|
484
480
|
|
|
485
481
|
def _save_mimo_partial(partial_path, done, video_path=None, scenes=None):
|
|
486
482
|
"""Persist completed chunk results incrementally so paid chunks survive a mid-loop failure."""
|
|
487
483
|
payload = {
|
|
488
|
-
"settings":
|
|
484
|
+
"settings": mimo_video_settings(),
|
|
489
485
|
"chunks": done,
|
|
490
|
-
"chunks_fingerprint": _mimo_cached_chunks_fingerprint(done),
|
|
491
486
|
}
|
|
492
487
|
if video_path is not None or scenes is not None:
|
|
493
488
|
payload["provenance"] = _mimo_partial_provenance(video_path, scenes)
|
|
@@ -507,7 +502,7 @@ def _mimo_chunks_match(cached_chunks, expected_chunks):
|
|
|
507
502
|
|
|
508
503
|
def mimo_video_overview_cache_fresh(overview_path, video_path, scenes):
|
|
509
504
|
"""Return True only when the final MiMo overview matches current inputs/settings."""
|
|
510
|
-
overview_path = Path(overview_path)
|
|
505
|
+
overview_path = Path(overview_path)
|
|
511
506
|
if not overview_path.exists():
|
|
512
507
|
return False
|
|
513
508
|
try:
|
|
@@ -516,24 +511,16 @@ def mimo_video_overview_cache_fresh(overview_path, video_path, scenes):
|
|
|
516
511
|
return False
|
|
517
512
|
if not isinstance(overview, dict) or overview.get("input") != "scene_chunks":
|
|
518
513
|
return False
|
|
519
|
-
if overview.get("settings") !=
|
|
520
|
-
return False
|
|
521
|
-
overview_fingerprint = overview.get("overview_fingerprint")
|
|
522
|
-
if not overview_fingerprint or overview_fingerprint != _mimo_overview_payload_fingerprint(overview):
|
|
523
|
-
return False
|
|
524
|
-
recorded = overview.get("chunks_fingerprint")
|
|
525
|
-
if not recorded:
|
|
514
|
+
if overview.get("settings") != mimo_video_settings():
|
|
526
515
|
return False
|
|
527
516
|
chunks = overview.get("chunks")
|
|
528
517
|
if not isinstance(chunks, list) or not all(
|
|
529
518
|
isinstance(chunk, dict) and _is_mimo_chunk_usable(chunk.get("content"))
|
|
530
519
|
for chunk in chunks
|
|
531
520
|
):
|
|
532
|
-
return False
|
|
533
|
-
if recorded != _mimo_cached_chunks_fingerprint(chunks):
|
|
534
|
-
return False
|
|
521
|
+
return False # a moderation-rejected chunk is retried, never served from cache
|
|
535
522
|
try:
|
|
536
|
-
if overview.get("
|
|
523
|
+
if overview.get("source_video_identity") != file_identity(video_path):
|
|
537
524
|
return False
|
|
538
525
|
expected_chunks = _mimo_video_chunks(scenes)
|
|
539
526
|
except (OSError, RuntimeError, TypeError, ValueError):
|
|
@@ -550,8 +537,8 @@ def _analyze_mimo_video_chunk(chunk_path, chunk):
|
|
|
550
537
|
{
|
|
551
538
|
"type": "video_url",
|
|
552
539
|
"video_url": {"url": video_url},
|
|
553
|
-
"fps": CONFIG
|
|
554
|
-
"media_resolution": CONFIG
|
|
540
|
+
"fps": CONFIG["mimo_video_fps"],
|
|
541
|
+
"media_resolution": CONFIG["mimo_media_resolution"],
|
|
555
542
|
},
|
|
556
543
|
{"type": "text", "text": _mimo_chunk_prompt(chunk)},
|
|
557
544
|
]
|
|
@@ -596,9 +583,9 @@ def _is_mimo_chunk_usable(content):
|
|
|
596
583
|
|
|
597
584
|
def analyze_video_overview(video_path, work_dir, scenes=None):
|
|
598
585
|
"""Use MiMo video understanding over local ffmpeg scene chunks."""
|
|
599
|
-
if not CONFIG
|
|
586
|
+
if not CONFIG["mimo_video_overview"]:
|
|
600
587
|
return None
|
|
601
|
-
if not CONFIG
|
|
588
|
+
if not CONFIG["mimo_video_api_key"]:
|
|
602
589
|
log("MiMo 视频概览已启用,但未设置 MIMO_VIDEO_API_KEY/MIMO_API_KEY,跳过")
|
|
603
590
|
return None
|
|
604
591
|
|
|
@@ -613,20 +600,16 @@ def analyze_video_overview(video_path, work_dir, scenes=None):
|
|
|
613
600
|
log(f"MiMo 视频理解:按 ffmpeg scene 分片分析 {len(chunks)} 段...")
|
|
614
601
|
chunk_results = []
|
|
615
602
|
unusable_chunks = []
|
|
616
|
-
removed_stale_cache = False
|
|
617
603
|
for chunk in chunks:
|
|
618
604
|
cache_key = _mimo_chunk_cache_key(chunk)
|
|
619
605
|
cached = done.get(cache_key)
|
|
620
|
-
if cached is not None:
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
continue
|
|
628
|
-
done.pop(cache_key, None)
|
|
629
|
-
removed_stale_cache = True
|
|
606
|
+
if cached is not None: # the partial only ever stores usable chunks
|
|
607
|
+
log(
|
|
608
|
+
f" MiMo 分片 {chunk['chunk_id'] + 1}/{len(chunks)}: "
|
|
609
|
+
f"{chunk['start']:.1f}-{chunk['end']:.1f}s(命中增量缓存,跳过)"
|
|
610
|
+
)
|
|
611
|
+
chunk_results.append(cached)
|
|
612
|
+
continue
|
|
630
613
|
chunk_path = chunks_dir / (
|
|
631
614
|
f"chunk_{chunk['chunk_id']:03d}_scene_{chunk['scene_id']}_"
|
|
632
615
|
f"{chunk['start']:.2f}-{chunk['end']:.2f}.mp4"
|
|
@@ -637,7 +620,7 @@ def analyze_video_overview(video_path, work_dir, scenes=None):
|
|
|
637
620
|
f"{chunk['start']:.1f}-{chunk['end']:.1f}s"
|
|
638
621
|
)
|
|
639
622
|
chunk_result = _analyze_mimo_video_chunk(chunk_path, chunk)
|
|
640
|
-
if _is_mimo_chunk_usable(chunk_result
|
|
623
|
+
if _is_mimo_chunk_usable(chunk_result["content"]):
|
|
641
624
|
chunk_results.append(chunk_result)
|
|
642
625
|
done[cache_key] = chunk_result
|
|
643
626
|
_save_mimo_partial(partial_path, done, video_path, scenes)
|
|
@@ -645,15 +628,9 @@ def analyze_video_overview(video_path, work_dir, scenes=None):
|
|
|
645
628
|
unusable_chunks.append(chunk)
|
|
646
629
|
log(f" MiMo 分片 {chunk['chunk_id'] + 1}: 未返回有效内容,保留为待重试")
|
|
647
630
|
|
|
648
|
-
if removed_stale_cache:
|
|
649
|
-
_save_mimo_partial(partial_path, done, video_path, scenes)
|
|
650
|
-
|
|
651
631
|
if not chunk_results:
|
|
652
632
|
log(f"MiMo 视频概览:{len(chunks)} 段均无有效内容(疑似被内容审核拦截),跳过概览")
|
|
653
|
-
|
|
654
|
-
partial_path.unlink()
|
|
655
|
-
except OSError:
|
|
656
|
-
pass
|
|
633
|
+
partial_path.unlink(missing_ok=True)
|
|
657
634
|
return None
|
|
658
635
|
|
|
659
636
|
if unusable_chunks:
|
|
@@ -680,23 +657,18 @@ def analyze_video_overview(video_path, work_dir, scenes=None):
|
|
|
680
657
|
"content": content,
|
|
681
658
|
"chunks": chunk_results,
|
|
682
659
|
"chunk_count": len(chunk_results),
|
|
683
|
-
"fps": CONFIG
|
|
684
|
-
"media_resolution": CONFIG
|
|
660
|
+
"fps": CONFIG["mimo_video_fps"],
|
|
661
|
+
"media_resolution": CONFIG["mimo_media_resolution"],
|
|
685
662
|
"input": "scene_chunks",
|
|
686
663
|
"partial": bool(unusable_chunks),
|
|
687
664
|
"unusable_chunk_count": len(unusable_chunks),
|
|
688
|
-
"
|
|
689
|
-
"chunk_max_seconds": CONFIG
|
|
690
|
-
"settings":
|
|
691
|
-
"chunks_fingerprint": _mimo_cached_chunks_fingerprint(chunk_results),
|
|
665
|
+
"source_video_identity": file_identity(video_path),
|
|
666
|
+
"chunk_max_seconds": CONFIG["mimo_video_chunk_max_seconds"],
|
|
667
|
+
"settings": mimo_video_settings(),
|
|
692
668
|
}
|
|
693
|
-
overview["overview_fingerprint"] = _mimo_overview_payload_fingerprint(overview)
|
|
694
669
|
overview_path = work_dir / "mimo_video_overview.json"
|
|
695
670
|
overview_path.write_text(json.dumps(overview, ensure_ascii=False, indent=2), encoding="utf-8")
|
|
696
671
|
# 所有分片完成后清理增量缓存,保持 work_dir 仅有规范产物
|
|
697
|
-
|
|
698
|
-
partial_path.unlink()
|
|
699
|
-
except OSError:
|
|
700
|
-
pass
|
|
672
|
+
partial_path.unlink(missing_ok=True)
|
|
701
673
|
log(f"MiMo 分片视频概览完成: {overview_path}")
|
|
702
674
|
return overview
|
|
@@ -3,9 +3,9 @@ name: video-voiceover
|
|
|
3
3
|
user-invocable: false
|
|
4
4
|
description: >
|
|
5
5
|
把带时间戳的 narration.json 合成为中文解说音频。使用 MiMo TTS(mimo-v2.5-tts)或
|
|
6
|
-
Fish Audio(s2.1-pro-free
|
|
6
|
+
Fish Audio(s2.1-pro-free)或显式配置的通用 IndexTTS HTTP 服务逐段生成语音,
|
|
7
7
|
按时间窗动态适配语速并处理响度;输入输出时间线上的旁白,产出 tts_segments 与 tts_meta.json。
|
|
8
|
-
|
|
8
|
+
触发词:配音、语音合成、TTS、解说配音、
|
|
9
9
|
voiceover、text to speech、旁白配音。
|
|
10
10
|
---
|
|
11
11
|
|
|
@@ -23,51 +23,61 @@ export MIMO_API_KEY=*** # 也可使用仅供 TTS 的 MIMO_TTS_API_KEY
|
|
|
23
23
|
export TTS_PROVIDER=fish-audio
|
|
24
24
|
export FISH_API_KEY=***
|
|
25
25
|
export FISH_TTS_REFERENCE_ID=<voice-model-id> # 可选;覆盖内置“娱乐扒妹”音色
|
|
26
|
+
|
|
27
|
+
# 或显式选择自托管 index-tts 端点,配置见 references/index-tts.md
|
|
28
|
+
export TTS_PROVIDER=index-tts
|
|
26
29
|
```
|
|
27
30
|
|
|
28
31
|
下面的 `scripts/...` 均相对于本技能目录。若执行器从仓库根目录启动,请给脚本路径加上本技能的绝对目录。
|
|
29
|
-
脚本不从其他技能目录读取文件;外部输入仅限命令显式传入的稿件、音频、参数与 `work_dir` 产物。
|
|
30
32
|
|
|
31
33
|
## 3. 输入契约
|
|
32
34
|
|
|
33
35
|
默认输入为 `work_dir/narration.json`。每段必须包含 `start`、`end` 与 `narration`,可选字段包括
|
|
34
36
|
`pause_after_ms` 和 `overlaps_speech`。时间统一表示音频最终放置的**输出时间线秒数**。
|
|
35
37
|
|
|
36
|
-
|
|
37
|
-
`narration_mapped.json`。
|
|
38
|
+
cut 流程先剪后配:`narration.json` 本身就是按剪后成片的输出时间写的,不存在另一份映射稿。
|
|
38
39
|
|
|
39
40
|
## 4. 运行命令
|
|
40
41
|
|
|
41
42
|
```bash
|
|
42
43
|
python3 scripts/voiceover.py --work-dir <work_dir> --narration <narration.json> \
|
|
43
|
-
[--tts-provider auto|mimo-tts|fish-audio] \
|
|
44
|
-
[--mimo-voice 冰糖 | --voice-ref <reference-audio>]
|
|
44
|
+
[--tts-provider auto|mimo-tts|fish-audio|index-tts] \
|
|
45
|
+
[--mimo-voice 冰糖 | --voice-ref <reference-audio>] \
|
|
46
|
+
[--preserve-approved-text]
|
|
45
47
|
```
|
|
46
48
|
|
|
47
|
-
单独运行且省略 `--narration` 时,默认读取 `work_dir/narration.json
|
|
48
|
-
|
|
49
|
-
```bash
|
|
50
|
-
python3 scripts/voiceover.py --work-dir <work_dir> \
|
|
51
|
-
--narration <work_dir/narration_mapped.json>
|
|
52
|
-
```
|
|
49
|
+
单独运行且省略 `--narration` 时,默认读取 `work_dir/narration.json`;`--narration` 只用于指定其他路径的同格式稿件。
|
|
53
50
|
|
|
54
51
|
## 5. 输出契约
|
|
55
52
|
|
|
56
53
|
- `tts_segments/*.wav`:每段旁白对应一个音频文件。
|
|
57
|
-
- `tts_meta.json`:包含 `segments`、`engine
|
|
54
|
+
- `tts_meta.json`:包含 `segments`、`engine`、`voice`(实际使用的 provider、模型、音色或参考音频)与 `narration`。每段记录 `audio_path`、时间、
|
|
58
55
|
`pause_after_ms` 和放置字段。
|
|
59
56
|
- 干净运行写入 `partial: false` 与 `failures: []`。
|
|
60
57
|
- 使用 `--allow-partial-tts` 跳过失败段时,写入 `partial: true` 和
|
|
61
58
|
`failures: [{index,start,end,text,error}]`,让缺失语音保持可见。
|
|
59
|
+
- `--preserve-approved-text` 是显式的批准稿保护策略。每段保留原始 `authored_text`
|
|
60
|
+
证据;TTS 实际读取的 `spoken_text` 只经过既有的格式/舞台提示清理。若完整语音超过时间窗及
|
|
61
|
+
累计语速预算,命令失败并报告段序号、原稿、实读文本、语音时长和窗口证据,不写成功的
|
|
62
|
+
`tts_meta.json`。严格模式下任何必需段失败(包括供应商失败)都不能被
|
|
63
|
+
`--allow-partial-tts` 降级为可交付的部分成功;异常记录标为 `required: true` 并带策略 ID。
|
|
64
|
+
仅含 `[停顿]` 等清理标记、清理后无实读文本的作者段也属于必需段错误。
|
|
62
65
|
|
|
63
66
|
## 6. 运行规则
|
|
64
67
|
|
|
65
|
-
-
|
|
68
|
+
- 重跑只复用分段 sidecar 中记录的文本、TTS 设置与该 WAV 的 `size`/`mtime_ns` 均与当前相等的分段音频;
|
|
69
|
+
修改旁白或合成参数后,只重生成受影响的 WAV。
|
|
70
|
+
- 批准稿保护策略属于缓存设置:严格模式不会命中旧的自动缩稿缓存;只有同一严格策略下、
|
|
71
|
+
`spoken_text` 完整匹配且 WAV 存在非空的缓存才可离线复用。
|
|
72
|
+
- 严格 CLI 在本轮合成前把旧 `tts_meta.json` 按时间戳归档至 `tts_meta.history/`,因此失败时
|
|
73
|
+
当前路径不会继续冒充本轮成功;成功元数据通过同目录临时文件原子替换。
|
|
66
74
|
- `auto` 优先使用已配置的 MiMo,MiMo key 缺失且设置了 `FISH_API_KEY` 时使用 Fish Audio;需要可复现的 provider 选择时显式传 `--tts-provider`。
|
|
75
|
+
- 自托管 index-tts 端点只能由 `--tts-provider index-tts` 或 `TTS_PROVIDER=index-tts` 显式选择,`auto`
|
|
76
|
+
永不兜底选择它。协议、请求体、receipt 语义与缓存失效规则见 `references/index-tts.md`。
|
|
67
77
|
- Fish Audio 直接请求 WAV;默认使用“娱乐扒妹”音色(`5653cea4ac83480aaf2bf45406556185`),`FISH_TTS_REFERENCE_ID` 可覆盖。模型、音色 ID、API URL、动态语速或归一化设置变化时会重新生成缓存。当前免费模型无 SLA,受 Fair Use 和官方免费期限约束。
|
|
68
78
|
- `--voice-ref` 仅用于 full/cut 解说克隆,切换到 `mimo-v2.5-tts-voiceclone`。仅在确需新合成时惰性规范化一次;
|
|
69
|
-
- dub voiceclone 原始 WAV
|
|
70
|
-
|
|
79
|
+
- dub voiceclone 原始 WAV 也会按模型、提示、台词和参考音频的 `size`/`mtime_ns` 缓存;匹配重跑不再重复请求或计费,`dub_manifest.json` 逐行记录 `tts_cache=hit|miss`;
|
|
80
|
+
参考音频文件变化会使旧缓存失效。仅在获得授权后使用,参考音频会发送到 MiMo。
|
|
71
81
|
- `TTS_WORKERS`、`TTS_TIMEOUT`、`TTS_RETRIES`、`ALLOW_PARTIAL_TTS` 用于调整并发、超时、重试与部分成功策略。
|
|
72
82
|
- dub 模式有独立的确定性门禁:`dub_lint.json` 会在语音克隆前阻止空行、重叠或越界译文;
|
|
73
83
|
`dub_review.json` 用于记录忠实度、语气、时长和平台适配复核。可通过
|
|
@@ -75,7 +85,7 @@ python3 scripts/voiceover.py --work-dir <work_dir> \
|
|
|
75
85
|
|
|
76
86
|
## 7. 能力边界
|
|
77
87
|
|
|
78
|
-
-
|
|
88
|
+
- 超窗时默认在句界自动缩稿并在 `spoken_text/truncated` 留痕;批准稿加 `--preserve-approved-text`。
|
|
79
89
|
- 不混流、不压低原声、不渲染字幕。
|
|
80
90
|
- 不分析视频,也不选择时间点;只为输入稿件中的既定分段配音。
|
|
81
|
-
- Fish Audio
|
|
91
|
+
- Fish Audio 与 IndexTTS 路径都不接受本地 `--voice-ref`;前者用已创建的 `FISH_TTS_REFERENCE_ID` 选择音色。
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# 自托管 IndexTTS 端点
|
|
2
|
+
|
|
3
|
+
`--tts-provider index-tts` 或 `TTS_PROVIDER=index-tts` 显式选择自托管的 index-tts 协议服务(JSON→WAV)。
|
|
4
|
+
`auto` 永不兜底选择它。
|
|
5
|
+
|
|
6
|
+
## 配置
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
export TTS_PROVIDER=index-tts
|
|
10
|
+
export INDEX_TTS_ENDPOINT=http://127.0.0.1:<port>/tts
|
|
11
|
+
export INDEX_TTS_VOICE=<authorized-voice-name>
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
- 缺 `INDEX_TTS_ENDPOINT` 或 `INDEX_TTS_VOICE` 时在缓存/请求前失败。
|
|
15
|
+
- endpoint 只接受无 userinfo/query/fragment 的 HTTP(S) URL,且请求禁止重定向,避免把批准文本转发至其他主机。
|
|
16
|
+
- 不接受 `--voice-ref` / `--mimo-voice`。
|
|
17
|
+
|
|
18
|
+
## 请求契约
|
|
19
|
+
|
|
20
|
+
请求体固定为 `{"voice": voice, "text": spoken_text}`。当前段 schema 的可选 `emotion`
|
|
21
|
+
以及额外 style、非默认 rate/pitch 控制会显式失败,不会静默忽略。原始合成使用 provider 默认速度,
|
|
22
|
+
不按标点、长度或位置自动计算 rate;这不等于下游放置阶段的端到端 tempo 锁。
|
|
23
|
+
|
|
24
|
+
## Receipt 与缓存
|
|
25
|
+
|
|
26
|
+
每段的 `provider_receipt` 只记录 `provider` 与“请求的 voice”(`requested_voice`)。它说明请求参数,
|
|
27
|
+
不是该声线的声学验证,也不代表已做人耳听审或音色身份验证。endpoint 不写入任何文件。
|
|
28
|
+
|
|
29
|
+
缓存复用条件:sidecar 中的文本与设置(含 `index_tts_voice`)与当前相等、WAV 的 `size`/`mtime_ns` 未变,
|
|
30
|
+
且 sidecar 的 receipt 对应当前 voice;缓存命中复用 sidecar 中的 receipt,不能现场补造。
|
|
31
|
+
服务端模型或部署变化不会被自动察觉;更换声线实现后请删除 `tts_segments/*.cache.json` 强制重合成。
|