dsh-hyperframes 0.3.1 → 0.4.0
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.en.md +6 -5
- package/README.md +7 -6
- package/lib/index.d.ts +1 -1
- package/lib/index.js +1 -0
- package/package.json +63 -63
- package/skills/embedded-captions/SKILL.md +28 -3
- package/skills/embedded-captions/references/aesthetic-principles.md +2 -3
- package/skills/embedded-captions/references/rail.md +1 -1
- package/skills/embedded-captions/scripts/check-occlusion.cjs +4 -1
- package/skills/embedded-captions/scripts/check-overflow.cjs +1 -0
- package/skills/embedded-captions/scripts/check-rail-climax.cjs +5 -1
- package/skills/embedded-captions/scripts/fixtures/heroless/theme.json +11 -0
- package/skills/embedded-captions/scripts/hf-cli.cjs +33 -0
- package/skills/embedded-captions/scripts/make-theme.cjs +95 -57
- package/skills/embedded-captions/scripts/make-theme.test.mjs +131 -0
- package/skills/embedded-captions/scripts/matte.cjs +1 -13
- package/skills/embedded-captions/scripts/measure-layout.cjs +2 -1
- package/skills/embedded-captions/scripts/preview-frames.cjs +39 -11
- package/skills/embedded-captions/scripts/preview-frames.test.mjs +24 -0
- package/skills/embedded-captions/scripts/render-and-composite.sh +8 -52
- package/skills/embedded-captions/scripts/safe-zones.cjs +4 -1
- package/skills/embedded-captions/scripts/transcribe.cjs +5 -14
- package/skills/embedded-captions/scripts/transcribe.test.mjs +41 -0
- package/skills/faceless-explainer/SKILL.md +20 -16
- package/skills/faceless-explainer/references/story-design.md +1 -1
- package/skills/faceless-explainer/scripts/assemble-index.mjs +6 -4
- package/skills/faceless-explainer/scripts/assemble-index.test.mjs +57 -2
- package/skills/faceless-explainer/scripts/audio.mjs +2 -0
- package/skills/faceless-explainer/scripts/build-frame.mjs +6 -3
- package/skills/faceless-explainer/scripts/captions.mjs +7 -4
- package/skills/faceless-explainer/scripts/frame-packets.mjs +4 -4
- package/skills/faceless-explainer/scripts/lib/bgm-volume.mjs +4 -0
- package/skills/faceless-explainer/scripts/lib/captured-fonts.mjs +58 -0
- package/skills/{hyperframes-core → faceless-explainer}/scripts/lib/frame-packets-core.mjs +5 -23
- package/skills/faceless-explainer/scripts/lib/storyboard.mjs +1 -1
- package/skills/faceless-explainer/scripts/lib/transitions.json +3 -3
- package/skills/faceless-explainer/scripts/transitions.mjs +1 -0
- package/skills/faceless-explainer/scripts/transitions.test.mjs +2 -0
- package/skills/faceless-explainer/sub-agents/frame-worker.md +1 -1
- package/skills/figma/SKILL.md +2 -0
- package/skills/general-video/SKILL.md +13 -11
- package/skills/general-video/scripts/frame-packets.mjs +4 -4
- package/skills/general-video/scripts/lib/frame-packets-core.mjs +194 -0
- package/skills/general-video/sub-agents/frame-worker.md +1 -1
- package/skills/hyperframes/SKILL.md +40 -30
- package/skills/{hyperframes-core → hyperframes}/references/brief-contract.md +15 -15
- package/skills/{hyperframes-core → hyperframes}/references/brief-format.md +8 -8
- package/skills/hyperframes/references/capability-menu.md +2 -2
- package/skills/{hyperframes-core → hyperframes}/references/frame-worker-core.md +1 -1
- package/skills/hyperframes/references/intent-interview.md +16 -16
- package/skills/hyperframes/references/plugin-installation.md +43 -0
- package/skills/{hyperframes-core → hyperframes}/references/production-loop.md +11 -11
- package/skills/hyperframes/references/review-loop.md +37 -0
- package/skills/hyperframes/references/route-briefs.md +1 -1
- package/skills/hyperframes/references/routes/motion-graphics.md +1 -1
- package/skills/{hyperframes-core → hyperframes}/references/script-format.md +3 -3
- package/skills/hyperframes/references/skill-lifecycle.md +2 -0
- package/skills/{hyperframes-core → hyperframes}/references/storyboard-format.md +3 -34
- package/skills/hyperframes/scripts/lib/frame-packets-core.mjs +194 -0
- package/skills/hyperframes/scripts/plugin-cli.mjs +88 -0
- package/skills/hyperframes/scripts/plugin-cli.test.mjs +117 -0
- package/skills/hyperframes-animation/SKILL.md +5 -1
- package/skills/hyperframes-animation/adapters/gsap.md +1 -1
- package/skills/hyperframes-animation/adapters/lottie.md +15 -2
- package/skills/hyperframes-animation/adapters/three.md +14 -0
- package/skills/hyperframes-animation/blueprints/titlecard-reveal.md +1 -1
- package/skills/hyperframes-animation/references/motion-blur.md +133 -0
- package/skills/hyperframes-animation/rules/cursor-click-ripple.md +1 -1
- package/skills/hyperframes-animation/rules/dynamic-content-sequencing.md +1 -1
- package/skills/hyperframes-animation/rules/hacker-flip-3d.md +1 -1
- package/skills/hyperframes-animation/rules/kinetic-beat-slam.md +2 -2
- package/skills/hyperframes-animation/rules/motion-blur-streak.md +1 -1
- package/skills/hyperframes-animation/rules/vertical-spring-ticker.md +1 -1
- package/skills/hyperframes-animation/scripts/package-loader.mjs +47 -16
- package/skills/hyperframes-animation/scripts/package-loader.test.mjs +52 -0
- package/skills/hyperframes-animation/techniques.md +1 -1
- package/skills/hyperframes-animation/transitions/TRANSITION-REGISTRY.md +3 -3
- package/skills/hyperframes-animation/transitions/css-push.md +4 -4
- package/skills/hyperframes-audio/SKILL.md +30 -22
- package/skills/hyperframes-audio/scripts/carve.mjs +6 -3
- package/skills/hyperframes-audio/scripts/carve.test.mjs +16 -1
- package/skills/hyperframes-cli/SKILL.md +44 -35
- package/skills/hyperframes-cli/references/init-and-scaffold.md +8 -6
- package/skills/hyperframes-cli/references/lint-validate-inspect.md +3 -3
- package/skills/hyperframes-cli/references/preview-render.md +35 -32
- package/skills/hyperframes-cli/references/upgrade-info-misc.md +50 -1
- package/skills/hyperframes-core/SKILL.md +30 -24
- package/skills/hyperframes-core/references/composition-patterns.md +3 -42
- package/skills/hyperframes-core/references/creator-editing-recipes.md +131 -7
- package/skills/hyperframes-core/references/data-attributes.md +11 -11
- package/skills/hyperframes-core/references/determinism-rules.md +2 -19
- package/skills/hyperframes-core/references/full-screen-motion.md +0 -9
- package/skills/hyperframes-core/references/minimal-composition.md +2 -2
- package/skills/hyperframes-core/references/sub-compositions.md +3 -66
- package/skills/hyperframes-core/references/tailwind.md +1 -20
- package/skills/hyperframes-core/references/tracks-and-clips.md +2 -6
- package/skills/hyperframes-core/references/variables-and-media.md +3 -3
- package/skills/hyperframes-creative/SKILL.md +2 -0
- package/skills/hyperframes-creative/references/house-style.md +15 -15
- package/skills/hyperframes-creative/references/story-spine.md +3 -1
- package/skills/hyperframes-creative/references/storyboard-recipe.md +57 -0
- package/skills/hyperframes-creative/references/typography.md +1 -1
- package/skills/hyperframes-creative/scripts/package-loader.mjs +47 -16
- package/skills/hyperframes-creative/scripts/package-loader.test.mjs +52 -0
- package/skills/hyperframes-keyframes/SKILL.md +4 -2
- package/skills/hyperframes-registry/SKILL.md +3 -1
- package/skills/hyperframes-registry/references/contributing.md +8 -2
- package/skills/hyperframes-registry/references/discovery.md +20 -34
- package/skills/hyperframes-studio/SKILL.md +77 -0
- package/skills/media-use/SKILL.md +14 -12
- package/skills/media-use/audio/references/captions/authoring.md +4 -0
- package/skills/media-use/audio/references/remove-background.md +1 -1
- package/skills/media-use/audio/references/tts-to-captions.md +11 -2
- package/skills/media-use/audio/references/tts.md +84 -6
- package/skills/media-use/audio/scripts/audio.mjs +15 -6
- package/skills/media-use/audio/scripts/gemini-pipeline.test.mjs +113 -0
- package/skills/media-use/audio/scripts/lib/audio-meta.mjs +39 -0
- package/skills/media-use/audio/scripts/lib/audio-meta.test.mjs +136 -0
- package/skills/media-use/audio/scripts/lib/bgm-volume.mjs +4 -0
- package/skills/media-use/audio/scripts/lib/bgm.mjs +2 -6
- package/skills/media-use/audio/scripts/lib/gemini-auth.mjs +43 -0
- package/skills/media-use/audio/scripts/lib/gemini-auth.py +52 -0
- package/skills/media-use/audio/scripts/lib/gemini-auth.test.mjs +86 -0
- package/skills/media-use/audio/scripts/lib/gemini-auth_test.py +90 -0
- package/skills/media-use/audio/scripts/lib/gemini-tts.mjs +122 -0
- package/skills/media-use/audio/scripts/lib/gemini-tts.test.mjs +243 -0
- package/skills/media-use/audio/scripts/lib/heygen.mjs +2 -1
- package/skills/media-use/audio/scripts/lib/tts.mjs +15 -3
- package/skills/media-use/audio/scripts/wait-bgm.mjs +8 -4
- package/skills/media-use/audio/scripts/wait-bgm.test.mjs +65 -0
- package/skills/media-use/references/audio.md +3 -2
- package/skills/media-use/references/grading.md +7 -7
- package/skills/media-use/references/media-treatment-recipes.md +1 -1
- package/skills/media-use/references/memory.md +2 -2
- package/skills/media-use/references/meta.md +2 -2
- package/skills/media-use/references/operations.md +15 -13
- package/skills/media-use/references/resolve.md +22 -11
- package/skills/media-use/references/setup-providers.md +11 -11
- package/skills/media-use/scripts/audio-duck.mjs +19 -27
- package/skills/media-use/scripts/compatibility.test.mjs +96 -0
- package/skills/media-use/scripts/lib/config-lock.mjs +45 -0
- package/skills/media-use/scripts/lib/duck.mjs +16 -10
- package/skills/media-use/scripts/lib/media-fetch.mjs +85 -0
- package/skills/media-use/scripts/lib/media-home.mjs +20 -0
- package/skills/media-use/scripts/lib/npx-sync.mjs +28 -2
- package/skills/media-use/scripts/lib/prefs-store.mjs +2 -2
- package/skills/media-use/scripts/lib/recipe-store.mjs +2 -2
- package/skills/media-use/scripts/lib/telemetry.mjs +29 -41
- package/skills/media-use/scripts/lib/transcriptCutFade.mjs +21 -0
- package/skills/media-use/scripts/prefs.mjs +1 -1
- package/skills/media-use/scripts/recipe.mjs +1 -1
- package/skills/media-use/scripts/resolve-plugin.test.mjs +62 -0
- package/skills/media-use/scripts/resolve.mjs +27 -1267
- package/skills/media-use/scripts/transcribe.mjs +39 -25
- package/skills/media-use/scripts/transcribe.test.mjs +112 -0
- package/skills/media-use/scripts/transcript-cut.mjs +39 -8
- package/skills/media-use/scripts/transcript-cut.test.mjs +117 -0
- package/skills/motion-graphics/SKILL.md +3 -1
- package/skills/motion-graphics/agents/builder.md +1 -0
- package/skills/motion-graphics/agents/director.md +2 -1
- package/skills/motion-graphics/catalog-map.md +25 -13
- package/skills/motion-graphics/categories/charts/module.md +2 -1
- package/skills/motion-graphics/categories/lower-thirds/module.md +4 -0
- package/skills/motion-graphics/categories/news/module.md +1 -1
- package/skills/motion-graphics/references/motion-vocabulary.md +4 -0
- package/skills/music-to-video/SKILL.md +8 -6
- package/skills/music-to-video/references/montage.md +3 -6
- package/skills/music-to-video/references/motion-primitives/3d-card-flip/index.html +10 -111
- package/skills/music-to-video/references/motion-primitives/3d-card-flip/scene.html +119 -0
- package/skills/music-to-video/references/motion-primitives/bg-flow-field/index.html +11 -284
- package/skills/music-to-video/references/motion-primitives/bg-flow-field/scene.html +294 -0
- package/skills/music-to-video/references/motion-primitives/binary-decrypt/index.html +10 -76
- package/skills/music-to-video/references/motion-primitives/binary-decrypt/scene.html +85 -0
- package/skills/music-to-video/references/motion-primitives/blur-resolve/index.html +10 -52
- package/skills/music-to-video/references/motion-primitives/blur-resolve/scene.html +60 -0
- package/skills/music-to-video/references/motion-primitives/braam-punch/index.html +10 -92
- package/skills/music-to-video/references/motion-primitives/braam-punch/scene.html +100 -0
- package/skills/music-to-video/references/motion-primitives/chromatic-split/index.html +10 -103
- package/skills/music-to-video/references/motion-primitives/chromatic-split/scene.html +111 -0
- package/skills/music-to-video/references/motion-primitives/chrome-sweep/index.html +10 -60
- package/skills/music-to-video/references/motion-primitives/chrome-sweep/scene.html +68 -0
- package/skills/music-to-video/references/motion-primitives/counting-punch/index.html +10 -110
- package/skills/music-to-video/references/motion-primitives/counting-punch/scene.html +119 -0
- package/skills/music-to-video/references/motion-primitives/crash-zoom-in/index.html +10 -112
- package/skills/music-to-video/references/motion-primitives/crash-zoom-in/scene.html +120 -0
- package/skills/music-to-video/references/motion-primitives/datamosh-smear/index.html +10 -121
- package/skills/music-to-video/references/motion-primitives/datamosh-smear/scene.html +131 -0
- package/skills/music-to-video/references/motion-primitives/directional-fill/index.html +10 -90
- package/skills/music-to-video/references/motion-primitives/directional-fill/scene.html +103 -0
- package/skills/music-to-video/references/motion-primitives/dolly-zoom/index.html +10 -94
- package/skills/music-to-video/references/motion-primitives/dolly-zoom/scene.html +102 -0
- package/skills/music-to-video/references/motion-primitives/electric-arc/index.html +10 -100
- package/skills/music-to-video/references/motion-primitives/electric-arc/scene.html +105 -0
- package/skills/music-to-video/references/motion-primitives/flash-cut/index.html +10 -89
- package/skills/music-to-video/references/motion-primitives/flash-cut/scene.html +97 -0
- package/skills/music-to-video/references/motion-primitives/gooey-metaball/index.html +10 -197
- package/skills/music-to-video/references/motion-primitives/gooey-metaball/scene.html +206 -0
- package/skills/music-to-video/references/motion-primitives/hard-cut/index.html +10 -74
- package/skills/music-to-video/references/motion-primitives/hard-cut/scene.html +82 -0
- package/skills/music-to-video/references/motion-primitives/hypercut-whip/index.html +10 -36
- package/skills/music-to-video/references/motion-primitives/hypercut-whip/scene.html +44 -0
- package/skills/music-to-video/references/motion-primitives/iris-open/index.html +10 -61
- package/skills/music-to-video/references/motion-primitives/iris-open/scene.html +69 -0
- package/skills/music-to-video/references/motion-primitives/kinetic-letter-in/index.html +10 -55
- package/skills/music-to-video/references/motion-primitives/kinetic-letter-in/scene.html +64 -0
- package/skills/music-to-video/references/motion-primitives/liquid-morph/index.html +10 -88
- package/skills/music-to-video/references/motion-primitives/liquid-morph/scene.html +96 -0
- package/skills/music-to-video/references/motion-primitives/mask-reveal/index.html +10 -66
- package/skills/music-to-video/references/motion-primitives/mask-reveal/scene.html +74 -0
- package/skills/music-to-video/references/motion-primitives/mosaic-pack/index.html +10 -81
- package/skills/music-to-video/references/motion-primitives/mosaic-pack/scene.html +89 -0
- package/skills/music-to-video/references/motion-primitives/neon-flicker/index.html +10 -65
- package/skills/music-to-video/references/motion-primitives/neon-flicker/scene.html +73 -0
- package/skills/music-to-video/references/motion-primitives/outline-to-fill/index.html +10 -74
- package/skills/music-to-video/references/motion-primitives/outline-to-fill/scene.html +82 -0
- package/skills/music-to-video/references/motion-primitives/palette-flip/index.html +10 -89
- package/skills/music-to-video/references/motion-primitives/palette-flip/scene.html +97 -0
- package/skills/music-to-video/references/motion-primitives/particle-burst/index.html +10 -98
- package/skills/music-to-video/references/motion-primitives/particle-burst/scene.html +106 -0
- package/skills/music-to-video/references/motion-primitives/pixel-dissolve/index.html +10 -78
- package/skills/music-to-video/references/motion-primitives/pixel-dissolve/scene.html +86 -0
- package/skills/music-to-video/references/motion-primitives/radial-burst-lines/index.html +10 -117
- package/skills/music-to-video/references/motion-primitives/radial-burst-lines/scene.html +130 -0
- package/skills/music-to-video/references/motion-primitives/screen-shake/index.html +10 -70
- package/skills/music-to-video/references/motion-primitives/screen-shake/scene.html +78 -0
- package/skills/music-to-video/references/motion-primitives/slot-machine-reveal/index.html +10 -87
- package/skills/music-to-video/references/motion-primitives/slot-machine-reveal/scene.html +100 -0
- package/skills/music-to-video/references/motion-primitives/spotlight-sweep/index.html +10 -94
- package/skills/music-to-video/references/motion-primitives/spotlight-sweep/scene.html +102 -0
- package/skills/music-to-video/references/motion-primitives/staggered-exit/index.html +10 -74
- package/skills/music-to-video/references/motion-primitives/staggered-exit/scene.html +82 -0
- package/skills/music-to-video/references/motion-primitives/text-spectral-rays/index.html +11 -271
- package/skills/music-to-video/references/motion-primitives/text-spectral-rays/scene.html +280 -0
- package/skills/music-to-video/references/motion-primitives/text-wave-distort/index.html +10 -92
- package/skills/music-to-video/references/motion-primitives/text-wave-distort/scene.html +106 -0
- package/skills/music-to-video/references/motion-primitives/tile-mosaic/index.html +10 -101
- package/skills/music-to-video/references/motion-primitives/tile-mosaic/scene.html +109 -0
- package/skills/music-to-video/references/motion-primitives/typewriter-reveal/index.html +10 -79
- package/skills/music-to-video/references/motion-primitives/typewriter-reveal/scene.html +91 -0
- package/skills/music-to-video/references/motion-primitives/word-grid-burst/index.html +10 -83
- package/skills/music-to-video/references/motion-primitives/word-grid-burst/scene.html +96 -0
- package/skills/music-to-video/references/planning.md +2 -2
- package/skills/music-to-video/scripts/lib/storyboard.mjs +1 -1
- package/skills/music-to-video/sub-agents/frame-worker.md +2 -2
- package/skills/pr-to-video/SKILL.md +20 -16
- package/skills/pr-to-video/references/code-vocabulary.md +14 -12
- package/skills/pr-to-video/references/story-design.md +1 -1
- package/skills/pr-to-video/scripts/assemble-index.mjs +6 -4
- package/skills/pr-to-video/scripts/assemble-index.test.mjs +57 -2
- package/skills/pr-to-video/scripts/audio.mjs +2 -0
- package/skills/pr-to-video/scripts/build-frame.mjs +6 -3
- package/skills/pr-to-video/scripts/captions.mjs +7 -4
- package/skills/pr-to-video/scripts/captions.test.mjs +41 -0
- package/skills/pr-to-video/scripts/frame-packets.mjs +4 -4
- package/skills/pr-to-video/scripts/lib/bgm-volume.mjs +4 -0
- package/skills/pr-to-video/scripts/lib/captured-fonts.mjs +58 -0
- package/skills/pr-to-video/scripts/lib/frame-packets-core.mjs +194 -0
- package/skills/pr-to-video/scripts/lib/storyboard.mjs +1 -1
- package/skills/pr-to-video/scripts/lib/transitions.json +3 -3
- package/skills/pr-to-video/scripts/transitions.mjs +7 -5
- package/skills/pr-to-video/sub-agents/frame-worker.md +1 -1
- package/skills/product-launch-video/SKILL.md +20 -16
- package/skills/product-launch-video/references/story-design.md +1 -1
- package/skills/product-launch-video/scripts/assemble-index.mjs +6 -4
- package/skills/product-launch-video/scripts/assemble-index.test.mjs +57 -2
- package/skills/product-launch-video/scripts/audio.mjs +2 -0
- package/skills/product-launch-video/scripts/audio.test.mjs +16 -0
- package/skills/product-launch-video/scripts/build-frame-subsets.test.mjs +152 -0
- package/skills/product-launch-video/scripts/build-frame.mjs +6 -3
- package/skills/product-launch-video/scripts/captions.mjs +7 -4
- package/skills/product-launch-video/scripts/frame-packets.mjs +4 -4
- package/skills/product-launch-video/scripts/lib/bgm-volume.mjs +4 -0
- package/skills/product-launch-video/scripts/lib/captured-fonts.mjs +58 -0
- package/skills/product-launch-video/scripts/lib/frame-packets-core.mjs +194 -0
- package/skills/product-launch-video/scripts/lib/storyboard.mjs +1 -1
- package/skills/product-launch-video/scripts/lib/transitions.json +3 -3
- package/skills/product-launch-video/scripts/transitions.mjs +7 -5
- package/skills/product-launch-video/scripts/transitions.test.mjs +68 -0
- package/skills/product-launch-video/sub-agents/frame-worker.md +1 -1
- package/skills/remotion-to-hyperframes/SKILL.md +7 -4
- package/skills/remotion-to-hyperframes/assets/test-corpus/tier-1-title-card/expected.json +5 -5
- package/skills/remotion-to-hyperframes/assets/test-corpus/tier-1-title-card/hf-src/compositions/title.html +31 -0
- package/skills/remotion-to-hyperframes/assets/test-corpus/tier-1-title-card/hf-src/index.html +10 -25
- package/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/README.md +8 -8
- package/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/expected.json +9 -9
- package/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/hf-src/compositions/scene-1.html +35 -0
- package/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/hf-src/compositions/scene-2.html +35 -0
- package/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/hf-src/compositions/scene-3.html +36 -0
- package/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/hf-src/index.html +28 -71
- package/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/expected.json +3 -3
- package/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/hf-src/compositions/scene-1.html +52 -0
- package/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/hf-src/compositions/scene-2.html +99 -0
- package/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/hf-src/compositions/scene-3.html +55 -0
- package/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/hf-src/index.html +13 -212
- package/skills/remotion-to-hyperframes/references/api-map.md +16 -16
- package/skills/remotion-to-hyperframes/references/limitations.md +7 -7
- package/skills/remotion-to-hyperframes/references/lottie.md +5 -7
- package/skills/remotion-to-hyperframes/references/media.md +3 -3
- package/skills/remotion-to-hyperframes/references/parameters.md +5 -0
- package/skills/remotion-to-hyperframes/references/sequencing.md +71 -18
- package/skills/slideshow/SKILL.md +4 -1
- package/skills/slideshow/references/standalone-harness.md +0 -3
- package/skills/talking-head-recut/SKILL.md +10 -1
- package/skills/hyperframes-core/references/review-loop.md +0 -41
- package/skills/media-use/scripts/lib/adopt.mjs +0 -128
- package/skills/media-use/scripts/lib/adopt.test.mjs +0 -95
- package/skills/media-use/scripts/lib/bgm-provider.mjs +0 -20
- package/skills/media-use/scripts/lib/brand-provider.mjs +0 -59
- package/skills/media-use/scripts/lib/bundled-sfx-provider.mjs +0 -108
- package/skills/media-use/scripts/lib/bundled-sfx-provider.test.mjs +0 -85
- package/skills/media-use/scripts/lib/cache.mjs +0 -150
- package/skills/media-use/scripts/lib/candidates.mjs +0 -91
- package/skills/media-use/scripts/lib/candidates.test.mjs +0 -155
- package/skills/media-use/scripts/lib/codex-provider.mjs +0 -161
- package/skills/media-use/scripts/lib/codex-provider.test.mjs +0 -34
- package/skills/media-use/scripts/lib/coverage.test.mjs +0 -117
- package/skills/media-use/scripts/lib/cube-build.mjs +0 -181
- package/skills/media-use/scripts/lib/cube-build.test.mjs +0 -80
- package/skills/media-use/scripts/lib/cube-validate.mjs +0 -198
- package/skills/media-use/scripts/lib/cube-validate.test.mjs +0 -125
- package/skills/media-use/scripts/lib/cutlist.test.mjs +0 -148
- package/skills/media-use/scripts/lib/duck.test.mjs +0 -118
- package/skills/media-use/scripts/lib/error-diffusion.test.mjs +0 -102
- package/skills/media-use/scripts/lib/freeze.mjs +0 -70
- package/skills/media-use/scripts/lib/freeze.test.mjs +0 -46
- package/skills/media-use/scripts/lib/grade-analyzer.mjs +0 -264
- package/skills/media-use/scripts/lib/grade-analyzer.test.mjs +0 -285
- package/skills/media-use/scripts/lib/heygen-cli.mjs +0 -191
- package/skills/media-use/scripts/lib/heygen-cli.test.mjs +0 -301
- package/skills/media-use/scripts/lib/heygen-search.mjs +0 -45
- package/skills/media-use/scripts/lib/heygen-search.test.mjs +0 -49
- package/skills/media-use/scripts/lib/heygen-video-provider.mjs +0 -127
- package/skills/media-use/scripts/lib/heygen-video-provider.test.mjs +0 -344
- package/skills/media-use/scripts/lib/image-provider.mjs +0 -44
- package/skills/media-use/scripts/lib/local-models.mjs +0 -318
- package/skills/media-use/scripts/lib/local-models.test.mjs +0 -220
- package/skills/media-use/scripts/lib/local-run.mjs +0 -78
- package/skills/media-use/scripts/lib/local-run.test.mjs +0 -121
- package/skills/media-use/scripts/lib/logo-provider.mjs +0 -222
- package/skills/media-use/scripts/lib/logo-provider.test.mjs +0 -137
- package/skills/media-use/scripts/lib/ltx-video-provider.mjs +0 -94
- package/skills/media-use/scripts/lib/ltx-video-provider.test.mjs +0 -316
- package/skills/media-use/scripts/lib/lut-preset-provider.mjs +0 -266
- package/skills/media-use/scripts/lib/lut-preset-provider.test.mjs +0 -176
- package/skills/media-use/scripts/lib/manifest.test.mjs +0 -388
- package/skills/media-use/scripts/lib/match.mjs +0 -47
- package/skills/media-use/scripts/lib/mflux-provider.mjs +0 -128
- package/skills/media-use/scripts/lib/mflux-provider.test.mjs +0 -174
- package/skills/media-use/scripts/lib/misses.mjs +0 -49
- package/skills/media-use/scripts/lib/misses.test.mjs +0 -78
- package/skills/media-use/scripts/lib/npx-sync.test.mjs +0 -50
- package/skills/media-use/scripts/lib/parakeet-words.test.mjs +0 -44
- package/skills/media-use/scripts/lib/prefs-store.test.mjs +0 -189
- package/skills/media-use/scripts/lib/probe.mjs +0 -39
- package/skills/media-use/scripts/lib/probe.test.mjs +0 -31
- package/skills/media-use/scripts/lib/providers.mjs +0 -5
- package/skills/media-use/scripts/lib/recipe-store.test.mjs +0 -367
- package/skills/media-use/scripts/lib/registry.mjs +0 -218
- package/skills/media-use/scripts/lib/registry.test.mjs +0 -261
- package/skills/media-use/scripts/lib/search.mjs +0 -24
- package/skills/media-use/scripts/lib/search.test.mjs +0 -32
- package/skills/media-use/scripts/lib/sfx-provider.mjs +0 -23
- package/skills/media-use/scripts/lib/specs.mjs +0 -132
- package/skills/media-use/scripts/lib/specs.test.mjs +0 -157
- package/skills/media-use/scripts/lib/stats.mjs +0 -117
- package/skills/media-use/scripts/lib/stats.test.mjs +0 -165
- package/skills/media-use/scripts/lib/telemetry.test.mjs +0 -373
- package/skills/media-use/scripts/lib/tts-local-provider.mjs +0 -92
- package/skills/media-use/scripts/lib/tts-local-provider.test.mjs +0 -72
- package/skills/media-use/scripts/lib/usage.mjs +0 -57
- package/skills/media-use/scripts/lib/usage.test.mjs +0 -54
- package/skills/media-use/scripts/lib/voice-provider.mjs +0 -57
- package/skills/media-use/scripts/lib/voice-provider.test.mjs +0 -46
- package/skills/media-use/scripts/resolve.test.mjs +0 -1120
- /package/skills/{hyperframes-core → hyperframes}/references/subagent-dispatch.md +0 -0
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
} from "node:fs";
|
|
13
13
|
import { homedir, tmpdir } from "node:os";
|
|
14
14
|
import { basename, extname, join, resolve } from "node:path";
|
|
15
|
-
import { parseArgs } from "node:util";
|
|
15
|
+
import { parseArgs, stripVTControlCharacters } from "node:util";
|
|
16
16
|
import { mergeTokensToWords } from "./lib/parakeet-words.mjs";
|
|
17
17
|
import { track } from "./lib/telemetry.mjs";
|
|
18
18
|
import { resolveNpxInvocation } from "./lib/npx-sync.mjs";
|
|
@@ -49,7 +49,8 @@ Usage:
|
|
|
49
49
|
|
|
50
50
|
Parakeet (default) beats whisper.cpp on accuracy + speed for English/European
|
|
51
51
|
languages; whisper.cpp (99 languages) is the fallback. Install Parakeet once:
|
|
52
|
-
|
|
52
|
+
npx hyperframes models install parakeet (macOS, Windows, Linux with glibc 2.32+)
|
|
53
|
+
uv venv ~/.venvs/parakeet && VIRTUAL_ENV=~/.venvs/parakeet uv pip install parakeet-mlx (Apple Silicon)`);
|
|
53
54
|
process.exit(0);
|
|
54
55
|
}
|
|
55
56
|
|
|
@@ -122,33 +123,57 @@ function runParakeet(runner) {
|
|
|
122
123
|
}
|
|
123
124
|
}
|
|
124
125
|
|
|
125
|
-
//
|
|
126
|
+
// The hyperframes CLI (its Parakeet, else whisper.cpp built on first use — see
|
|
126
127
|
// SKILL.md): writes transcript.json into --dir; relocate to --out.
|
|
127
|
-
function
|
|
128
|
+
function runCli(cliEngine) {
|
|
128
129
|
const workDir = mkdtempSync(join(tmpdir(), "media-use-whisper-"));
|
|
129
130
|
try {
|
|
130
131
|
// On Windows a bare "npx" is npx.cmd, which execFileSync cannot exec
|
|
131
132
|
// (spawnSync npx ENOENT) — resolveNpxInvocation reroutes it through
|
|
132
133
|
// node + npx-cli.js (and throws actionably when it can't), same
|
|
133
134
|
// mechanism as the audio engine's TTS spawns.
|
|
135
|
+
// Under auto or parakeet the CLI may run its own Parakeet; --json says which engine ran.
|
|
134
136
|
const resolved = resolveNpxInvocation(
|
|
135
|
-
["hyperframes", "transcribe", inputPath, "--dir", workDir],
|
|
137
|
+
["hyperframes", "transcribe", inputPath, "--dir", workDir, "--engine", cliEngine, "--json"],
|
|
136
138
|
{ stdio: ["ignore", "pipe", "pipe"], timeout: 1_800_000 },
|
|
137
139
|
);
|
|
138
|
-
|
|
140
|
+
let stdout;
|
|
141
|
+
try {
|
|
142
|
+
stdout = String(execFileSync(resolved.cmd, resolved.args, resolved.opts));
|
|
143
|
+
} catch (e) {
|
|
144
|
+
// stderr holds spinner redraws and --json prints the reason on stdout, so keep the last lines.
|
|
145
|
+
const tail = stripVTControlCharacters(`${e.stderr ?? ""}\n${e.stdout ?? ""}`)
|
|
146
|
+
.split(/[\r\n]+/)
|
|
147
|
+
.map((l) => l.trim())
|
|
148
|
+
.filter((l) => l && l !== "│")
|
|
149
|
+
.slice(-3)
|
|
150
|
+
.join("\n");
|
|
151
|
+
throw new Error(
|
|
152
|
+
`hyperframes transcribe failed (exit ${e.status ?? e.code})${tail ? `:\n${tail}` : ""}`,
|
|
153
|
+
);
|
|
154
|
+
}
|
|
139
155
|
const produced = join(workDir, "transcript.json");
|
|
140
|
-
if (!existsSync(produced))
|
|
156
|
+
if (!existsSync(produced))
|
|
157
|
+
throw new Error("hyperframes transcribe produced no transcript.json");
|
|
141
158
|
const tmp = `${outPath}.tmp-${process.pid}`;
|
|
142
159
|
copyFileSync(produced, tmp);
|
|
143
160
|
renameSync(tmp, outPath); // atomic publish
|
|
144
161
|
let words;
|
|
145
162
|
try {
|
|
146
163
|
const t = JSON.parse(readFileSync(outPath, "utf8"));
|
|
147
|
-
|
|
164
|
+
// The CLI writes a flat word array, not { words }.
|
|
165
|
+
words = Array.isArray(t) ? t.length : undefined;
|
|
148
166
|
} catch {
|
|
149
167
|
/* leave undefined */
|
|
150
168
|
}
|
|
151
|
-
|
|
169
|
+
let ran = "whisper";
|
|
170
|
+
try {
|
|
171
|
+
ran = JSON.parse(stdout.trim().split("\n").at(-1)).engine === "parakeet" ? "parakeet" : ran;
|
|
172
|
+
} catch {
|
|
173
|
+
/* no JSON line: keep whisper */
|
|
174
|
+
}
|
|
175
|
+
report(ran, words);
|
|
176
|
+
return ran;
|
|
152
177
|
} finally {
|
|
153
178
|
rmSync(workDir, { recursive: true, force: true });
|
|
154
179
|
}
|
|
@@ -156,22 +181,11 @@ function runWhisper() {
|
|
|
156
181
|
|
|
157
182
|
try {
|
|
158
183
|
const parakeetBin = resolveParakeet();
|
|
159
|
-
const engine
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
: "whisper";
|
|
165
|
-
if (engine === "parakeet") {
|
|
166
|
-
if (!parakeetBin) {
|
|
167
|
-
throw new Error(
|
|
168
|
-
"parakeet-mlx not found (checked $HYPERFRAMES_PARAKEET, ~/.venvs/parakeet, and PATH). Install: uv venv ~/.venvs/parakeet && VIRTUAL_ENV=~/.venvs/parakeet uv pip install parakeet-mlx (or use --engine whisper)",
|
|
169
|
-
);
|
|
170
|
-
}
|
|
171
|
-
runParakeet(parakeetBin);
|
|
172
|
-
} else {
|
|
173
|
-
runWhisper();
|
|
174
|
-
}
|
|
184
|
+
const asked = args.engine === "parakeet" || args.engine === "whisper" ? args.engine : "auto";
|
|
185
|
+
// Without parakeet-mlx, the CLI's own Parakeet (any OS, `hyperframes models install parakeet`).
|
|
186
|
+
let engine = "parakeet";
|
|
187
|
+
if (asked !== "whisper" && parakeetBin) runParakeet(parakeetBin);
|
|
188
|
+
else engine = runCli(asked);
|
|
175
189
|
await track("media_use_transcribe", { engine });
|
|
176
190
|
} catch (err) {
|
|
177
191
|
if (args.json) console.log(JSON.stringify({ ok: false, error: err.message }));
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { execFileSync, spawnSync } from "node:child_process";
|
|
3
|
+
import { chmodSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import test from "node:test";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
|
|
9
|
+
// The stub stands in for `npx hyperframes transcribe <in> --dir <dir> ...`: it records its args,
|
|
10
|
+
// writes the CLI's flat array and prints the CLI's --json line naming `cliEngine`.
|
|
11
|
+
function runScript(engine, cliEngine) {
|
|
12
|
+
const root = mkdtempSync(join(tmpdir(), "media-use-transcribe-"));
|
|
13
|
+
try {
|
|
14
|
+
const bin = join(root, "bin");
|
|
15
|
+
mkdirSync(bin);
|
|
16
|
+
writeFileSync(
|
|
17
|
+
join(bin, "npx"),
|
|
18
|
+
`#!/bin/sh\necho "$@" > "${root}/args"\nwhile [ "$1" != "--dir" ]; do shift; done\n` +
|
|
19
|
+
`echo '[{"text":"a","start":0,"end":1},{"text":"b","start":1,"end":2}]' > "$2/transcript.json"\n` +
|
|
20
|
+
`echo '{"ok":true,"engine":"${cliEngine}"}'\n`,
|
|
21
|
+
);
|
|
22
|
+
chmodSync(join(bin, "npx"), 0o755);
|
|
23
|
+
const input = join(root, "in.wav");
|
|
24
|
+
writeFileSync(input, "");
|
|
25
|
+
const script = fileURLToPath(new URL("./transcribe.mjs", import.meta.url));
|
|
26
|
+
const env = { ...process.env, PATH: `${bin}:${process.env.PATH}`, HOME: root };
|
|
27
|
+
delete env.HYPERFRAMES_PARAKEET;
|
|
28
|
+
const out = execFileSync(
|
|
29
|
+
process.execPath,
|
|
30
|
+
[script, "--input", input, "--engine", engine, "--json"],
|
|
31
|
+
{
|
|
32
|
+
encoding: "utf8",
|
|
33
|
+
env: { ...env, HYPERFRAMES_MEDIA_HOME: join(root, "home"), HYPERFRAMES_NO_TELEMETRY: "1" },
|
|
34
|
+
},
|
|
35
|
+
);
|
|
36
|
+
return { result: JSON.parse(out.trim()), args: readFileSync(join(root, "args"), "utf8") };
|
|
37
|
+
} finally {
|
|
38
|
+
rmSync(root, { recursive: true, force: true });
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
test(
|
|
43
|
+
"--engine whisper makes the CLI run whisper and reports the flat transcript's word count",
|
|
44
|
+
{ skip: process.platform === "win32" },
|
|
45
|
+
() => {
|
|
46
|
+
const { result, args } = runScript("whisper", "whisper");
|
|
47
|
+
assert.match(args, /--engine whisper --json/);
|
|
48
|
+
assert.equal(result.words, 2);
|
|
49
|
+
assert.equal(result.engine, "whisper");
|
|
50
|
+
},
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
test(
|
|
54
|
+
"auto reports the engine the CLI says it ran, Parakeet included",
|
|
55
|
+
{ skip: process.platform === "win32" },
|
|
56
|
+
() => {
|
|
57
|
+
const { result, args } = runScript("auto", "parakeet");
|
|
58
|
+
assert.match(args, /--engine auto --json/);
|
|
59
|
+
assert.equal(result.engine, "parakeet");
|
|
60
|
+
},
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
test(
|
|
64
|
+
"--engine parakeet without parakeet-mlx asks the CLI for its Parakeet",
|
|
65
|
+
{ skip: process.platform === "win32" },
|
|
66
|
+
() => {
|
|
67
|
+
const { result, args } = runScript("parakeet", "parakeet");
|
|
68
|
+
assert.match(args, /--engine parakeet --json/);
|
|
69
|
+
assert.equal(result.engine, "parakeet");
|
|
70
|
+
},
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
test(
|
|
74
|
+
"a failed --engine parakeet run reports the reason the CLI printed on stdout",
|
|
75
|
+
{ skip: process.platform === "win32" },
|
|
76
|
+
() => {
|
|
77
|
+
const root = mkdtempSync(join(tmpdir(), "media-use-transcribe-"));
|
|
78
|
+
try {
|
|
79
|
+
const bin = join(root, "bin");
|
|
80
|
+
mkdirSync(bin);
|
|
81
|
+
const reason =
|
|
82
|
+
"Parakeet is not installed. Install it with: hyperframes models install parakeet";
|
|
83
|
+
writeFileSync(
|
|
84
|
+
join(bin, "npx"),
|
|
85
|
+
`#!/bin/sh\necho '{"ok":false,"error":"${reason}"}'\nexit 1\n`,
|
|
86
|
+
);
|
|
87
|
+
chmodSync(join(bin, "npx"), 0o755);
|
|
88
|
+
const input = join(root, "in.wav");
|
|
89
|
+
writeFileSync(input, "");
|
|
90
|
+
const script = fileURLToPath(new URL("./transcribe.mjs", import.meta.url));
|
|
91
|
+
const res = spawnSync(
|
|
92
|
+
process.execPath,
|
|
93
|
+
[script, "--input", input, "--engine", "parakeet", "--json"],
|
|
94
|
+
{
|
|
95
|
+
encoding: "utf8",
|
|
96
|
+
env: {
|
|
97
|
+
...process.env,
|
|
98
|
+
HYPERFRAMES_PARAKEET: "",
|
|
99
|
+
PATH: `${bin}:${process.env.PATH}`,
|
|
100
|
+
HOME: root,
|
|
101
|
+
HYPERFRAMES_MEDIA_HOME: join(root, "home"),
|
|
102
|
+
HYPERFRAMES_NO_TELEMETRY: "1",
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
);
|
|
106
|
+
assert.equal(res.status, 1);
|
|
107
|
+
assert.match(JSON.parse(res.stdout.trim()).error, /hyperframes models install parakeet/);
|
|
108
|
+
} finally {
|
|
109
|
+
rmSync(root, { recursive: true, force: true });
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
);
|
|
@@ -6,6 +6,7 @@ import { tmpdir } from "node:os";
|
|
|
6
6
|
import { dirname, extname, join, resolve } from "node:path";
|
|
7
7
|
import { parseArgs } from "node:util";
|
|
8
8
|
import { compileCutList } from "./lib/cutlist.mjs";
|
|
9
|
+
import { fadeFilterFor } from "./lib/transcriptCutFade.mjs";
|
|
9
10
|
import { track } from "./lib/telemetry.mjs";
|
|
10
11
|
|
|
11
12
|
const { values: args } = parseArgs({
|
|
@@ -92,11 +93,23 @@ function run() {
|
|
|
92
93
|
|
|
93
94
|
try {
|
|
94
95
|
const parts = segments.map((segment, index) => {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
96
|
+
// Intermediates carry PCM audio, not the final codec. Encoding each
|
|
97
|
+
// segment to a lossy codec separately makes the encoder pad every segment
|
|
98
|
+
// with priming silence (~25-35ms for AAC), which concat then bakes in as a
|
|
99
|
+
// gap at each cut -- a defect distinct from, and surviving, the fades
|
|
100
|
+
// below. PCM has no priming, so audio is encoded exactly once, at concat.
|
|
101
|
+
const ext = args.copy ? extname(outPath) || ".mp4" : isAudioOnly(outPath) ? ".wav" : ".mkv";
|
|
102
|
+
const out = join(tmpDir, `segment-${String(index).padStart(4, "0")}${ext}`);
|
|
103
|
+
// --copy stays fade-free (stream copy cannot filter). A segment's true
|
|
104
|
+
// start/end (index 0's start, the last segment's end) borders nothing
|
|
105
|
+
// kept, so only an interior splice edge gets a ramp.
|
|
106
|
+
const fade = args.copy
|
|
107
|
+
? null
|
|
108
|
+
: fadeFilterFor(segment.end - segment.start, {
|
|
109
|
+
fadeIn: index > 0,
|
|
110
|
+
fadeOut: index < segments.length - 1,
|
|
111
|
+
});
|
|
112
|
+
cutSegment(inputPath, segment, out, args.copy, fade);
|
|
100
113
|
return out;
|
|
101
114
|
});
|
|
102
115
|
const listPath = join(tmpDir, "list.txt");
|
|
@@ -107,9 +120,16 @@ function run() {
|
|
|
107
120
|
// Encode to a sibling temp (same extension so ffmpeg picks the right muxer),
|
|
108
121
|
// then atomic-rename so a SIGKILL mid-encode can't leave a truncated outPath.
|
|
109
122
|
const tmpOut = `${outPath}.part${extname(outPath) || ".mp4"}`;
|
|
123
|
+
// --copy already produced final-codec segments, so concat can stream-copy.
|
|
124
|
+
// Otherwise the PCM intermediates are encoded here, once, for the whole file.
|
|
125
|
+
const concatCodecs = args.copy
|
|
126
|
+
? ["-c", "copy"]
|
|
127
|
+
: isAudioOnly(outPath)
|
|
128
|
+
? encodeArgsFor(extname(outPath).toLowerCase())
|
|
129
|
+
: ["-c:v", "copy", "-c:a", "aac", "-b:a", "192k", "-movflags", "+faststart"];
|
|
110
130
|
execFileSync(
|
|
111
131
|
"ffmpeg",
|
|
112
|
-
["-y", "-f", "concat", "-safe", "0", "-i", listPath,
|
|
132
|
+
["-y", "-f", "concat", "-safe", "0", "-i", listPath, ...concatCodecs, tmpOut],
|
|
113
133
|
{
|
|
114
134
|
stdio: "ignore",
|
|
115
135
|
},
|
|
@@ -158,7 +178,7 @@ function run() {
|
|
|
158
178
|
console.log(`next: resolve --from ${outPath} --type <type>`);
|
|
159
179
|
}
|
|
160
180
|
|
|
161
|
-
function cutSegment(inputPath, segment, outPath, copy) {
|
|
181
|
+
function cutSegment(inputPath, segment, outPath, copy, fade) {
|
|
162
182
|
const argv = [
|
|
163
183
|
"-y",
|
|
164
184
|
"-nostdin",
|
|
@@ -171,8 +191,15 @@ function cutSegment(inputPath, segment, outPath, copy) {
|
|
|
171
191
|
];
|
|
172
192
|
if (copy) {
|
|
173
193
|
argv.push("-c", "copy", "-avoid_negative_ts", "make_zero");
|
|
194
|
+
} else if (extname(outPath).toLowerCase() === ".mkv") {
|
|
195
|
+
// Concat splices raw segment edges together; without a short ramp the
|
|
196
|
+
// waveform steps discontinuously at every boundary and you hear a click.
|
|
197
|
+
if (fade) argv.push("-af", fade);
|
|
198
|
+
// Video intermediate: keep the picture cheap and the audio uncompressed.
|
|
199
|
+
argv.push("-c:v", "libx264", "-preset", "veryfast", "-crf", "18", "-c:a", "pcm_s16le");
|
|
174
200
|
} else {
|
|
175
|
-
argv.push(
|
|
201
|
+
if (fade) argv.push("-af", fade);
|
|
202
|
+
argv.push("-c:a", "pcm_s16le");
|
|
176
203
|
}
|
|
177
204
|
argv.push(outPath);
|
|
178
205
|
execFileSync("ffmpeg", argv, { stdio: "ignore" });
|
|
@@ -180,6 +207,10 @@ function cutSegment(inputPath, segment, outPath, copy) {
|
|
|
180
207
|
|
|
181
208
|
// Codec set per output container. Audio-only outputs must not get the
|
|
182
209
|
// video-centric aac/x264 set (aac inside .wav breaks timing entirely).
|
|
210
|
+
function isAudioOnly(filePath) {
|
|
211
|
+
return [".wav", ".mp3", ".m4a", ".aac", ".flac"].includes(extname(filePath).toLowerCase());
|
|
212
|
+
}
|
|
213
|
+
|
|
183
214
|
function encodeArgsFor(ext) {
|
|
184
215
|
if (ext === ".wav") return ["-c:a", "pcm_s16le"];
|
|
185
216
|
if (ext === ".mp3") return ["-c:a", "libmp3lame", "-q:a", "2"];
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { execFileSync, spawnSync } from "node:child_process";
|
|
3
|
+
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import test from "node:test";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
|
|
9
|
+
const SCRIPT = fileURLToPath(new URL("./transcript-cut.mjs", import.meta.url));
|
|
10
|
+
const HAS_FFMPEG =
|
|
11
|
+
spawnSync("ffmpeg", ["-version"], { stdio: "ignore" }).status === 0 &&
|
|
12
|
+
spawnSync("ffprobe", ["-version"], { stdio: "ignore" }).status === 0;
|
|
13
|
+
|
|
14
|
+
const SAMPLE_RATE = 44100;
|
|
15
|
+
// A continuous tone's own slope between samples never exceeds amplitude * 2*pi*f/rate
|
|
16
|
+
// (~2054 for 440Hz at full scale here); a raw splice between two independently-cut
|
|
17
|
+
// points on the same tone lands at two unrelated phases, so an unfaded join jumps far
|
|
18
|
+
// past that. This threshold sits well above normal tone motion and well below a splice.
|
|
19
|
+
const MAX_CONTINUOUS_STEP = 6000;
|
|
20
|
+
const MIN_SUSPICIOUS_ZERO_RUN = 20;
|
|
21
|
+
|
|
22
|
+
function fixture() {
|
|
23
|
+
const dir = mkdtempSync(join(tmpdir(), "media-use-transcript-cut-"));
|
|
24
|
+
return { dir, cleanup: () => rmSync(dir, { recursive: true, force: true }) };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function run(args) {
|
|
28
|
+
return spawnSync(process.execPath, [SCRIPT, ...args], { encoding: "utf8" });
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function readPcm(filePath) {
|
|
32
|
+
const raw = execFileSync("ffmpeg", [
|
|
33
|
+
"-hide_banner",
|
|
34
|
+
"-loglevel",
|
|
35
|
+
"error",
|
|
36
|
+
"-i",
|
|
37
|
+
filePath,
|
|
38
|
+
"-f",
|
|
39
|
+
"s16le",
|
|
40
|
+
"-ac",
|
|
41
|
+
"1",
|
|
42
|
+
"-ar",
|
|
43
|
+
String(SAMPLE_RATE),
|
|
44
|
+
"-",
|
|
45
|
+
]);
|
|
46
|
+
const samples = new Int16Array(raw.buffer, raw.byteOffset, raw.length / 2);
|
|
47
|
+
return samples;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
test(
|
|
51
|
+
"keeps a spliced tone continuous at every cut, with no raw phase jump or silence gap",
|
|
52
|
+
{ skip: !HAS_FFMPEG },
|
|
53
|
+
(t) => {
|
|
54
|
+
const { dir, cleanup } = fixture();
|
|
55
|
+
t.after(cleanup);
|
|
56
|
+
|
|
57
|
+
const source = join(dir, "tone.wav");
|
|
58
|
+
execFileSync("ffmpeg", [
|
|
59
|
+
"-y",
|
|
60
|
+
"-hide_banner",
|
|
61
|
+
"-loglevel",
|
|
62
|
+
"error",
|
|
63
|
+
"-f",
|
|
64
|
+
"lavfi",
|
|
65
|
+
"-i",
|
|
66
|
+
`sine=frequency=440:duration=6:sample_rate=${SAMPLE_RATE}`,
|
|
67
|
+
source,
|
|
68
|
+
]);
|
|
69
|
+
|
|
70
|
+
const transcriptPath = join(dir, "transcript.json");
|
|
71
|
+
writeFileSync(transcriptPath, JSON.stringify({ words: [{ text: "tone", start: 0, end: 6 }] }));
|
|
72
|
+
|
|
73
|
+
const output = join(dir, "out.wav");
|
|
74
|
+
// Two "ugly" removal ranges: neither aligned to the 440Hz period, so the kept
|
|
75
|
+
// segments' cut edges land at unrelated phases of the same continuous tone --
|
|
76
|
+
// exactly the shape that clicks without a fade, and the shape jrusso1020's
|
|
77
|
+
// review proved this branch never actually fades.
|
|
78
|
+
const result = run([
|
|
79
|
+
"--input",
|
|
80
|
+
source,
|
|
81
|
+
"--transcript",
|
|
82
|
+
transcriptPath,
|
|
83
|
+
"--remove",
|
|
84
|
+
"1.37-1.83,3.29-3.71",
|
|
85
|
+
"--out",
|
|
86
|
+
output,
|
|
87
|
+
"--json",
|
|
88
|
+
]);
|
|
89
|
+
assert.equal(result.status, 0, result.stderr || result.stdout);
|
|
90
|
+
|
|
91
|
+
const samples = readPcm(output);
|
|
92
|
+
assert.ok(samples.length > SAMPLE_RATE, "expected several seconds of audio");
|
|
93
|
+
|
|
94
|
+
let maxStep = 0;
|
|
95
|
+
let zeroRun = 0;
|
|
96
|
+
let maxZeroRun = 0;
|
|
97
|
+
for (let i = 1; i < samples.length; i++) {
|
|
98
|
+
const step = Math.abs(samples[i] - samples[i - 1]);
|
|
99
|
+
if (step > maxStep) maxStep = step;
|
|
100
|
+
if (samples[i] === 0) {
|
|
101
|
+
zeroRun++;
|
|
102
|
+
if (zeroRun > maxZeroRun) maxZeroRun = zeroRun;
|
|
103
|
+
} else {
|
|
104
|
+
zeroRun = 0;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
assert.ok(
|
|
109
|
+
maxStep <= MAX_CONTINUOUS_STEP,
|
|
110
|
+
`largest sample-to-sample step was ${maxStep}, expected <= ${MAX_CONTINUOUS_STEP} (a raw, unfaded splice)`,
|
|
111
|
+
);
|
|
112
|
+
assert.ok(
|
|
113
|
+
maxZeroRun < MIN_SUSPICIOUS_ZERO_RUN,
|
|
114
|
+
`found a run of ${maxZeroRun} consecutive zero samples, expected < ${MIN_SUSPICIOUS_ZERO_RUN} (a priming-silence gap)`,
|
|
115
|
+
);
|
|
116
|
+
},
|
|
117
|
+
);
|
|
@@ -11,6 +11,8 @@ description: >
|
|
|
11
11
|
Longer / narrated / multi-scene → /general-video. Unclear → /hyperframes.
|
|
12
12
|
---
|
|
13
13
|
|
|
14
|
+
**Plugin installs:** Before setup or freshness commands, follow [plugin execution rules](../hyperframes/references/plugin-installation.md) when this skill is inside a HyperFrames plugin. Standalone installs keep the update instructions below.
|
|
15
|
+
|
|
14
16
|
> **First, keep this skill fresh — confirm with the user before running:** `npx hyperframes skills update motion-graphics`. A fast no-op when everything is current; otherwise it refreshes this skill plus the core domain skills it depends on before you rely on them.
|
|
15
17
|
|
|
16
18
|
> **figma source**: If the logo/asset/animation to build from comes from a figma.com URL, run `/figma` first — asset export, brand tokens, and Motion→GSAP translation if the graphic is a Figma Motion import — then build from its output. Don't drive Figma via raw MCP tools directly: that skips SVG sanitization, `.media/manifest.jsonl` provenance, and brand-token `var()` binding, so a later brand change can't propagate without a full re-import.
|
|
@@ -19,7 +21,7 @@ description: >
|
|
|
19
21
|
|
|
20
22
|
> **The front door is `/hyperframes`.** This skill makes a **short, design-led, unnarrated motion graphic** (motion is the message; ~under 10s, no voice-over). Anything longer, narrated, or multi-scene — or any uncertainty → read `/hyperframes` first: the intent layer owns every route decision.
|
|
21
23
|
|
|
22
|
-
This workflow is **autonomous by design** — at most one clarifying question (`agents/director.md`), then build through verification without intermediate review. The intent layer (`/hyperframes` → `references/intent-interview.md`) routes here directly without run-shape questions; a storyboard and companion session add little to a piece this short. Rendering is still user-gated: after checks and proof snapshots pass, ask the canonical “preview first, or render?” question from `../hyperframes
|
|
24
|
+
This workflow is **autonomous by design** — at most one clarifying question (`agents/director.md`), then build through verification without intermediate review. The intent layer (`/hyperframes` → `references/intent-interview.md`) routes here directly without run-shape questions; a storyboard and companion session add little to a piece this short. Rendering is still user-gated: after checks and proof snapshots pass, ask the canonical “preview first, or render?” question from `../hyperframes/references/brief-contract.md`. When a `BRIEF.md` exists, read it before the director's question.
|
|
23
25
|
|
|
24
26
|
A short design-led motion graphic. **Asset-first**: decide the asset strategy and source real material _before_ designing the shot, then design the shot around what you have, then compose by reusing catalog capabilities. All artifacts go to `PROJECT_DIR = videos/<project-name>/` (created in Step 0); all paths below are relative to it.
|
|
25
27
|
|
|
@@ -6,6 +6,7 @@ Turn `shot-plan.json` into one renderable HyperFrames composition (`compositions
|
|
|
6
6
|
|
|
7
7
|
Default = **compose existing catalog capabilities, not hand-author**:
|
|
8
8
|
|
|
9
|
+
- **Search first, for every named effect** — `npx hyperframes catalog --query "<the move, in plain English>" --json`, including an effect the user names after the plan is written. It needs nothing installed and no project. Author by hand only after a search came back with nothing that does the job, and report that miss with `npx hyperframes feedback --search-miss`.
|
|
9
10
|
- `npx hyperframes add <block>` (registry) → customize in place. Most blocks bake content/data into their own script (only a few expose CSS-var params), so reuse = **add + edit**.
|
|
10
11
|
- `hyperframes-animation` rules / blueprints / transitions for motion; runtime adapters (GSAP default).
|
|
11
12
|
|
|
@@ -40,7 +40,8 @@ Emit a DRAFT `shot-plan.json`.
|
|
|
40
40
|
|
|
41
41
|
Given the draft + resolved `assets/index.md` (if Step 2 ran) + `catalog-map.md`, design the shot **around the assets**:
|
|
42
42
|
|
|
43
|
-
-
|
|
43
|
+
- **Run `npx hyperframes catalog --query "<the move, in plain English>" --json` for every look or effect the brief names**, before naming any block. It ranks the whole hosted registry and needs nothing installed. `catalog-map.md` is a partial snapshot; the search is the source of truth.
|
|
44
|
+
- Pick the **catalog block(s)** from those results + the `hyperframes-animation` rules / blueprints (see `catalog-map.md` for how each category customizes).
|
|
44
45
|
- Layout (hero-frame), motion (per `references/motion-vocabulary.md`), beats, pacing, exits.
|
|
45
46
|
- `asset-fusion`: read the asset's **geometric affordance** → `element_positions` (center / extent / safe-zones / avoid-zones) + **eyedropper palette** from the asset.
|
|
46
47
|
- Finalize `shot-plan.json`: `content.block` + `content.customize` + the per-category `content`.
|
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Director → catalog block map (reuse-first)
|
|
2
2
|
|
|
3
|
+
<!-- registry-items: allow=geo-highlight,geo-flow,flag-borders,pin-rollout,stat-motion,data-composition-src -->
|
|
4
|
+
<!-- allow= entries above are deliberately NOT registry items: geo-*/flag-borders/pin-rollout and stat-motion are hand-authored gaps this file names in order to say they are gaps. -->
|
|
5
|
+
|
|
6
|
+
**Search the live catalog before you use this table, and again before any hand-authored effect.** Run
|
|
7
|
+
`npx hyperframes catalog --query "<the move, in plain English>" --json` — once per named look or effect the
|
|
8
|
+
user asks for ("CRT scanlines", "glitch", "film grain", "shimmer sweep"), including effects asked for
|
|
9
|
+
mid-build. **The search needs nothing installed**: it ranks the whole hosted registry (~400 items) from any
|
|
10
|
+
directory, with no project, no prior `add`, and no account. The table below is a hand-maintained snapshot of
|
|
11
|
+
part of that registry and it under-covers by design — treat a miss here as unproven, and only the search's
|
|
12
|
+
own empty result as evidence the catalog lacks the move. When it does, say so with
|
|
13
|
+
`npx hyperframes feedback --search-miss "<query>" --wanted "<the move>" --tier <tier from the envelope>`
|
|
14
|
+
before hand-authoring. Full flags and tiers: `/hyperframes-registry` → § Discovery.
|
|
15
|
+
|
|
3
16
|
The Builder's **default is to compose existing HF catalog blocks, not hand-author.** `npx hyperframes add <block>` drops a block's source into `compositions/`; the Builder then **customizes in place** — most blocks bake their content/data into their own script (only a few expose CSS-var `params`), so reuse = **add + edit**, not pure variable injection. Hand-author only (a) gaps no block covers and (b) the asset-fusion affordance binding.
|
|
4
17
|
|
|
5
18
|
After classifying intent → category, the **Director names the block(s) + what to customize** in the shot-plan IR:
|
|
@@ -10,19 +23,18 @@ After classifying intent → category, the **Director names the block(s) + what
|
|
|
10
23
|
|
|
11
24
|
## Category → block(s)
|
|
12
25
|
|
|
13
|
-
| Category | Borrow from catalog
|
|
14
|
-
| ---------------------- |
|
|
15
|
-
| **kinetic-typo** | one of **
|
|
16
|
-
| **charts** | `data-chart` (bar+line, staggered, value labels)
|
|
17
|
-
| **stat** | `apple-money-count` (finance: $ counter + burst + SFX) **or**
|
|
18
|
-
| maps/geo | **vector lane**: `us-map` (+bubble/hex/flow), `world-map`, `spain-map` + **hand-author** `geo-highlight`/`geo-flow`/`flag-borders`/`pin-rollout` (NOT in registry — build per `categories/maps/module.md`). **basemap lane** (real satellite/dark / zoom-to-place): bake via `categories/maps/bake-basemap.mjs` → `<video>` + geo-aligned SVG overlay (border draw-on + colour-block fills)
|
|
19
|
-
| diagram | `flowchart` (SVG connectors + nodes)
|
|
20
|
-
| **
|
|
21
|
-
|
|
|
22
|
-
|
|
|
23
|
-
|
|
|
24
|
-
|
|
|
25
|
-
| **asset-fusion (RWA)** | **annotation kit from `north-korea-locked-down`** — scribble-circle draw-on, pop-up pill + pointer, red-wash, scanline, corners, camera push; + `us-map-bubble` callouts/connectors | asset, annotation position (`element_positions`), label, palette **eyedropper'd from the asset** | **NET-NEW (not in catalog): the affordance binding — asset geometry _becomes_ the chart axis (straw → gauge)** |
|
|
26
|
+
| Category | Borrow from catalog | Customize | Gaps → hand-author |
|
|
27
|
+
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------- |
|
|
28
|
+
| **kinetic-typo** | one of **17 `caption-*`** (kinetic-slam, editorial-emphasis, clip-wipe, gradient-fill, neon-glow, glitch-rgb, matrix-decode, particle-burst, weight-shift, pill-karaoke, highlight, blend-difference…) | words; `emphasis_words` → `word--emphasis`; palette; font; timing | a motif none of the 17 cover |
|
|
29
|
+
| **charts** | `data-chart` (bar+line, staggered, value labels), `bar-chart-race` (a race) | edit the data array + headline/subtitle; `--bg`/`--text` | pie/donut, ring/% |
|
|
30
|
+
| **stat** | `apple-money-count` (finance: $ counter + burst + SFX) **or** hand-author a generic %/number + ring per `categories/stat/module.md` (there is no `stat-motion` block) | target, prefix/suffix, label, palette | — |
|
|
31
|
+
| maps/geo | **vector lane**: `us-map` (+bubble/hex/flow), `world-map`, `spain-map` + **hand-author** `geo-highlight`/`geo-flow`/`flag-borders`/`pin-rollout` (NOT in registry — build per `categories/maps/module.md`). **basemap lane** (real satellite/dark / zoom-to-place): bake via `categories/maps/bake-basemap.mjs` → `<video>` + geo-aligned SVG overlay (border draw-on + colour-block fills) | regions/data, palette, callouts/connectors, basemap style | (basemap lane now covers real imagery + zoom-to-address — was the gap) |
|
|
32
|
+
| diagram | `flowchart` (SVG connectors + nodes) | nodes, edges, labels | — |
|
|
33
|
+
| **brand reveal** | `logo-outro` (piece assembly + glow + tagline + URL pill) | logo asset, tagline, URL, palette | — |
|
|
34
|
+
| transitions | 15+ shader transitions (domain-warp, whip-pan, sdf-iris, glitch…) | direction, timing | — |
|
|
35
|
+
| social overlay | ig / tiktok / yt / x / reddit / spotify / macos cards | handle, metrics, avatar | — |
|
|
36
|
+
| polish | grain-overlay, vignette, shimmer-sweep, texture-mask-text | intensity | — |
|
|
37
|
+
| **asset-fusion (RWA)** | **annotation kit from `north-korea-locked-down`** — scribble-circle draw-on, pop-up pill + pointer, red-wash, scanline, corners, camera push; + `us-map-bubble` callouts/connectors | asset, annotation position (`element_positions`), label, palette **eyedropper'd from the asset** | **NET-NEW (not in catalog): the affordance binding — asset geometry _becomes_ the chart axis (straw → gauge)** |
|
|
26
38
|
|
|
27
39
|
## Reuse mechanics
|
|
28
40
|
|
|
@@ -9,7 +9,8 @@ Animated **data-viz** from data. Asset-free (the "input" is the data). "One char
|
|
|
9
9
|
## Vocabulary / leans on
|
|
10
10
|
|
|
11
11
|
- Block: **`data-chart`** (animated **bar + line**, staggered reveal, value labels — proven: borrowed + customized + rendered to MP4 in the prototype `charts-demo`).
|
|
12
|
-
-
|
|
12
|
+
- Also in the registry: **`bar-chart-race`** — install it rather than hand-authoring a race.
|
|
13
|
+
- Gaps (hand-author): **pie / donut, ring/%** — no registry block covers these. Search first (`npx hyperframes catalog --query "pie chart reveal" --json`), then use D3/visx for data→geometry + GSAP for motion.
|
|
13
14
|
- Signature animations: bar stagger-grow · line `stroke-dashoffset` draw-on · pie radial sweep · ring fill · KPI count-up · race reorder.
|
|
14
15
|
|
|
15
16
|
## Build (reuse-first)
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# lower-thirds — category module
|
|
2
2
|
|
|
3
|
+
<!-- registry-items: allow= -->
|
|
4
|
+
|
|
5
|
+
**The live search is the source of truth for what the registry has.** The table(s) below are a hand-maintained sample and under-cover by design: run `npx hyperframes catalog --query "<what you want>" --json` — it needs nothing installed — before concluding the registry lacks something. Item names here are checked against `registry/registry.json` by `bun run lint:skills`.
|
|
6
|
+
|
|
3
7
|
**Name/title bars, callouts, social overlays** — graphics meant to sit over other footage. Asset-free (+ optional logo). Usually `export: alpha-overlay` (transparent). ~3–6s (or loop/hold).
|
|
4
8
|
|
|
5
9
|
## Plan (Director)
|
|
@@ -11,7 +11,7 @@ Two layouts share that one highlight core — pick by aspect + whether there's a
|
|
|
11
11
|
|
|
12
12
|
## Source (Step 2)
|
|
13
13
|
|
|
14
|
-
RWA / web search (or `hyperframes capture`) → a real article. The Director extracts the **keyword** (1–2 words / a number / a name — the hook) and, for Layout B, also a **brand logo**, a **date**, and a **person photo**. `asset_needs: { kind: news|web|image, query }` — request the logo (
|
|
14
|
+
RWA / web search (or `hyperframes capture`) → a real article. The Director extracts the **keyword** (1–2 words / a number / a name — the hook) and, for Layout B, also a **brand logo**, a **date**, and a **person photo**. `asset_needs: { kind: news|web|image, query }` — request the logo (`media-use resolve --type logo`, theSVG-first) and the person photo (Wikimedia) as separate asset queries. Run the person photo through `hyperframes remove-background <in> -o <out>.png` to get a transparent cutout.
|
|
15
15
|
|
|
16
16
|
## The highlight core (shared by both layouts) — the marker-band technique
|
|
17
17
|
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# text module · motion vocabulary (primitive → GSAP)
|
|
2
2
|
|
|
3
|
+
<!-- registry-items: allow= -->
|
|
4
|
+
|
|
5
|
+
**The live search is the source of truth for what the registry has.** The table(s) below are a hand-maintained sample and under-cover by design: run `npx hyperframes catalog --query "<what you want>" --json` — it needs nothing installed — before concluding the registry lacks something. Item names here are checked against `registry/registry.json` by `bun run lint:skills`.
|
|
6
|
+
|
|
3
7
|
Named primitives the Director references in `motion` strings and the Builder implements. `code_hint`s are framework-neutral physics; the GSAP recipe is the HF implementation. Prefer an HF **registry component** (bottom) when one fits — don't reinvent.
|
|
4
8
|
|
|
5
9
|
## Entry
|