@officexapp/vidfarm-devcli 0.21.10
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/.agents/skills/dollarplatoon-skill/SKILL.md +1227 -0
- package/.agents/skills/editor-capabilities/SKILL.md +182 -0
- package/.agents/skills/editor-capabilities/references/re-theme-walkthrough.md +58 -0
- package/.agents/skills/embedded-captions/CATALOG.md +93 -0
- package/.agents/skills/embedded-captions/SKILL.md +286 -0
- package/.agents/skills/embedded-captions/assets/brand/CDPR-fankit-terms.txt +35 -0
- package/.agents/skills/embedded-captions/assets/brand/cyberpunk-widths.json +115 -0
- package/.agents/skills/embedded-captions/assets/fonts/char-widths.json +2211 -0
- package/.agents/skills/embedded-captions/assets/strokefonts/HersheyScript1.svg +242 -0
- package/.agents/skills/embedded-captions/assets/strokefonts/HersheyScriptMed.svg +242 -0
- package/.agents/skills/embedded-captions/dna/README.md +148 -0
- package/.agents/skills/embedded-captions/dna/chrome.json +68 -0
- package/.agents/skills/embedded-captions/dna/cream.json +69 -0
- package/.agents/skills/embedded-captions/dna/documentary.json +62 -0
- package/.agents/skills/embedded-captions/dna/editorial.json +70 -0
- package/.agents/skills/embedded-captions/dna/glitch.json +82 -0
- package/.agents/skills/embedded-captions/dna/ink.json +65 -0
- package/.agents/skills/embedded-captions/dna/keynote.json +62 -0
- package/.agents/skills/embedded-captions/dna/loud.json +75 -0
- package/.agents/skills/embedded-captions/dna/neon.json +58 -0
- package/.agents/skills/embedded-captions/dna/velocity.json +89 -0
- package/.agents/skills/embedded-captions/modes/cinematic/README.md +48 -0
- package/.agents/skills/embedded-captions/modes/cinematic/_archive/champion/spec.md +98 -0
- package/.agents/skills/embedded-captions/modes/cinematic/_archive/champion/template.html +188 -0
- package/.agents/skills/embedded-captions/modes/cinematic/_archive/memory-wall/spec.md +134 -0
- package/.agents/skills/embedded-captions/modes/cinematic/_archive/memory-wall/template.html +179 -0
- package/.agents/skills/embedded-captions/modes/cinematic/_archive/portrait-header/spec.md +81 -0
- package/.agents/skills/embedded-captions/modes/cinematic/_archive/portrait-header/template.html +170 -0
- package/.agents/skills/embedded-captions/modes/cinematic/cinematic-cream/spec.md +16 -0
- package/.agents/skills/embedded-captions/modes/cinematic/cinematic-cream/template.html +226 -0
- package/.agents/skills/embedded-captions/modes/cinematic/engine.html +355 -0
- package/.agents/skills/embedded-captions/modes/standard/_anatomy.md +233 -0
- package/.agents/skills/embedded-captions/modes/standard/_motion.md +192 -0
- package/.agents/skills/embedded-captions/modes/standard/fonts/build-fonts-css.cjs +97 -0
- package/.agents/skills/embedded-captions/references/aesthetic-principles.md +168 -0
- package/.agents/skills/embedded-captions/references/anti-patterns.md +188 -0
- package/.agents/skills/embedded-captions/references/bespoke-vs-presets.md +178 -0
- package/.agents/skills/embedded-captions/references/caption-grouping.md +130 -0
- package/.agents/skills/embedded-captions/references/composition-craft.md +522 -0
- package/.agents/skills/embedded-captions/references/direction-catalog.md +138 -0
- package/.agents/skills/embedded-captions/references/example-renders/champion.html +371 -0
- package/.agents/skills/embedded-captions/references/example-renders/memory-wall.html +334 -0
- package/.agents/skills/embedded-captions/references/failure-modes.md +145 -0
- package/.agents/skills/embedded-captions/references/layout-heuristics.md +238 -0
- package/.agents/skills/embedded-captions/references/motion-vocabulary.md +148 -0
- package/.agents/skills/embedded-captions/references/rail.md +68 -0
- package/.agents/skills/embedded-captions/references/reference-bar.md +47 -0
- package/.agents/skills/embedded-captions/references/scene-types.md +94 -0
- package/.agents/skills/embedded-captions/references/test-set.md +66 -0
- package/.agents/skills/embedded-captions/references/typographic-moves.md +266 -0
- package/.agents/skills/embedded-captions/references/typography-presets.md +63 -0
- package/.agents/skills/embedded-captions/scripts/audio-envelope.cjs +95 -0
- package/.agents/skills/embedded-captions/scripts/check-occlusion.cjs +250 -0
- package/.agents/skills/embedded-captions/scripts/check-overflow.cjs +194 -0
- package/.agents/skills/embedded-captions/scripts/check-rail-climax.cjs +233 -0
- package/.agents/skills/embedded-captions/scripts/check-timing.cjs +173 -0
- package/.agents/skills/embedded-captions/scripts/fill-timings.cjs +124 -0
- package/.agents/skills/embedded-captions/scripts/fit-fonts.cjs +158 -0
- package/.agents/skills/embedded-captions/scripts/gen-stroke-path.py +46 -0
- package/.agents/skills/embedded-captions/scripts/inject-fonts.cjs +169 -0
- package/.agents/skills/embedded-captions/scripts/lib-dna.cjs +201 -0
- package/.agents/skills/embedded-captions/scripts/make-cinematic.cjs +1205 -0
- package/.agents/skills/embedded-captions/scripts/make-composition.cjs +404 -0
- package/.agents/skills/embedded-captions/scripts/make-theme.cjs +8781 -0
- package/.agents/skills/embedded-captions/scripts/matte.cjs +294 -0
- package/.agents/skills/embedded-captions/scripts/measure-layout.cjs +293 -0
- package/.agents/skills/embedded-captions/scripts/prepare.sh +39 -0
- package/.agents/skills/embedded-captions/scripts/preview-frames.cjs +268 -0
- package/.agents/skills/embedded-captions/scripts/render-and-composite.sh +509 -0
- package/.agents/skills/embedded-captions/scripts/render-theme.sh +34 -0
- package/.agents/skills/embedded-captions/scripts/safe-zones.cjs +813 -0
- package/.agents/skills/embedded-captions/scripts/transcribe.cjs +349 -0
- package/.agents/skills/embedded-captions/themes/PORTING.md +49 -0
- package/.agents/skills/embedded-captions/themes/README.md +244 -0
- package/.agents/skills/embedded-captions/themes/anchor.json +27 -0
- package/.agents/skills/embedded-captions/themes/arcade.json +51 -0
- package/.agents/skills/embedded-captions/themes/aurora.json +41 -0
- package/.agents/skills/embedded-captions/themes/biolume.json +35 -0
- package/.agents/skills/embedded-captions/themes/brush.json +74 -0
- package/.agents/skills/embedded-captions/themes/chalkboard.json +52 -0
- package/.agents/skills/embedded-captions/themes/dossier.json +56 -0
- package/.agents/skills/embedded-captions/themes/graffiti.json +62 -0
- package/.agents/skills/embedded-captions/themes/hologram.json +41 -0
- package/.agents/skills/embedded-captions/themes/inkwater.json +43 -0
- package/.agents/skills/embedded-captions/themes/laser.json +46 -0
- package/.agents/skills/embedded-captions/themes/lastpage.json +33 -0
- package/.agents/skills/embedded-captions/themes/neonsign.json +53 -0
- package/.agents/skills/embedded-captions/themes/nightcity.json +56 -0
- package/.agents/skills/embedded-captions/themes/ordnance.json +54 -0
- package/.agents/skills/embedded-captions/themes/papercut.json +37 -0
- package/.agents/skills/embedded-captions/themes/popup.json +46 -0
- package/.agents/skills/embedded-captions/themes/ransom.json +62 -0
- package/.agents/skills/embedded-captions/themes/scoreboard.json +54 -0
- package/.agents/skills/embedded-captions/themes/spectrum.json +46 -0
- package/.agents/skills/embedded-captions/themes/stardust.json +29 -0
- package/.agents/skills/embedded-captions/themes/stomp.json +43 -0
- package/.agents/skills/embedded-captions/themes/terminal.json +49 -0
- package/.agents/skills/embedded-captions/themes/thunder.json +44 -0
- package/.agents/skills/embedded-captions/themes/transit.json +44 -0
- package/.agents/skills/embedded-captions/themes/vhs.json +47 -0
- package/.agents/skills/faceless-explainer/SKILL.md +209 -0
- package/.agents/skills/faceless-explainer/references/cut-catalog.md +215 -0
- package/.agents/skills/faceless-explainer/references/motion-language.md +156 -0
- package/.agents/skills/faceless-explainer/references/story-design.md +248 -0
- package/.agents/skills/faceless-explainer/references/visual-design.md +146 -0
- package/.agents/skills/faceless-explainer/scripts/assemble-index.mjs +570 -0
- package/.agents/skills/faceless-explainer/scripts/audio.mjs +260 -0
- package/.agents/skills/faceless-explainer/scripts/build-frame.mjs +536 -0
- package/.agents/skills/faceless-explainer/scripts/captions.mjs +508 -0
- package/.agents/skills/faceless-explainer/scripts/lib/assets.mjs +55 -0
- package/.agents/skills/faceless-explainer/scripts/lib/dimensions.mjs +45 -0
- package/.agents/skills/faceless-explainer/scripts/lib/storyboard.mjs +249 -0
- package/.agents/skills/faceless-explainer/scripts/lib/tokens.mjs +204 -0
- package/.agents/skills/faceless-explainer/scripts/lib/transition-registry.mjs +38 -0
- package/.agents/skills/faceless-explainer/scripts/lib/transitions.json +71 -0
- package/.agents/skills/faceless-explainer/scripts/transitions.mjs +320 -0
- package/.agents/skills/faceless-explainer/sub-agents/frame-worker.md +75 -0
- package/.agents/skills/farmville-saas-ux/SKILL.md +156 -0
- package/.agents/skills/farmville-saas-ux/assets/starter.html +294 -0
- package/.agents/skills/farmville-saas-ux/references/components.md +340 -0
- package/.agents/skills/farmville-saas-ux/references/porting-guide.md +121 -0
- package/.agents/skills/farmville-saas-ux/references/tokens.md +271 -0
- package/.agents/skills/flockposter/LICENSE +17 -0
- package/.agents/skills/flockposter/README.md +72 -0
- package/.agents/skills/flockposter/SKILL.md +562 -0
- package/.agents/skills/flockposter/examples/cli-instagram-reel.sh +21 -0
- package/.agents/skills/flockposter/examples/cli-multi-platform.json +69 -0
- package/.agents/skills/flockposter/examples/cli-tiktok-upload-only.sh +22 -0
- package/.agents/skills/flockposter/examples/cli-x-thread.sh +17 -0
- package/.agents/skills/flockposter/examples/cli-youtube-short.sh +21 -0
- package/.agents/skills/flockposter/examples/public-api-instagram-reel.json +28 -0
- package/.agents/skills/general-video/SKILL.md +143 -0
- package/.agents/skills/hyperframes/SKILL.md +160 -0
- package/.agents/skills/hyperframes-animation/SKILL.md +84 -0
- package/.agents/skills/hyperframes-animation/adapters/animate-text.md +64 -0
- package/.agents/skills/hyperframes-animation/adapters/animejs.md +114 -0
- package/.agents/skills/hyperframes-animation/adapters/css-animations.md +143 -0
- package/.agents/skills/hyperframes-animation/adapters/gsap-easing-and-stagger.md +118 -0
- package/.agents/skills/hyperframes-animation/adapters/gsap-timeline-and-labels.md +96 -0
- package/.agents/skills/hyperframes-animation/adapters/gsap-transforms-and-perf.md +96 -0
- package/.agents/skills/hyperframes-animation/adapters/gsap.md +105 -0
- package/.agents/skills/hyperframes-animation/adapters/html-in-canvas-patterns.md +507 -0
- package/.agents/skills/hyperframes-animation/adapters/lottie.md +117 -0
- package/.agents/skills/hyperframes-animation/adapters/three.md +131 -0
- package/.agents/skills/hyperframes-animation/adapters/typegpu.md +178 -0
- package/.agents/skills/hyperframes-animation/adapters/waapi.md +101 -0
- package/.agents/skills/hyperframes-animation/blueprints/comparison-split.md +27 -0
- package/.agents/skills/hyperframes-animation/blueprints/constellation-hub.md +44 -0
- package/.agents/skills/hyperframes-animation/blueprints/cta-morph-press.md +28 -0
- package/.agents/skills/hyperframes-animation/blueprints/cursor-ui-demo.md +57 -0
- package/.agents/skills/hyperframes-animation/blueprints/dataviz-countup.md +46 -0
- package/.agents/skills/hyperframes-animation/blueprints/device-surface-showcase.md +53 -0
- package/.agents/skills/hyperframes-animation/blueprints/grid-card-assemble.md +66 -0
- package/.agents/skills/hyperframes-animation/blueprints/kinetic-type-beats.md +78 -0
- package/.agents/skills/hyperframes-animation/blueprints/logo-assemble-lockup.md +69 -0
- package/.agents/skills/hyperframes-animation/blueprints/overwhelm-surround.md +28 -0
- package/.agents/skills/hyperframes-animation/blueprints/spatial-pan-stations.md +37 -0
- package/.agents/skills/hyperframes-animation/blueprints/ticker-takeover.md +29 -0
- package/.agents/skills/hyperframes-animation/blueprints/titlecard-reveal.md +42 -0
- package/.agents/skills/hyperframes-animation/blueprints/typewriter-reveal.md +51 -0
- package/.agents/skills/hyperframes-animation/blueprints/video-text-pivot.md +30 -0
- package/.agents/skills/hyperframes-animation/blueprints-index.md +146 -0
- package/.agents/skills/hyperframes-animation/examples/assets/avatars/02.avif +0 -0
- package/.agents/skills/hyperframes-animation/examples/assets/brands/github.avif +0 -0
- package/.agents/skills/hyperframes-animation/examples/assets/brands/nvidia.avif +0 -0
- package/.agents/skills/hyperframes-animation/examples/assets/brands/visa.avif +0 -0
- package/.agents/skills/hyperframes-animation/examples/assets/brands/zoominfo.avif +0 -0
- package/.agents/skills/hyperframes-animation/examples/brand-reveal-assemble-zoom.html +382 -0
- package/.agents/skills/hyperframes-animation/examples/comparison-split-cards.html +649 -0
- package/.agents/skills/hyperframes-animation/examples/concept-demo-decode-pan.html +520 -0
- package/.agents/skills/hyperframes-animation/examples/cta-morph-press.html +468 -0
- package/.agents/skills/hyperframes-animation/examples/cta-orbit-collapse.html +1298 -0
- package/.agents/skills/hyperframes-animation/examples/demo-page-scroll-spotlight.html +759 -0
- package/.agents/skills/hyperframes-animation/examples/hook-counter-burst.html +729 -0
- package/.agents/skills/hyperframes-animation/examples/messaging-multi-phrase.html +352 -0
- package/.agents/skills/hyperframes-animation/examples/metric-video-text-pivot.html +779 -0
- package/.agents/skills/hyperframes-animation/examples/problem-mockup-overwhelm.html +1374 -0
- package/.agents/skills/hyperframes-animation/examples/proof-logo-chain.html +861 -0
- package/.agents/skills/hyperframes-animation/examples/takeover-ticker-displace.html +347 -0
- package/.agents/skills/hyperframes-animation/examples/workflow-approve-press.html +606 -0
- package/.agents/skills/hyperframes-animation/rules/3d-page-scroll.md +227 -0
- package/.agents/skills/hyperframes-animation/rules/3d-text-depth-layers.md +297 -0
- package/.agents/skills/hyperframes-animation/rules/ai-tracking-box.md +382 -0
- package/.agents/skills/hyperframes-animation/rules/ambient-glow-bloom.md +305 -0
- package/.agents/skills/hyperframes-animation/rules/asr-keyword-glow.md +286 -0
- package/.agents/skills/hyperframes-animation/rules/avatar-cloud-network.md +371 -0
- package/.agents/skills/hyperframes-animation/rules/camera-cursor-tracking.md +246 -0
- package/.agents/skills/hyperframes-animation/rules/card-morph-anchor.md +267 -0
- package/.agents/skills/hyperframes-animation/rules/center-outward-expansion.md +227 -0
- package/.agents/skills/hyperframes-animation/rules/context-sensitive-cursor.md +257 -0
- package/.agents/skills/hyperframes-animation/rules/coordinate-target-zoom.md +332 -0
- package/.agents/skills/hyperframes-animation/rules/counting-dynamic-scale.md +283 -0
- package/.agents/skills/hyperframes-animation/rules/css-marker-patterns.md +373 -0
- package/.agents/skills/hyperframes-animation/rules/cursor-click-ripple.md +262 -0
- package/.agents/skills/hyperframes-animation/rules/depth-of-field-blur.md +313 -0
- package/.agents/skills/hyperframes-animation/rules/depth-scatter-assemble.md +303 -0
- package/.agents/skills/hyperframes-animation/rules/discrete-text-sequence.md +273 -0
- package/.agents/skills/hyperframes-animation/rules/dynamic-content-sequencing.md +307 -0
- package/.agents/skills/hyperframes-animation/rules/gsap-effects.md +299 -0
- package/.agents/skills/hyperframes-animation/rules/hacker-flip-3d.md +223 -0
- package/.agents/skills/hyperframes-animation/rules/kinetic-beat-slam.md +183 -0
- package/.agents/skills/hyperframes-animation/rules/motion-blur-streak.md +328 -0
- package/.agents/skills/hyperframes-animation/rules/multi-phase-camera.md +273 -0
- package/.agents/skills/hyperframes-animation/rules/orbit-3d-entry.md +301 -0
- package/.agents/skills/hyperframes-animation/rules/physics-press-reaction.md +350 -0
- package/.agents/skills/hyperframes-animation/rules/press-release-spring.md +296 -0
- package/.agents/skills/hyperframes-animation/rules/reactive-displacement.md +277 -0
- package/.agents/skills/hyperframes-animation/rules/scale-swap-transition.md +298 -0
- package/.agents/skills/hyperframes-animation/rules/sine-wave-loop.md +278 -0
- package/.agents/skills/hyperframes-animation/rules/split-tilt-cards.md +277 -0
- package/.agents/skills/hyperframes-animation/rules/spring-pop-entrance.md +273 -0
- package/.agents/skills/hyperframes-animation/rules/stat-bars-and-fills.md +156 -0
- package/.agents/skills/hyperframes-animation/rules/svg-icon-enrichment.md +329 -0
- package/.agents/skills/hyperframes-animation/rules/svg-path-draw.md +274 -0
- package/.agents/skills/hyperframes-animation/rules/vertical-spring-ticker.md +239 -0
- package/.agents/skills/hyperframes-animation/rules/viewport-change.md +349 -0
- package/.agents/skills/hyperframes-animation/rules-index.md +86 -0
- package/.agents/skills/hyperframes-animation/scripts/animation-map.mjs +606 -0
- package/.agents/skills/hyperframes-animation/scripts/package-loader.mjs +288 -0
- package/.agents/skills/hyperframes-animation/scripts/package-loader.test.mjs +62 -0
- package/.agents/skills/hyperframes-animation/techniques.md +507 -0
- package/.agents/skills/hyperframes-animation/transitions/TRANSITION-REGISTRY.md +167 -0
- package/.agents/skills/hyperframes-animation/transitions/catalog.md +127 -0
- package/.agents/skills/hyperframes-animation/transitions/css-3d.md +12 -0
- package/.agents/skills/hyperframes-animation/transitions/css-blur.md +51 -0
- package/.agents/skills/hyperframes-animation/transitions/css-cover.md +43 -0
- package/.agents/skills/hyperframes-animation/transitions/css-destruction.md +95 -0
- package/.agents/skills/hyperframes-animation/transitions/css-dissolve.md +66 -0
- package/.agents/skills/hyperframes-animation/transitions/css-distortion.md +45 -0
- package/.agents/skills/hyperframes-animation/transitions/css-grid.md +10 -0
- package/.agents/skills/hyperframes-animation/transitions/css-light.md +49 -0
- package/.agents/skills/hyperframes-animation/transitions/css-mechanical.md +30 -0
- package/.agents/skills/hyperframes-animation/transitions/css-other.md +25 -0
- package/.agents/skills/hyperframes-animation/transitions/css-push.md +41 -0
- package/.agents/skills/hyperframes-animation/transitions/css-radial.md +37 -0
- package/.agents/skills/hyperframes-animation/transitions/css-scale.md +24 -0
- package/.agents/skills/hyperframes-animation/transitions/overview.md +153 -0
- package/.agents/skills/hyperframes-cli/SKILL.md +113 -0
- package/.agents/skills/hyperframes-cli/references/doctor-browser.md +45 -0
- package/.agents/skills/hyperframes-cli/references/init-and-scaffold.md +55 -0
- package/.agents/skills/hyperframes-cli/references/lambda.md +132 -0
- package/.agents/skills/hyperframes-cli/references/lint-validate-inspect.md +121 -0
- package/.agents/skills/hyperframes-cli/references/preview-render.md +149 -0
- package/.agents/skills/hyperframes-cli/references/upgrade-info-misc.md +75 -0
- package/.agents/skills/hyperframes-core/SKILL.md +78 -0
- package/.agents/skills/hyperframes-core/references/composition-patterns.md +263 -0
- package/.agents/skills/hyperframes-core/references/data-attributes.md +70 -0
- package/.agents/skills/hyperframes-core/references/determinism-rules.md +68 -0
- package/.agents/skills/hyperframes-core/references/full-screen-motion.md +62 -0
- package/.agents/skills/hyperframes-core/references/minimal-composition.md +66 -0
- package/.agents/skills/hyperframes-core/references/script-format.md +49 -0
- package/.agents/skills/hyperframes-core/references/storyboard-format.md +95 -0
- package/.agents/skills/hyperframes-core/references/sub-compositions.md +237 -0
- package/.agents/skills/hyperframes-core/references/subagent-dispatch.md +41 -0
- package/.agents/skills/hyperframes-core/references/tailwind.md +125 -0
- package/.agents/skills/hyperframes-core/references/tracks-and-clips.md +76 -0
- package/.agents/skills/hyperframes-core/references/variables-and-media.md +90 -0
- package/.agents/skills/hyperframes-creative/SKILL.md +70 -0
- package/.agents/skills/hyperframes-creative/frame-presets/biennale-yellow/FRAME.md +289 -0
- package/.agents/skills/hyperframes-creative/frame-presets/biennale-yellow/caption-skin.html +221 -0
- package/.agents/skills/hyperframes-creative/frame-presets/biennale-yellow/frame-showcase.html +1214 -0
- package/.agents/skills/hyperframes-creative/frame-presets/blockframe/FRAME.md +275 -0
- package/.agents/skills/hyperframes-creative/frame-presets/blockframe/caption-skin.html +224 -0
- package/.agents/skills/hyperframes-creative/frame-presets/blockframe/frame-showcase.html +1347 -0
- package/.agents/skills/hyperframes-creative/frame-presets/blue-professional/FRAME.md +307 -0
- package/.agents/skills/hyperframes-creative/frame-presets/blue-professional/caption-skin.html +223 -0
- package/.agents/skills/hyperframes-creative/frame-presets/blue-professional/frame-showcase.html +1372 -0
- package/.agents/skills/hyperframes-creative/frame-presets/bold-poster/FRAME.md +275 -0
- package/.agents/skills/hyperframes-creative/frame-presets/bold-poster/caption-skin.html +230 -0
- package/.agents/skills/hyperframes-creative/frame-presets/bold-poster/frame-showcase.html +1140 -0
- package/.agents/skills/hyperframes-creative/frame-presets/broadside/FRAME.md +284 -0
- package/.agents/skills/hyperframes-creative/frame-presets/broadside/caption-skin.html +225 -0
- package/.agents/skills/hyperframes-creative/frame-presets/broadside/frame-showcase.html +1166 -0
- package/.agents/skills/hyperframes-creative/frame-presets/capsule/FRAME.md +284 -0
- package/.agents/skills/hyperframes-creative/frame-presets/capsule/caption-skin.html +224 -0
- package/.agents/skills/hyperframes-creative/frame-presets/capsule/frame-showcase.html +1465 -0
- package/.agents/skills/hyperframes-creative/frame-presets/cartesian/FRAME.md +278 -0
- package/.agents/skills/hyperframes-creative/frame-presets/cartesian/caption-skin.html +222 -0
- package/.agents/skills/hyperframes-creative/frame-presets/cartesian/frame-showcase.html +1263 -0
- package/.agents/skills/hyperframes-creative/frame-presets/claude/FRAME.md +277 -0
- package/.agents/skills/hyperframes-creative/frame-presets/claude/caption-skin.html +224 -0
- package/.agents/skills/hyperframes-creative/frame-presets/claude/frame-showcase.html +1439 -0
- package/.agents/skills/hyperframes-creative/frame-presets/cobalt-grid/FRAME.md +265 -0
- package/.agents/skills/hyperframes-creative/frame-presets/cobalt-grid/caption-skin.html +238 -0
- package/.agents/skills/hyperframes-creative/frame-presets/cobalt-grid/frame-showcase.html +1468 -0
- package/.agents/skills/hyperframes-creative/frame-presets/coral/FRAME.md +314 -0
- package/.agents/skills/hyperframes-creative/frame-presets/coral/caption-skin.html +226 -0
- package/.agents/skills/hyperframes-creative/frame-presets/coral/frame-showcase.html +1268 -0
- package/.agents/skills/hyperframes-creative/frame-presets/creative-mode/FRAME.md +333 -0
- package/.agents/skills/hyperframes-creative/frame-presets/creative-mode/caption-skin.html +227 -0
- package/.agents/skills/hyperframes-creative/frame-presets/creative-mode/frame-showcase.html +1330 -0
- package/.agents/skills/hyperframes-creative/frame-presets/daisy-days/FRAME.md +274 -0
- package/.agents/skills/hyperframes-creative/frame-presets/daisy-days/caption-skin.html +230 -0
- package/.agents/skills/hyperframes-creative/frame-presets/daisy-days/frame-showcase.html +1444 -0
- package/.agents/skills/hyperframes-creative/frame-presets/editorial-forest/FRAME.md +265 -0
- package/.agents/skills/hyperframes-creative/frame-presets/editorial-forest/caption-skin.html +223 -0
- package/.agents/skills/hyperframes-creative/frame-presets/editorial-forest/frame-showcase.html +1273 -0
- package/.agents/skills/hyperframes-creative/palettes/bold-energetic.md +14 -0
- package/.agents/skills/hyperframes-creative/palettes/clean-corporate.md +14 -0
- package/.agents/skills/hyperframes-creative/palettes/dark-premium.md +14 -0
- package/.agents/skills/hyperframes-creative/palettes/jewel-rich.md +14 -0
- package/.agents/skills/hyperframes-creative/palettes/monochrome.md +14 -0
- package/.agents/skills/hyperframes-creative/palettes/nature-earth.md +14 -0
- package/.agents/skills/hyperframes-creative/palettes/neon-electric.md +14 -0
- package/.agents/skills/hyperframes-creative/palettes/pastel-soft.md +14 -0
- package/.agents/skills/hyperframes-creative/palettes/warm-editorial.md +14 -0
- package/.agents/skills/hyperframes-creative/references/audio-reactive.md +76 -0
- package/.agents/skills/hyperframes-creative/references/beat-direction.md +164 -0
- package/.agents/skills/hyperframes-creative/references/composition-patterns.md +199 -0
- package/.agents/skills/hyperframes-creative/references/data-in-motion.md +19 -0
- package/.agents/skills/hyperframes-creative/references/design-adherence.md +19 -0
- package/.agents/skills/hyperframes-creative/references/design-picker.md +123 -0
- package/.agents/skills/hyperframes-creative/references/design-spec.md +55 -0
- package/.agents/skills/hyperframes-creative/references/house-style.md +73 -0
- package/.agents/skills/hyperframes-creative/references/motion-principles.md +150 -0
- package/.agents/skills/hyperframes-creative/references/narration.md +92 -0
- package/.agents/skills/hyperframes-creative/references/prompt-expansion.md +68 -0
- package/.agents/skills/hyperframes-creative/references/typography.md +218 -0
- package/.agents/skills/hyperframes-creative/references/video-composition.md +63 -0
- package/.agents/skills/hyperframes-creative/references/visual-styles.md +457 -0
- package/.agents/skills/hyperframes-creative/scripts/contrast-report.mjs +353 -0
- package/.agents/skills/hyperframes-creative/scripts/extract-audio-data.py +188 -0
- package/.agents/skills/hyperframes-creative/scripts/package-loader.mjs +288 -0
- package/.agents/skills/hyperframes-creative/scripts/package-loader.test.mjs +62 -0
- package/.agents/skills/hyperframes-creative/templates/design-picker.html +1432 -0
- package/.agents/skills/hyperframes-keyframes/SKILL.md +237 -0
- package/.agents/skills/hyperframes-keyframes/agents/openai.yaml +4 -0
- package/.agents/skills/hyperframes-keyframes/references/keyframe-patterns.md +106 -0
- package/.agents/skills/motion-graphics/SKILL.md +172 -0
- package/.agents/skills/motion-graphics/agents/builder.md +40 -0
- package/.agents/skills/motion-graphics/agents/director.md +53 -0
- package/.agents/skills/motion-graphics/agents/finalize.md +17 -0
- package/.agents/skills/motion-graphics/catalog-map.md +40 -0
- package/.agents/skills/motion-graphics/categories/asset-fusion/module.md +37 -0
- package/.agents/skills/motion-graphics/categories/charts/module.md +21 -0
- package/.agents/skills/motion-graphics/categories/kinetic-type/module.md +19 -0
- package/.agents/skills/motion-graphics/categories/logo-reveal/module.md +17 -0
- package/.agents/skills/motion-graphics/categories/lower-thirds/module.md +16 -0
- package/.agents/skills/motion-graphics/categories/maps/bake-basemap.mjs +273 -0
- package/.agents/skills/motion-graphics/categories/maps/module.md +61 -0
- package/.agents/skills/motion-graphics/categories/news/module.md +56 -0
- package/.agents/skills/motion-graphics/categories/stat/module.md +21 -0
- package/.agents/skills/motion-graphics/categories/tweet/module.md +16 -0
- package/.agents/skills/motion-graphics/categories/webpage/module.md +21 -0
- package/.agents/skills/motion-graphics/grounding/PROTOCOL.md +64 -0
- package/.agents/skills/motion-graphics/grounding/locate.mjs +227 -0
- package/.agents/skills/motion-graphics/phases/source/guide.md +21 -0
- package/.agents/skills/motion-graphics/references/builder-contract.md +38 -0
- package/.agents/skills/motion-graphics/references/motion-vocabulary.md +43 -0
- package/.agents/skills/motion-graphics/references/shot-plan-ir.md +54 -0
- package/.agents/skills/motion-graphics/samples/asset-fusion/_ref-circle-highlight.html +306 -0
- package/.agents/skills/music/SKILL.md +416 -0
- package/.agents/skills/music/references/api_reference.md +519 -0
- package/.agents/skills/music/references/installation.md +65 -0
- package/.agents/skills/product-launch-video/SKILL.md +208 -0
- package/.agents/skills/product-launch-video/references/cut-catalog.md +220 -0
- package/.agents/skills/product-launch-video/references/motion-language.md +156 -0
- package/.agents/skills/product-launch-video/references/story-design.md +373 -0
- package/.agents/skills/product-launch-video/references/visual-design.md +126 -0
- package/.agents/skills/product-launch-video/scripts/assemble-index.mjs +570 -0
- package/.agents/skills/product-launch-video/scripts/audio.mjs +260 -0
- package/.agents/skills/product-launch-video/scripts/build-frame.mjs +536 -0
- package/.agents/skills/product-launch-video/scripts/captions.mjs +508 -0
- package/.agents/skills/product-launch-video/scripts/lib/assets.mjs +55 -0
- package/.agents/skills/product-launch-video/scripts/lib/dimensions.mjs +45 -0
- package/.agents/skills/product-launch-video/scripts/lib/pad-frame-duration.mjs +36 -0
- package/.agents/skills/product-launch-video/scripts/lib/pad-frame-duration.test.mjs +76 -0
- package/.agents/skills/product-launch-video/scripts/lib/storyboard.mjs +249 -0
- package/.agents/skills/product-launch-video/scripts/lib/tokens.mjs +204 -0
- package/.agents/skills/product-launch-video/scripts/lib/transition-registry.mjs +38 -0
- package/.agents/skills/product-launch-video/scripts/lib/transitions.json +71 -0
- package/.agents/skills/product-launch-video/scripts/stage-assets.mjs +39 -0
- package/.agents/skills/product-launch-video/scripts/transitions.mjs +327 -0
- package/.agents/skills/product-launch-video/sub-agents/frame-worker.md +75 -0
- package/.agents/skills/slideshow/SKILL.md +550 -0
- package/.agents/skills/slideshow/references/standalone-harness.md +1020 -0
- package/.agents/skills/talking-head-recut/NOTICE.md +39 -0
- package/.agents/skills/talking-head-recut/SKILL.md +1195 -0
- package/.agents/skills/talking-head-recut/assets/vendor/gsap.min.js +10 -0
- package/.agents/skills/talking-head-recut/references/DESIGN_INDEX.md +145 -0
- package/.agents/skills/talking-head-recut/references/frames/clean.html +92 -0
- package/.agents/skills/talking-head-recut/references/frames/hairline.html +171 -0
- package/.agents/skills/talking-head-recut/references/frames/polaroid.html +131 -0
- package/.agents/skills/talking-head-recut/references/layouts/overlay.html +164 -0
- package/.agents/skills/talking-head-recut/references/layouts/pip.html +143 -0
- package/.agents/skills/talking-head-recut/references/layouts/split.html +121 -0
- package/.agents/skills/talking-head-recut/references/layouts/stack.html +109 -0
- package/.agents/skills/talking-head-recut/references/styles/academic.html +172 -0
- package/.agents/skills/talking-head-recut/references/styles/audit.html +170 -0
- package/.agents/skills/talking-head-recut/references/styles/editorial.html +189 -0
- package/.agents/skills/talking-head-recut/references/styles/geom.html +181 -0
- package/.agents/skills/talking-head-recut/references/styles/minimal.html +130 -0
- package/.agents/skills/talking-head-recut/references/styles/spotlight.html +142 -0
- package/.agents/skills/talking-head-recut/references/styles/swiss.html +188 -0
- package/.agents/skills/talking-head-recut/references/styles/terminal.html +196 -0
- package/.agents/skills/talking-head-recut/references/styles/whiteboard.html +170 -0
- package/.agents/skills/talking-head-recut/references/styles/xhs.html +206 -0
- package/.agents/skills/text-to-speech/SKILL.md +226 -0
- package/.agents/skills/text-to-speech/references/installation.md +90 -0
- package/.agents/skills/text-to-speech/references/streaming.md +307 -0
- package/.agents/skills/text-to-speech/references/voice-settings.md +115 -0
- package/.agents/skills/vidfarm-director/SKILL.md +179 -0
- package/.agents/skills/vidfarm-director/recipes/find-and-fork-template.md +16 -0
- package/.agents/skills/vidfarm-director/recipes/local-edit-render-approve.md +13 -0
- package/.agents/skills/vidfarm-director/recipes/onboard-a-new-director.md +13 -0
- package/.agents/skills/vidfarm-director/recipes/retheme-template.md +17 -0
- package/.agents/skills/vidfarm-director/references/assets-and-sourcing.md +117 -0
- package/.agents/skills/vidfarm-director/references/automation-and-local-dev.md +276 -0
- package/.agents/skills/vidfarm-director/references/core-workflows.md +301 -0
- package/.agents/skills/vidfarm-director/references/editor-workflows.md +367 -0
- package/.agents/skills/vidfarm-director/references/onboarding.md +28 -0
- package/.agents/skills/vidfarm-director/references/primitives.md +307 -0
- package/.agents/skills/vidfarm-director/references/rest-api.md +84 -0
- package/.agents/skills/vidfarm-media/SKILL.md +158 -0
- package/.agents/skills/vidfarm-media/references/tts.md +111 -0
- package/.agents/skills/vidfarm-media/scripts/audio.mjs +628 -0
- package/.agents/skills/vidfarm-media/scripts/wait-bgm.mjs +31 -0
- package/.agents/skills/website-to-video/SKILL.md +145 -0
- package/.agents/skills/website-to-video/assets/sfx/CREDITS.md +35 -0
- package/.agents/skills/website-to-video/assets/sfx/manifest.json +97 -0
- package/.agents/skills/website-to-video/references/beat-builder-guide.md +263 -0
- package/.agents/skills/website-to-video/references/capabilities.md +713 -0
- package/.agents/skills/website-to-video/references/step-0-capture.md +55 -0
- package/.agents/skills/website-to-video/references/step-1-design.md +333 -0
- package/.agents/skills/website-to-video/references/step-2-brief.md +178 -0
- package/.agents/skills/website-to-video/references/step-3-storyboard.md +572 -0
- package/.agents/skills/website-to-video/references/step-4-vo.md +225 -0
- package/.agents/skills/website-to-video/references/step-5-build.md +483 -0
- package/.agents/skills/website-to-video/references/step-6-validate.md +341 -0
- package/.agents/skills/website-to-video/scripts/w2h-verify.mjs +759 -0
- package/README.md +97 -0
- package/SKILL.director.md +1644 -0
- package/SKILL.md +83 -0
- package/demo/README.md +28 -0
- package/demo/dist/app.css +1 -0
- package/demo/dist/app.js +968 -0
- package/demo/dist/chunks/chunk-3OT2D42H.js +1 -0
- package/demo/dist/chunks/chunk-DXB73IDG.js +1 -0
- package/demo/dist/chunks/domEditingLayers-AT7G6F4L-ZTHJHB7R.js +1 -0
- package/demo/dist/chunks/hyperframes-player-7HKH6NSO.js +459 -0
- package/demo/dist/chunks/lib-XAQ37YOE.js +1 -0
- package/demo/dist/chunks/src-TJ2QYA4U.js +207 -0
- package/demo/dist/favicon.ico +0 -0
- package/demo/dist/icons/timeline/audio.svg +7 -0
- package/demo/dist/icons/timeline/captions.svg +5 -0
- package/demo/dist/icons/timeline/composition.svg +12 -0
- package/demo/dist/icons/timeline/image.svg +18 -0
- package/demo/dist/icons/timeline/music.svg +10 -0
- package/demo/dist/icons/timeline/text.svg +3 -0
- package/demo/dist/index.html +16 -0
- package/dist/src/cli.js +7258 -0
- package/dist/src/devcli/auth-store.js +67 -0
- package/dist/src/devcli/captions.js +310 -0
- package/dist/src/devcli/clip-store.js +368 -0
- package/dist/src/devcli/clips.js +1138 -0
- package/dist/src/devcli/composition-edit.js +1292 -0
- package/dist/src/devcli/cost-mode.js +149 -0
- package/dist/src/devcli/doctor.js +192 -0
- package/dist/src/devcli/hyperframes-cli.js +118 -0
- package/dist/src/devcli/local-backend.js +167 -0
- package/dist/src/devcli/local-frontend-server.js +681 -0
- package/dist/src/devcli/local-render.js +136 -0
- package/dist/src/devcli/skills.js +476 -0
- package/dist/src/devcli/speech.js +178 -0
- package/dist/src/devcli/stills.js +249 -0
- package/dist/src/devcli/telemetry.js +236 -0
- package/dist/src/devcli/timeline-edit.js +490 -0
- package/dist/src/devcli/transitions.js +205 -0
- package/dist/src/hyperframes/composition.js +780 -0
- package/dist/src/lib/crypto.js +45 -0
- package/dist/src/lib/dev-log.js +54 -0
- package/dist/src/lib/display-name.js +11 -0
- package/dist/src/lib/ids.js +24 -0
- package/dist/src/lib/images.js +19 -0
- package/dist/src/lib/json.js +15 -0
- package/dist/src/lib/package-root.js +47 -0
- package/dist/src/lib/render-media-prep.js +293 -0
- package/dist/src/lib/template-paths.js +28 -0
- package/dist/src/lib/time.js +7 -0
- package/dist/src/lib/url-clean.js +85 -0
- package/dist/src/services/captions.js +123 -0
- package/dist/src/services/clip-curation/cost.js +116 -0
- package/dist/src/services/clip-curation/ffmpeg.js +352 -0
- package/dist/src/services/clip-curation/gemini.js +418 -0
- package/dist/src/services/clip-curation/hunt.js +470 -0
- package/dist/src/services/clip-curation/index.js +19 -0
- package/dist/src/services/clip-curation/local-agent.js +248 -0
- package/dist/src/services/clip-curation/media-select.js +152 -0
- package/dist/src/services/clip-curation/presets.js +20 -0
- package/dist/src/services/clip-curation/presets.v1.json +59 -0
- package/dist/src/services/clip-curation/query.js +167 -0
- package/dist/src/services/clip-curation/refine.js +165 -0
- package/dist/src/services/clip-curation/scan.js +189 -0
- package/dist/src/services/clip-curation/source-naming.js +131 -0
- package/dist/src/services/clip-curation/taxonomy.js +73 -0
- package/dist/src/services/clip-curation/taxonomy.v1.json +102 -0
- package/dist/src/services/clip-curation/types.js +7 -0
- package/dist/src/services/composition-lint.js +199 -0
- package/dist/src/services/provider-errors.js +128 -0
- package/dist/src/services/speech.js +629 -0
- package/package.json +154 -0
- package/public/assets/discover-client-app.js +1 -0
- package/public/assets/favicon.ico +0 -0
- package/public/assets/file-directory-app.js +89 -0
- package/public/assets/homepage-app.js +54 -0
- package/public/assets/homepage-client-app.js +80 -0
- package/public/assets/landing-page-client-app.js +87 -0
- package/public/assets/logo-vidfarm.png +0 -0
- package/public/assets/page-runtime-client-app.js +94 -0
- package/public/assets/placeholders/scene-placeholder.png +0 -0
- package/public/serve-shells/editor.html +3146 -0
- package/public/serve-shells/library-files.html +3450 -0
- package/public/serve-shells/library-raws.html +4404 -0
- package/public/serve-shells/tools-clipper.html +3991 -0
- package/public/serve-shells/tools-image.html +5584 -0
- package/public/serve-shells/tools-video.html +4404 -0
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
// Local-agent model client — devcli-only. Runs scene tagging, prompt-guided
|
|
2
|
+
// keep/drop/merge, and NL→criteria through the user's LOCAL agent CLI
|
|
3
|
+
// subscription (claude or codex) instead of a provider API key, so `vidfarm
|
|
4
|
+
// clips scan` works out of the box on a machine that has Claude Code or Codex
|
|
5
|
+
// installed: local ffmpeg for compute, local agent for evaluation. Cloud is the
|
|
6
|
+
// explicit backup (devcli `--cloud`), never the default. Embeddings cannot ride
|
|
7
|
+
// on a CLI agent — pair with a gemini/openai key (embedding-only) or the
|
|
8
|
+
// library stays structured-searchable, the same degraded mode as
|
|
9
|
+
// openrouter-without-embed-key. NEVER constructed in Lambda.
|
|
10
|
+
import { spawn } from "node:child_process";
|
|
11
|
+
import { spawnSync } from "node:child_process";
|
|
12
|
+
import { ACTIVE_TAXONOMY_VERSION, getTaxonomy, sanitizeTagValues } from "./taxonomy.js";
|
|
13
|
+
import { buildQueryPrompt, buildTaggingPrompt, ClipEmbeddingClient } from "./gemini.js";
|
|
14
|
+
/** Find a usable local agent CLI on PATH. Returns null when none is installed. */
|
|
15
|
+
export function detectLocalAgent(preferred) {
|
|
16
|
+
const candidates = preferred === "claude" ? ["claude"]
|
|
17
|
+
: preferred === "codex" ? ["codex"]
|
|
18
|
+
: ["claude", "codex"];
|
|
19
|
+
for (const kind of candidates) {
|
|
20
|
+
const found = spawnSync("which", [kind], { encoding: "utf8" });
|
|
21
|
+
const bin = (found.stdout || "").trim();
|
|
22
|
+
if (found.status === 0 && bin)
|
|
23
|
+
return { kind, bin };
|
|
24
|
+
}
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
export class LocalAgentClipClient {
|
|
28
|
+
provider = "local-agent";
|
|
29
|
+
tier;
|
|
30
|
+
taxonomyVersion;
|
|
31
|
+
tagModelId;
|
|
32
|
+
embeddingProvider;
|
|
33
|
+
embeddingModelId;
|
|
34
|
+
kind;
|
|
35
|
+
bin;
|
|
36
|
+
embedder;
|
|
37
|
+
timeoutMs;
|
|
38
|
+
constructor(opts = {}) {
|
|
39
|
+
const detected = opts.bin
|
|
40
|
+
? { kind: (opts.agent === "codex" ? "codex" : "claude"), bin: opts.bin }
|
|
41
|
+
: detectLocalAgent(opts.agent === "auto" ? undefined : opts.agent);
|
|
42
|
+
if (!detected) {
|
|
43
|
+
throw new Error("No local agent CLI found. Install Claude Code (`claude`) or Codex (`codex`), pass a provider key (--gemini-key / GEMINI_API_KEY), or run with --cloud.");
|
|
44
|
+
}
|
|
45
|
+
this.kind = detected.kind;
|
|
46
|
+
this.bin = detected.bin;
|
|
47
|
+
this.tagModelId = `${this.kind}-cli`;
|
|
48
|
+
this.tier = opts.tier ?? "flash-lite";
|
|
49
|
+
this.taxonomyVersion = opts.taxonomyVersion ?? ACTIVE_TAXONOMY_VERSION;
|
|
50
|
+
this.embedder = opts.embedding ? new ClipEmbeddingClient(opts.embedding) : null;
|
|
51
|
+
this.embeddingProvider = this.embedder ? this.embedder.provider : null;
|
|
52
|
+
this.embeddingModelId = this.embedder ? this.embedder.modelId : null;
|
|
53
|
+
this.timeoutMs = Math.max(30_000, opts.timeoutMs ?? 300_000);
|
|
54
|
+
}
|
|
55
|
+
get canEmbed() {
|
|
56
|
+
return this.embedder !== null;
|
|
57
|
+
}
|
|
58
|
+
/** Tag one scene: the agent Reads the keyframe files itself, then emits the tag JSON. */
|
|
59
|
+
async tagScene(input) {
|
|
60
|
+
// No audio path — CLI agents can't transcribe the mp3; transcript stays
|
|
61
|
+
// whatever the caller supplied (usually empty on local-agent scans).
|
|
62
|
+
const base = buildTaggingPrompt(input.transcript, this.taxonomyVersion, false, input.guidancePrompt);
|
|
63
|
+
const prompt = [
|
|
64
|
+
base,
|
|
65
|
+
"",
|
|
66
|
+
`The scene's keyframe image files are on disk — open and inspect EVERY one of these ${input.keyframePaths.length} file(s) before answering:`,
|
|
67
|
+
...input.keyframePaths.map((p) => `- ${p}`),
|
|
68
|
+
"",
|
|
69
|
+
"Respond with ONLY the JSON object (no prose, no markdown fence) with keys:",
|
|
70
|
+
`content_type, subject, action, emotion, setting, composition, motion (string arrays), energy (string), audio_type (string array), transcript (string), description (string).`,
|
|
71
|
+
`content_type is the KIND of shot — pick 1-2 of: talking_head, b_roll, product_shot, screen_recording, demo, reaction, interview, establishing, lifestyle, text_graphic.`
|
|
72
|
+
].join("\n");
|
|
73
|
+
try {
|
|
74
|
+
const raw = await this.runAgent(prompt);
|
|
75
|
+
const parsed = extractJsonObject(raw);
|
|
76
|
+
if (!parsed)
|
|
77
|
+
throw new Error("local agent returned no JSON");
|
|
78
|
+
const withDefaults = {
|
|
79
|
+
content_type: [], subject: [], action: [], emotion: [], setting: [], composition: [], motion: [],
|
|
80
|
+
energy: "medium", audio_type: [], transcript: "", description: "",
|
|
81
|
+
...parsed
|
|
82
|
+
};
|
|
83
|
+
const sanitized = sanitizeTagValues(withDefaults, this.taxonomyVersion);
|
|
84
|
+
const tags = {
|
|
85
|
+
...sanitized,
|
|
86
|
+
transcript: (input.transcript || String(withDefaults.transcript ?? "")).trim()
|
|
87
|
+
};
|
|
88
|
+
return { tags, description: String(withDefaults.description ?? "").trim() };
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
92
|
+
// eslint-disable-next-line no-console
|
|
93
|
+
console.warn(`[clips] tag failed (local-agent/${this.kind}) @${input.scene.start_time_sec.toFixed(1)}s: ${message}`);
|
|
94
|
+
return {
|
|
95
|
+
tags: {
|
|
96
|
+
content_type: [], subject: [], action: [], emotion: [], setting: [], composition: [], motion: [],
|
|
97
|
+
energy: "medium",
|
|
98
|
+
audio_type: input.transcript ? ["dialogue"] : ["silence"],
|
|
99
|
+
transcript: input.transcript || ""
|
|
100
|
+
},
|
|
101
|
+
description: input.transcript ? input.transcript.slice(0, 120) : ""
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/** Keep/drop/merge for prompt-guided segmentation via the local agent. */
|
|
106
|
+
async decideScenes(input) {
|
|
107
|
+
const prompt = [
|
|
108
|
+
input.prompt,
|
|
109
|
+
"",
|
|
110
|
+
`The candidate scenes' keyframe image files are on disk, in timeline order — open and inspect EVERY one of these ${input.imagePaths.length} file(s) before deciding:`,
|
|
111
|
+
...input.imagePaths.map((p, i) => `- scene ${i}: ${p}`),
|
|
112
|
+
"",
|
|
113
|
+
`Respond with ONLY a JSON object (no prose, no markdown fence): {"decisions":[{"keep":true|false,"merge_with_previous":true|false}, …]} with exactly ${input.imagePaths.length} entries in scene order.`
|
|
114
|
+
].join("\n");
|
|
115
|
+
try {
|
|
116
|
+
const raw = await this.runAgent(prompt);
|
|
117
|
+
const parsed = extractJsonObject(raw);
|
|
118
|
+
if (!parsed || !Array.isArray(parsed.decisions))
|
|
119
|
+
return null;
|
|
120
|
+
return parsed.decisions.map((d) => ({
|
|
121
|
+
keep: Boolean(d?.keep),
|
|
122
|
+
merge_with_previous: Boolean(d?.merge_with_previous)
|
|
123
|
+
}));
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
if (process.env.VIDFARM_DEBUG_LOGS === "true") {
|
|
127
|
+
// eslint-disable-next-line no-console
|
|
128
|
+
console.warn(`[clips] local-agent refine failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
129
|
+
}
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
async embedDocuments(texts) {
|
|
134
|
+
return this.embedder ? this.embedder.embed(texts, "RETRIEVAL_DOCUMENT") : [];
|
|
135
|
+
}
|
|
136
|
+
async embedQuery(text) {
|
|
137
|
+
if (!this.embedder)
|
|
138
|
+
return undefined;
|
|
139
|
+
const [vec] = await this.embedder.embed([text], "RETRIEVAL_QUERY");
|
|
140
|
+
return vec;
|
|
141
|
+
}
|
|
142
|
+
async embedQueries(texts) {
|
|
143
|
+
return this.embedder ? this.embedder.embed(texts, "RETRIEVAL_QUERY") : [];
|
|
144
|
+
}
|
|
145
|
+
/** NL → structured criteria (text-only, so a clean fit for a CLI agent). */
|
|
146
|
+
async naturalLanguageToCriteria(query) {
|
|
147
|
+
const prompt = [
|
|
148
|
+
buildQueryPrompt(query, this.taxonomyVersion),
|
|
149
|
+
"",
|
|
150
|
+
"Respond with ONLY the JSON object (no prose, no markdown fence)."
|
|
151
|
+
].join("\n");
|
|
152
|
+
try {
|
|
153
|
+
const raw = await this.runAgent(prompt);
|
|
154
|
+
const parsed = extractJsonObject(raw);
|
|
155
|
+
if (!parsed)
|
|
156
|
+
return { semantic_text: query };
|
|
157
|
+
return { ...sanitizeCriteria(parsed, this.taxonomyVersion), semantic_text: query };
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
return { semantic_text: query };
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
// ── CLI plumbing ──────────────────────────────────────────────────────────
|
|
164
|
+
runAgent(prompt) {
|
|
165
|
+
const args = this.kind === "claude"
|
|
166
|
+
? [
|
|
167
|
+
"-p", prompt,
|
|
168
|
+
"--output-format", "text",
|
|
169
|
+
// Reading N keyframes = N tool turns; leave headroom.
|
|
170
|
+
"--max-turns", "50",
|
|
171
|
+
"--allowedTools", "Read",
|
|
172
|
+
...(process.env.VIDFARM_LOCAL_AGENT_MODEL ? ["--model", process.env.VIDFARM_LOCAL_AGENT_MODEL] : [])
|
|
173
|
+
]
|
|
174
|
+
: [
|
|
175
|
+
"exec",
|
|
176
|
+
"--skip-git-repo-check",
|
|
177
|
+
...(process.env.VIDFARM_LOCAL_AGENT_MODEL ? ["--model", process.env.VIDFARM_LOCAL_AGENT_MODEL] : []),
|
|
178
|
+
prompt
|
|
179
|
+
];
|
|
180
|
+
return new Promise((resolve, reject) => {
|
|
181
|
+
const child = spawn(this.bin, args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
182
|
+
let stdout = "";
|
|
183
|
+
let stderr = "";
|
|
184
|
+
const timer = setTimeout(() => {
|
|
185
|
+
child.kill("SIGKILL");
|
|
186
|
+
reject(new Error(`${this.kind} CLI timed out after ${Math.round(this.timeoutMs / 1000)}s`));
|
|
187
|
+
}, this.timeoutMs);
|
|
188
|
+
timer.unref?.();
|
|
189
|
+
child.stdout.on("data", (d) => (stdout += d.toString()));
|
|
190
|
+
child.stderr.on("data", (d) => (stderr += d.toString()));
|
|
191
|
+
child.on("error", (error) => {
|
|
192
|
+
clearTimeout(timer);
|
|
193
|
+
reject(error);
|
|
194
|
+
});
|
|
195
|
+
child.on("close", (code) => {
|
|
196
|
+
clearTimeout(timer);
|
|
197
|
+
if (code === 0)
|
|
198
|
+
resolve(stdout);
|
|
199
|
+
else
|
|
200
|
+
reject(new Error(`${this.kind} CLI exited ${code}: ${(stderr || stdout).slice(0, 400)}`));
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
/** Pull the first parseable JSON object out of agent output (fenced block, or outermost braces). */
|
|
206
|
+
export function extractJsonObject(text) {
|
|
207
|
+
const trimmed = (text ?? "").trim();
|
|
208
|
+
if (!trimmed)
|
|
209
|
+
return null;
|
|
210
|
+
const fenced = /```(?:json)?\s*([\s\S]*?)```/.exec(trimmed);
|
|
211
|
+
for (const candidate of [fenced?.[1], trimmed]) {
|
|
212
|
+
if (!candidate)
|
|
213
|
+
continue;
|
|
214
|
+
const start = candidate.indexOf("{");
|
|
215
|
+
const end = candidate.lastIndexOf("}");
|
|
216
|
+
if (start < 0 || end <= start)
|
|
217
|
+
continue;
|
|
218
|
+
try {
|
|
219
|
+
return JSON.parse(candidate.slice(start, end + 1));
|
|
220
|
+
}
|
|
221
|
+
catch {
|
|
222
|
+
/* try the next candidate */
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
/** Constrain agent-produced criteria to the controlled vocabulary + known fields. */
|
|
228
|
+
function sanitizeCriteria(raw, version) {
|
|
229
|
+
const cats = getTaxonomy(version).categories;
|
|
230
|
+
const out = {};
|
|
231
|
+
const arrayFields = ["subject", "action", "emotion", "setting", "composition", "motion", "audio_type", "energy"];
|
|
232
|
+
for (const field of arrayFields) {
|
|
233
|
+
const allowed = new Set(cats[field] ?? []);
|
|
234
|
+
const values = Array.isArray(raw[field]) ? raw[field].map(String).filter((v) => allowed.has(v)) : [];
|
|
235
|
+
if (values.length)
|
|
236
|
+
out[field] = values;
|
|
237
|
+
}
|
|
238
|
+
if (Number.isFinite(Number(raw.min_duration_sec)))
|
|
239
|
+
out.min_duration_sec = Number(raw.min_duration_sec);
|
|
240
|
+
if (Number.isFinite(Number(raw.max_duration_sec)))
|
|
241
|
+
out.max_duration_sec = Number(raw.max_duration_sec);
|
|
242
|
+
if (typeof raw.has_dialogue === "boolean")
|
|
243
|
+
out.has_dialogue = raw.has_dialogue;
|
|
244
|
+
if (typeof raw.has_subject === "boolean")
|
|
245
|
+
out.has_subject = raw.has_subject;
|
|
246
|
+
return out;
|
|
247
|
+
}
|
|
248
|
+
//# sourceMappingURL=local-agent.js.map
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
// Resolution-aware media selection for the RapidAPI social-video downloader.
|
|
2
|
+
// The lookup returns several renditions per source (e.g. 360p / 720p / 1080p,
|
|
3
|
+
// sometimes audio-only or HLS playlists). The clip library's quality is capped
|
|
4
|
+
// by whatever source we ingest, so we must pick the HIGHEST-resolution playable
|
|
5
|
+
// MP4 — not just the first one. Shared by the clip-scan Lambda (infra/lambda)
|
|
6
|
+
// and the in-process serve/import path (src/app.ts) so both stay in lock-step.
|
|
7
|
+
/** True when a candidate is a directly-downloadable MP4 (not HLS/DASH/audio). */
|
|
8
|
+
function isPlayableMp4(m) {
|
|
9
|
+
if (!m.url)
|
|
10
|
+
return false;
|
|
11
|
+
if (m.videoAvailable === false)
|
|
12
|
+
return false;
|
|
13
|
+
if (m.type && !/video/i.test(m.type))
|
|
14
|
+
return false;
|
|
15
|
+
// HLS/DASH playlists can't be fetched as a single file — skip them.
|
|
16
|
+
if (/\.(m3u8|mpd)(\?|$)/i.test(m.url))
|
|
17
|
+
return false;
|
|
18
|
+
const ext = (m.extension ?? "").toLowerCase();
|
|
19
|
+
return ext === "mp4" || /\.mp4(\?|$)/i.test(m.url);
|
|
20
|
+
}
|
|
21
|
+
/** Any playable video (mp4 preferred, but a non-HLS fallback still beats nothing). */
|
|
22
|
+
function isPlayableVideo(m) {
|
|
23
|
+
if (!m.url)
|
|
24
|
+
return false;
|
|
25
|
+
if (m.videoAvailable === false)
|
|
26
|
+
return false;
|
|
27
|
+
if (m.type && !/video/i.test(m.type))
|
|
28
|
+
return false;
|
|
29
|
+
if (/\.(m3u8|mpd)(\?|$)/i.test(m.url))
|
|
30
|
+
return false;
|
|
31
|
+
// Skip audio-only renditions (some resolvers list mp3/m4a alongside the video).
|
|
32
|
+
const ext = (m.extension ?? "").toLowerCase();
|
|
33
|
+
if (/^(mp3|m4a|aac|wav|ogg|opus)$/.test(ext) || /\.(mp3|m4a|aac|wav|ogg|opus)(\?|$)/i.test(m.url))
|
|
34
|
+
return false;
|
|
35
|
+
if (m.audioAvailable === true && m.videoAvailable !== true && !ext) {
|
|
36
|
+
// audio flagged, video not flagged, and no extension to prove otherwise
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Estimate a rendition's vertical resolution (used as the ranking key). Reads an
|
|
43
|
+
* explicit height first, then parses the quality/label string. Returns 0 when
|
|
44
|
+
* nothing is parseable so labelled renditions always outrank unknown ones.
|
|
45
|
+
*/
|
|
46
|
+
export function estimateMediaHeight(m) {
|
|
47
|
+
const h = Number(m.height);
|
|
48
|
+
if (Number.isFinite(h) && h > 0)
|
|
49
|
+
return h;
|
|
50
|
+
const w = Number(m.width);
|
|
51
|
+
// Assume ~16:9 when only width is known (a coarse but monotonic proxy).
|
|
52
|
+
if (Number.isFinite(w) && w > 0)
|
|
53
|
+
return Math.round((w * 9) / 16);
|
|
54
|
+
const label = `${m.quality ?? ""} ${m.label ?? ""}`.toLowerCase();
|
|
55
|
+
// "1080p", "720 p", "2160p60", "1440P" → the leading pixel-height number.
|
|
56
|
+
const pMatch = label.match(/(\d{3,4})\s*p/);
|
|
57
|
+
if (pMatch)
|
|
58
|
+
return Number(pMatch[1]);
|
|
59
|
+
// "4k"/"2k" shorthand.
|
|
60
|
+
if (/\b4k|uhd\b/.test(label))
|
|
61
|
+
return 2160;
|
|
62
|
+
if (/\b2k\b/.test(label))
|
|
63
|
+
return 1440;
|
|
64
|
+
// Named tiers.
|
|
65
|
+
if (/full\s*hd|fhd/.test(label))
|
|
66
|
+
return 1080;
|
|
67
|
+
if (/\bhd\b/.test(label))
|
|
68
|
+
return 720;
|
|
69
|
+
if (/\bsd\b|low|small/.test(label))
|
|
70
|
+
return 360;
|
|
71
|
+
// A bare number (some providers give just "1080" / "720").
|
|
72
|
+
const nMatch = label.match(/\b(\d{3,4})\b/);
|
|
73
|
+
if (nMatch)
|
|
74
|
+
return Number(nMatch[1]);
|
|
75
|
+
return 0;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Rank every playable rendition best-first: highest-resolution MP4s, then any
|
|
79
|
+
* non-MP4 playable video as a fallback tier. Ranking is stable so ties keep the
|
|
80
|
+
* provider's own ordering (usually its preferred rendition first). This is the
|
|
81
|
+
* candidate order the download fallback walks — pickBestVideoMedia is just its
|
|
82
|
+
* head.
|
|
83
|
+
*/
|
|
84
|
+
export function rankPlayableVideoMedias(medias) {
|
|
85
|
+
const list = Array.isArray(medias) ? medias : [];
|
|
86
|
+
const rankByHeight = (pool) => pool
|
|
87
|
+
// Decorate-sort-undecorate to keep it a stable sort by descending height.
|
|
88
|
+
.map((m, index) => ({ m, index, h: estimateMediaHeight(m) }))
|
|
89
|
+
.sort((a, b) => b.h - a.h || a.index - b.index)
|
|
90
|
+
.map((entry) => entry.m);
|
|
91
|
+
const mp4s = rankByHeight(list.filter(isPlayableMp4));
|
|
92
|
+
const others = rankByHeight(list.filter((m) => !isPlayableMp4(m) && isPlayableVideo(m)));
|
|
93
|
+
return [...mp4s, ...others];
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Pick the best rendition to ingest: the highest-resolution playable MP4, with a
|
|
97
|
+
* non-MP4 playable video as a last resort, else null.
|
|
98
|
+
*/
|
|
99
|
+
export function pickBestVideoMedia(medias) {
|
|
100
|
+
return rankPlayableVideoMedias(medias)[0] ?? null;
|
|
101
|
+
}
|
|
102
|
+
// A browser-ish UA + Accept: the RapidAPI-hosted copies (snapvideo.co) can reject
|
|
103
|
+
// bare programmatic fetches, and the top-res server-side "merge" rendition
|
|
104
|
+
// transiently 403s while the merge runs or when rate-limited.
|
|
105
|
+
const MEDIA_DOWNLOAD_UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36";
|
|
106
|
+
const RETRYABLE_DOWNLOAD_STATUS = new Set([403, 408, 425, 429, 500, 502, 503, 504]);
|
|
107
|
+
function delay(ms) {
|
|
108
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Download the first playable rendition that actually responds, walking the
|
|
112
|
+
* ranked candidates highest-res first. The RapidAPI resolver returns SEVERAL
|
|
113
|
+
* copies it hosts itself (e.g. a progressive 360p plus server-side-merged
|
|
114
|
+
* 720p/1080p); the merge renditions can transiently 403 while merging or when
|
|
115
|
+
* rate-limited, so we retry a rendition once and then fall back to the next copy
|
|
116
|
+
* rather than hard-failing the whole ingest. Returns the live Response (caller
|
|
117
|
+
* streams `response.body` to disk/S3) plus the winning candidate. Throws only if
|
|
118
|
+
* EVERY rendition is unreachable, with each failure in the message.
|
|
119
|
+
*/
|
|
120
|
+
export async function fetchFirstDownloadableMedia(medias) {
|
|
121
|
+
const ranked = rankPlayableVideoMedias(medias);
|
|
122
|
+
if (!ranked.length) {
|
|
123
|
+
throw new Error("Video download lookup returned no playable MP4 media URL.");
|
|
124
|
+
}
|
|
125
|
+
const failures = [];
|
|
126
|
+
for (const media of ranked) {
|
|
127
|
+
const url = media.url;
|
|
128
|
+
if (!url)
|
|
129
|
+
continue;
|
|
130
|
+
const label = media.quality ?? media.label ?? "?";
|
|
131
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
132
|
+
if (attempt > 0)
|
|
133
|
+
await delay(750);
|
|
134
|
+
try {
|
|
135
|
+
const response = await fetch(url, { headers: { "user-agent": MEDIA_DOWNLOAD_UA, accept: "*/*" } });
|
|
136
|
+
if (response.ok && response.body) {
|
|
137
|
+
return { response, media };
|
|
138
|
+
}
|
|
139
|
+
failures.push(`${label}:HTTP ${response.status}`);
|
|
140
|
+
await response.body?.cancel?.().catch(() => { });
|
|
141
|
+
// A non-transient status won't fix itself on retry — move to the next copy.
|
|
142
|
+
if (!RETRYABLE_DOWNLOAD_STATUS.has(response.status))
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
failures.push(`${label}:${error instanceof Error ? error.message : String(error)}`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
throw new Error(`Source video download failed; all ${ranked.length} rendition(s) unavailable (${failures.join("; ")}).`);
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=media-select.js.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Starter preset criteria library. Presets are just saved SplicingCriteria
|
|
2
|
+
// (see types.ts). The shipped ones are `builtin: true`; users save their own
|
|
3
|
+
// with `builtin: false` (persisted per-store in devcli/cloud).
|
|
4
|
+
import presetsV1 from "./presets.v1.json" with { type: "json" };
|
|
5
|
+
const SEED = presetsV1;
|
|
6
|
+
/** The shipped built-in presets. Frozen so callers can't mutate the shared array. */
|
|
7
|
+
export const BUILTIN_PRESETS = SEED.presets.map((p) => ({
|
|
8
|
+
preset_id: p.preset_id,
|
|
9
|
+
name: p.name,
|
|
10
|
+
description: p.description,
|
|
11
|
+
criteria: p.criteria,
|
|
12
|
+
builtin: true,
|
|
13
|
+
created_at: "1970-01-01T00:00:00.000Z"
|
|
14
|
+
}));
|
|
15
|
+
/** Look up a built-in preset by its id or (case-insensitive) name. */
|
|
16
|
+
export function findBuiltinPreset(nameOrId) {
|
|
17
|
+
const needle = nameOrId.trim().toLowerCase();
|
|
18
|
+
return BUILTIN_PRESETS.find((p) => p.preset_id.toLowerCase() === needle || p.name.toLowerCase() === needle);
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=presets.js.map
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "v1",
|
|
3
|
+
"presets": [
|
|
4
|
+
{
|
|
5
|
+
"preset_id": "preset_funny_reactions",
|
|
6
|
+
"name": "funny reaction gifs",
|
|
7
|
+
"description": "Short single-subject reactions facing the camera — the raw material for reaction cutaways.",
|
|
8
|
+
"criteria": {
|
|
9
|
+
"emotion": ["surprised", "amused", "deadpan", "exasperated"],
|
|
10
|
+
"subject": ["person", "single_speaker"],
|
|
11
|
+
"composition": ["close_up", "medium_shot", "centered"],
|
|
12
|
+
"max_duration_sec": 2.5,
|
|
13
|
+
"has_subject": true,
|
|
14
|
+
"semantic_text": "funny reaction, person reacting to the camera"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"preset_id": "preset_broll_transitions",
|
|
19
|
+
"name": "b-roll transitions",
|
|
20
|
+
"description": "Quiet low-motion cutaways with no talking — good glue between beats.",
|
|
21
|
+
"criteria": {
|
|
22
|
+
"motion": ["static", "low"],
|
|
23
|
+
"max_duration_sec": 1.5,
|
|
24
|
+
"has_dialogue": false,
|
|
25
|
+
"semantic_text": "short b-roll transition, no dialogue"
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"preset_id": "preset_establishing_shots",
|
|
30
|
+
"name": "establishing shots",
|
|
31
|
+
"description": "Wide scene-setters with no clear subject.",
|
|
32
|
+
"criteria": {
|
|
33
|
+
"composition": ["wide_shot"],
|
|
34
|
+
"has_subject": false,
|
|
35
|
+
"semantic_text": "wide establishing shot of a location"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"preset_id": "preset_typing_hands",
|
|
40
|
+
"name": "typing hands",
|
|
41
|
+
"description": "Close-ups of hands on a keyboard — classic productivity b-roll.",
|
|
42
|
+
"criteria": {
|
|
43
|
+
"action": ["typing", "hands_on_keyboard"],
|
|
44
|
+
"composition": ["close_up"],
|
|
45
|
+
"semantic_text": "close up of hands typing on a keyboard"
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"preset_id": "preset_money_shots",
|
|
50
|
+
"name": "money shots",
|
|
51
|
+
"description": "High-energy hero shots — the payoff frames.",
|
|
52
|
+
"criteria": {
|
|
53
|
+
"composition": ["close_up", "medium_shot"],
|
|
54
|
+
"energy": ["high"],
|
|
55
|
+
"semantic_text": "high energy hero product money shot"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
]
|
|
59
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
// Structured filter + hybrid semantic re-rank. Same logic on devcli and cloud
|
|
2
|
+
// so a preset or NL query returns the same clips regardless of target.
|
|
3
|
+
//
|
|
4
|
+
// Search flow (handoff): structured filter FIRST (cheap, exact), then semantic
|
|
5
|
+
// re-rank the survivors by embedding similarity. If the structured filter
|
|
6
|
+
// matches nothing but the query carries semantic intent, we relax to a
|
|
7
|
+
// semantic-only pass so natural-language queries still return their best guess.
|
|
8
|
+
const DIALOGUE_AUDIO = new Set(["dialogue", "voiceover"]);
|
|
9
|
+
/** How many categorical facets `criteria` constrains (used for the match-ratio soft score). */
|
|
10
|
+
function constraintCount(criteria) {
|
|
11
|
+
let n = 0;
|
|
12
|
+
for (const key of ["content_type", "subject", "action", "emotion", "setting", "composition", "motion", "audio_type", "energy"]) {
|
|
13
|
+
if (Array.isArray(criteria[key]) && criteria[key].length)
|
|
14
|
+
n++;
|
|
15
|
+
}
|
|
16
|
+
if (criteria.min_duration_sec != null || criteria.max_duration_sec != null)
|
|
17
|
+
n++;
|
|
18
|
+
if (criteria.has_dialogue != null)
|
|
19
|
+
n++;
|
|
20
|
+
if (criteria.has_subject != null)
|
|
21
|
+
n++;
|
|
22
|
+
return n;
|
|
23
|
+
}
|
|
24
|
+
function hasSubject(tags) {
|
|
25
|
+
return tags.subject.length > 0 && !(tags.subject.length === 1 && tags.subject[0] === "no_subject");
|
|
26
|
+
}
|
|
27
|
+
function hasDialogue(tags) {
|
|
28
|
+
return tags.audio_type.some((a) => DIALOGUE_AUDIO.has(a)) || Boolean(tags.transcript.trim());
|
|
29
|
+
}
|
|
30
|
+
/** Hard structured filter: does this clip satisfy every constraint in `criteria`? */
|
|
31
|
+
export function matchesCriteria(clip, criteria) {
|
|
32
|
+
const t = clip.tags;
|
|
33
|
+
const anyOf = (have, want) => !want || want.length === 0 || want.some((w) => have.includes(w));
|
|
34
|
+
if (!anyOf(t.content_type ?? [], criteria.content_type))
|
|
35
|
+
return false;
|
|
36
|
+
if (!anyOf(t.subject, criteria.subject))
|
|
37
|
+
return false;
|
|
38
|
+
if (!anyOf(t.action, criteria.action))
|
|
39
|
+
return false;
|
|
40
|
+
if (!anyOf(t.emotion, criteria.emotion))
|
|
41
|
+
return false;
|
|
42
|
+
if (!anyOf(t.setting, criteria.setting))
|
|
43
|
+
return false;
|
|
44
|
+
if (!anyOf(t.composition, criteria.composition))
|
|
45
|
+
return false;
|
|
46
|
+
if (!anyOf(t.motion, criteria.motion))
|
|
47
|
+
return false;
|
|
48
|
+
if (!anyOf(t.audio_type, criteria.audio_type))
|
|
49
|
+
return false;
|
|
50
|
+
if (criteria.energy && criteria.energy.length && !criteria.energy.includes(t.energy))
|
|
51
|
+
return false;
|
|
52
|
+
if (criteria.min_duration_sec != null && clip.duration_sec < criteria.min_duration_sec)
|
|
53
|
+
return false;
|
|
54
|
+
if (criteria.max_duration_sec != null && clip.duration_sec > criteria.max_duration_sec)
|
|
55
|
+
return false;
|
|
56
|
+
if (criteria.has_dialogue === true && !hasDialogue(t))
|
|
57
|
+
return false;
|
|
58
|
+
if (criteria.has_dialogue === false && hasDialogue(t))
|
|
59
|
+
return false;
|
|
60
|
+
if (criteria.has_subject === true && !hasSubject(t))
|
|
61
|
+
return false;
|
|
62
|
+
if (criteria.has_subject === false && hasSubject(t))
|
|
63
|
+
return false;
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
/** Fraction of constrained facets this clip satisfies (0..1); 1 when there are no constraints. */
|
|
67
|
+
export function structuredMatchRatio(clip, criteria) {
|
|
68
|
+
const total = constraintCount(criteria);
|
|
69
|
+
if (total === 0)
|
|
70
|
+
return 1;
|
|
71
|
+
const t = clip.tags;
|
|
72
|
+
let hit = 0;
|
|
73
|
+
const anyOf = (have, want) => Boolean(want && want.length && want.some((w) => have.includes(w)));
|
|
74
|
+
if (anyOf(t.content_type ?? [], criteria.content_type))
|
|
75
|
+
hit++;
|
|
76
|
+
if (anyOf(t.subject, criteria.subject))
|
|
77
|
+
hit++;
|
|
78
|
+
if (anyOf(t.action, criteria.action))
|
|
79
|
+
hit++;
|
|
80
|
+
if (anyOf(t.emotion, criteria.emotion))
|
|
81
|
+
hit++;
|
|
82
|
+
if (anyOf(t.setting, criteria.setting))
|
|
83
|
+
hit++;
|
|
84
|
+
if (anyOf(t.composition, criteria.composition))
|
|
85
|
+
hit++;
|
|
86
|
+
if (anyOf(t.motion, criteria.motion))
|
|
87
|
+
hit++;
|
|
88
|
+
if (anyOf(t.audio_type, criteria.audio_type))
|
|
89
|
+
hit++;
|
|
90
|
+
if (criteria.energy && criteria.energy.length && criteria.energy.includes(t.energy))
|
|
91
|
+
hit++;
|
|
92
|
+
if ((criteria.min_duration_sec != null || criteria.max_duration_sec != null) &&
|
|
93
|
+
(criteria.min_duration_sec == null || clip.duration_sec >= criteria.min_duration_sec) &&
|
|
94
|
+
(criteria.max_duration_sec == null || clip.duration_sec <= criteria.max_duration_sec))
|
|
95
|
+
hit++;
|
|
96
|
+
if (criteria.has_dialogue != null && hasDialogue(t) === criteria.has_dialogue)
|
|
97
|
+
hit++;
|
|
98
|
+
if (criteria.has_subject != null && hasSubject(t) === criteria.has_subject)
|
|
99
|
+
hit++;
|
|
100
|
+
return hit / total;
|
|
101
|
+
}
|
|
102
|
+
/** Cosine similarity of two vectors (safe on unnormalized input). */
|
|
103
|
+
export function cosineSimilarity(a, b) {
|
|
104
|
+
if (!a || !b || a.length === 0 || a.length !== b.length)
|
|
105
|
+
return 0;
|
|
106
|
+
let dot = 0;
|
|
107
|
+
let na = 0;
|
|
108
|
+
let nb = 0;
|
|
109
|
+
for (let i = 0; i < a.length; i++) {
|
|
110
|
+
dot += a[i] * b[i];
|
|
111
|
+
na += a[i] * a[i];
|
|
112
|
+
nb += b[i] * b[i];
|
|
113
|
+
}
|
|
114
|
+
if (na === 0 || nb === 0)
|
|
115
|
+
return 0;
|
|
116
|
+
return dot / (Math.sqrt(na) * Math.sqrt(nb));
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Run the two-stage search over an in-memory clip set. Cloud can push the
|
|
120
|
+
* structured part into DynamoDB and the semantic part into a vector index; this
|
|
121
|
+
* pure function is the reference implementation devcli uses directly.
|
|
122
|
+
*/
|
|
123
|
+
export function searchClips(clips, opts) {
|
|
124
|
+
const { criteria, queryEmbedding } = opts;
|
|
125
|
+
const limit = opts.limit ?? 20;
|
|
126
|
+
const relax = opts.relaxWhenEmpty ?? true;
|
|
127
|
+
let candidates = clips.filter((c) => matchesCriteria(c, criteria));
|
|
128
|
+
let relaxed = false;
|
|
129
|
+
if (candidates.length === 0 && relax && (queryEmbedding || criteria.semantic_text)) {
|
|
130
|
+
candidates = clips.slice();
|
|
131
|
+
relaxed = true;
|
|
132
|
+
}
|
|
133
|
+
const hits = candidates.map((clip) => scoreClip(clip, criteria, queryEmbedding, relaxed));
|
|
134
|
+
return rankHits(hits, limit);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Score a single clip against a query. `relaxed` = the structured filter was
|
|
138
|
+
* bypassed (semantic-only pass), so factor in how well it still matches the
|
|
139
|
+
* criteria; when not relaxed the clip already passed the hard filter (ratio 1).
|
|
140
|
+
* Exported so the devcli SQLite store can reuse the exact scoring after doing
|
|
141
|
+
* the structured filter / KNN prefilter in SQL.
|
|
142
|
+
*/
|
|
143
|
+
export function scoreClip(clip, criteria, queryEmbedding, relaxed) {
|
|
144
|
+
const similarity = queryEmbedding ? cosineSimilarity(clip.embedding, queryEmbedding) : undefined;
|
|
145
|
+
const ratio = relaxed ? structuredMatchRatio(clip, criteria) : 1;
|
|
146
|
+
let score;
|
|
147
|
+
if (similarity != null) {
|
|
148
|
+
// Blend: mostly semantic, with a structured-match boost. Normalize cosine
|
|
149
|
+
// (-1..1) into 0..1 so scores read sensibly.
|
|
150
|
+
const sim01 = (similarity + 1) / 2;
|
|
151
|
+
score = 0.8 * sim01 + 0.2 * ratio;
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
score = ratio;
|
|
155
|
+
}
|
|
156
|
+
return { clip, score, similarity };
|
|
157
|
+
}
|
|
158
|
+
/** Sort hits by score (newer clips break ties) and take the top `limit`. */
|
|
159
|
+
export function rankHits(hits, limit) {
|
|
160
|
+
hits.sort((a, b) => {
|
|
161
|
+
if (b.score !== a.score)
|
|
162
|
+
return b.score - a.score;
|
|
163
|
+
return (b.clip.created_at || "").localeCompare(a.clip.created_at || "");
|
|
164
|
+
});
|
|
165
|
+
return hits.slice(0, limit);
|
|
166
|
+
}
|
|
167
|
+
//# sourceMappingURL=query.js.map
|