@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,165 @@
|
|
|
1
|
+
// Prompt-guided segmentation. ffmpeg gives us candidate scene cuts by visual
|
|
2
|
+
// change; this optional pass lets a user's guidance prompt actually reshape HOW
|
|
3
|
+
// the video is clipped — one multimodal LLM call sees a keyframe per candidate
|
|
4
|
+
// scene and decides, per scene, keep/drop (is it relevant to the goal?) and
|
|
5
|
+
// merge-with-previous (does it continue the prior kept scene as one clip?). We
|
|
6
|
+
// then rebuild the scene list from those decisions. Falls back to the raw scenes
|
|
7
|
+
// on any mismatch/error so a scan never breaks.
|
|
8
|
+
import { mkdirSync, rmSync } from "node:fs";
|
|
9
|
+
import path from "node:path";
|
|
10
|
+
import { extractKeyframes } from "./ffmpeg.js";
|
|
11
|
+
// One small keyframe per scene; cap the montage per LLM call so a single request
|
|
12
|
+
// stays affordable/latency-bounded. Longer videos are refined in consecutive
|
|
13
|
+
// batches of this size (see MAX_REFINE_BATCHES) rather than skipping guidance.
|
|
14
|
+
const MAX_SCENES_PER_BATCH = 60;
|
|
15
|
+
// Bound total cost on very long sources: past this many batches we keep the raw
|
|
16
|
+
// segmentation for the whole video (logged) instead of firing dozens of calls.
|
|
17
|
+
const MAX_REFINE_BATCHES = 30;
|
|
18
|
+
export async function refineScenesWithGuidance(input) {
|
|
19
|
+
const { client, videoPath, scenes, guidancePrompt } = input;
|
|
20
|
+
if (!guidancePrompt.trim() || scenes.length === 0) {
|
|
21
|
+
return { scenes, refined: false, kept: scenes.length, dropped: 0, merged: 0 };
|
|
22
|
+
}
|
|
23
|
+
const batchCount = Math.ceil(scenes.length / MAX_SCENES_PER_BATCH);
|
|
24
|
+
if (batchCount > MAX_REFINE_BATCHES) {
|
|
25
|
+
return {
|
|
26
|
+
scenes,
|
|
27
|
+
refined: false,
|
|
28
|
+
kept: scenes.length,
|
|
29
|
+
dropped: 0,
|
|
30
|
+
merged: 0,
|
|
31
|
+
note: `too many scenes (${scenes.length} > ${MAX_SCENES_PER_BATCH * MAX_REFINE_BATCHES}) for guided refinement — kept raw segmentation`
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
const framesDir = path.join(input.workDir, "refine-frames");
|
|
35
|
+
mkdirSync(framesDir, { recursive: true });
|
|
36
|
+
try {
|
|
37
|
+
// Decide every scene, batching the LLM calls so a long source still gets
|
|
38
|
+
// guided keep/drop/merge (instead of falling back to raw cuts). Decisions
|
|
39
|
+
// are concatenated in timeline order so merge_with_previous still works
|
|
40
|
+
// across a batch boundary in rebuildScenes.
|
|
41
|
+
const keepDefault = () => ({ keep: true, merge_with_previous: false });
|
|
42
|
+
const decisions = [];
|
|
43
|
+
for (let b = 0; b < batchCount; b++) {
|
|
44
|
+
const batch = scenes.slice(b * MAX_SCENES_PER_BATCH, (b + 1) * MAX_SCENES_PER_BATCH);
|
|
45
|
+
// One small keyframe per candidate scene in this batch, in timeline order.
|
|
46
|
+
const frames = [];
|
|
47
|
+
for (const scene of batch) {
|
|
48
|
+
const [p] = await extractKeyframes({ videoPath, scene, outDir: framesDir, count: 1, width: 200 });
|
|
49
|
+
if (p)
|
|
50
|
+
frames.push({ index: scene.index, path: p });
|
|
51
|
+
}
|
|
52
|
+
// Couldn't frame this batch — keep its scenes rather than dropping blind.
|
|
53
|
+
if (frames.length !== batch.length) {
|
|
54
|
+
for (let i = 0; i < batch.length; i++)
|
|
55
|
+
decisions.push(keepDefault());
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
// Divide the overall clip target across batches by their share of scenes.
|
|
59
|
+
const batchTarget = input.targetClipCount && input.targetClipCount > 0
|
|
60
|
+
? Math.max(1, Math.round((input.targetClipCount * batch.length) / scenes.length))
|
|
61
|
+
: undefined;
|
|
62
|
+
const batchDecisions = await askForDecisions(client, guidancePrompt, batch, frames, batchTarget);
|
|
63
|
+
if (!batchDecisions)
|
|
64
|
+
throw new Error("model returned no refinement plan");
|
|
65
|
+
for (let i = 0; i < batch.length; i++) {
|
|
66
|
+
decisions.push(batchDecisions?.[i] ?? keepDefault());
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (decisions.length === 0) {
|
|
70
|
+
return { scenes, refined: false, kept: scenes.length, dropped: 0, merged: 0, note: "model returned no plan" };
|
|
71
|
+
}
|
|
72
|
+
return rebuildScenes(scenes, decisions);
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
// The provider/model call (or keyframe prep) threw. Keep the raw scenes so
|
|
76
|
+
// a scan CAN still proceed, but carry the real reason up as providerError
|
|
77
|
+
// so the caller can decide to fail loudly with a provider-attributed
|
|
78
|
+
// message instead of silently ignoring the user's guidance prompt.
|
|
79
|
+
const providerError = error instanceof Error ? error.message : String(error);
|
|
80
|
+
return {
|
|
81
|
+
scenes,
|
|
82
|
+
refined: false,
|
|
83
|
+
kept: scenes.length,
|
|
84
|
+
dropped: 0,
|
|
85
|
+
merged: 0,
|
|
86
|
+
note: "refinement failed; using raw scenes",
|
|
87
|
+
providerError
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
finally {
|
|
91
|
+
try {
|
|
92
|
+
rmSync(framesDir, { recursive: true, force: true });
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
/* ignore */
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
async function askForDecisions(client, guidancePrompt, scenes, frames, targetClipCount) {
|
|
100
|
+
const listing = scenes
|
|
101
|
+
.map((s, i) => `Scene ${i}: ${s.start_time_sec.toFixed(1)}s–${s.end_time_sec.toFixed(1)}s (${s.duration_sec.toFixed(1)}s)`)
|
|
102
|
+
.join("\n");
|
|
103
|
+
const target = targetClipCount && targetClipCount > 0 ? Math.round(targetClipCount) : null;
|
|
104
|
+
const prompt = [
|
|
105
|
+
`You are curating short clips from a long video for this exact goal: "${guidancePrompt.trim()}".`,
|
|
106
|
+
`There are ${scenes.length} candidate scenes, shown as ${scenes.length} keyframes below in timeline order (one per scene).`,
|
|
107
|
+
listing,
|
|
108
|
+
"",
|
|
109
|
+
"For EACH scene (in order) decide:",
|
|
110
|
+
"- keep: true ONLY if the scene clearly and directly serves the goal above; when in doubt, drop it (keep:false). Follow the goal STRICTLY — do not keep loosely-related or generic filler.",
|
|
111
|
+
"- merge_with_previous: true if this scene continues the previous KEPT scene and they should be ONE clip (e.g. a single continuous action split by a minor cut). Otherwise false.",
|
|
112
|
+
"",
|
|
113
|
+
"UNIQUENESS (important): the output must be DISTINCT clips — no near-duplicates. If several scenes show essentially the same shot (same subject, framing, action, and setting), KEEP ONLY THE SINGLE BEST ONE and set keep:false for the visually-redundant rest. Prefer variety across the kept clips.",
|
|
114
|
+
target
|
|
115
|
+
? `Aim for roughly ${target} kept clip${target === 1 ? "" : "s"} total (a few more or fewer is fine) — keep the strongest, most distinct scenes and drop the weakest/most repetitive.`
|
|
116
|
+
: "Be selective — dropping off-topic and duplicate scenes and merging fragmented ones is the point.",
|
|
117
|
+
"",
|
|
118
|
+
`Return exactly ${scenes.length} decisions, in the same order as the scenes.`
|
|
119
|
+
].join("\n");
|
|
120
|
+
// The client owns the model call (API providers via structured output, the
|
|
121
|
+
// local devcli agent via CLI) — refine only builds the prompt and rebuilds
|
|
122
|
+
// the scene list from the decisions.
|
|
123
|
+
return client.decideScenes({
|
|
124
|
+
prompt,
|
|
125
|
+
imagePaths: frames.map((f) => f.path),
|
|
126
|
+
maxOutputTokens: Math.max(1000, scenes.length * 40)
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
export function rebuildScenes(scenes, decisions) {
|
|
130
|
+
const out = [];
|
|
131
|
+
let dropped = 0;
|
|
132
|
+
let merged = 0;
|
|
133
|
+
for (let i = 0; i < scenes.length; i++) {
|
|
134
|
+
// Align by index; a missing decision (short array) defaults to keep.
|
|
135
|
+
const d = decisions[i] ?? { keep: true, merge_with_previous: false };
|
|
136
|
+
if (!d.keep) {
|
|
137
|
+
dropped++;
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
const previous = out[out.length - 1];
|
|
141
|
+
const isAdjacent = previous
|
|
142
|
+
? Math.abs(previous.end_time_sec - scenes[i].start_time_sec) <= 0.05
|
|
143
|
+
: false;
|
|
144
|
+
if (d.merge_with_previous && previous && isAdjacent) {
|
|
145
|
+
// Extend only an ADJACENT previous kept clip. A dropped scene creates a
|
|
146
|
+
// real timeline gap; bridging that gap would silently put the rejected
|
|
147
|
+
// footage back into the exported raw.
|
|
148
|
+
previous.end_time_sec = scenes[i].end_time_sec;
|
|
149
|
+
previous.duration_sec = round3(previous.end_time_sec - previous.start_time_sec);
|
|
150
|
+
merged++;
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
out.push({ ...scenes[i], index: out.length });
|
|
154
|
+
}
|
|
155
|
+
// An empty plan is a valid strict-prompt result. Silently restoring every
|
|
156
|
+
// scene here would completely ignore the user's request.
|
|
157
|
+
if (out.length === 0) {
|
|
158
|
+
return { scenes: [], refined: true, kept: 0, dropped, merged };
|
|
159
|
+
}
|
|
160
|
+
return { scenes: out, refined: true, kept: out.length, dropped, merged };
|
|
161
|
+
}
|
|
162
|
+
function round3(n) {
|
|
163
|
+
return Math.round(n * 1000) / 1000;
|
|
164
|
+
}
|
|
165
|
+
//# sourceMappingURL=refine.js.map
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
// Scan orchestration shared by devcli and cloud. `processSceneToClip` turns ONE
|
|
2
|
+
// detected scene into a full Clip record (extract clip/keyframes/thumb/audio →
|
|
3
|
+
// Gemini tag+transcribe → embed). devcli calls it in a bounded pool; a cloud
|
|
4
|
+
// Step Functions Map state calls it once per scene. Persistence is the caller's
|
|
5
|
+
// job (SQLite vs DynamoDB+S3) — this stays storage-agnostic.
|
|
6
|
+
import { mkdirSync } from "node:fs";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import { createIdV7 } from "../../lib/ids.js";
|
|
9
|
+
import { ACTIVE_TAXONOMY_VERSION } from "./taxonomy.js";
|
|
10
|
+
import { buildClipEmbeddingText } from "./gemini.js";
|
|
11
|
+
import { detectScenes, extractAudioClip, extractClip, extractKeyframes, extractSceneAudio, extractThumbnail, extractWaveformThumbnail, probeVideo } from "./ffmpeg.js";
|
|
12
|
+
import { applyUserContentTypeHints, capScenesEvenlyToCount, cropFilterFor, fitScenesToDurationBand, normalizeWindows } from "./hunt.js";
|
|
13
|
+
import { refineScenesWithGuidance } from "./refine.js";
|
|
14
|
+
/** Turn one scene into a Clip. Throws only on unrecoverable extraction failure. */
|
|
15
|
+
export async function processSceneToClip(input) {
|
|
16
|
+
const { ctx, videoPath, scene } = input;
|
|
17
|
+
const framesDir = path.join(ctx.workDir, "frames");
|
|
18
|
+
const audioDir = path.join(ctx.workDir, "audio");
|
|
19
|
+
mkdirSync(ctx.clipsDir, { recursive: true });
|
|
20
|
+
mkdirSync(ctx.thumbsDir, { recursive: true });
|
|
21
|
+
const clipId = createIdV7("clip");
|
|
22
|
+
const cropFilter = ctx.crop ? cropFilterFor(ctx.crop.aspect, ctx.crop.focus) : null;
|
|
23
|
+
// 1. Keyframes for visual tagging (small, low-res), framed like the output.
|
|
24
|
+
const keyframePaths = await extractKeyframes({
|
|
25
|
+
videoPath,
|
|
26
|
+
scene,
|
|
27
|
+
outDir: framesDir,
|
|
28
|
+
count: ctx.framesPerScene ?? 2,
|
|
29
|
+
cropFilter
|
|
30
|
+
});
|
|
31
|
+
// 2. Per-scene audio for ASR (one combined tag+transcribe call).
|
|
32
|
+
let audioPath;
|
|
33
|
+
if (ctx.includeAudio ?? true) {
|
|
34
|
+
const a = await extractSceneAudio({
|
|
35
|
+
videoPath,
|
|
36
|
+
scene,
|
|
37
|
+
outPath: path.join(audioDir, `scene-${scene.index}.mp3`)
|
|
38
|
+
});
|
|
39
|
+
audioPath = a ?? undefined;
|
|
40
|
+
}
|
|
41
|
+
// 3. Tag (+ transcription when audio present on Gemini), biased by the optional guidance prompt.
|
|
42
|
+
const tagResult = await ctx.client.tagScene({
|
|
43
|
+
keyframePaths,
|
|
44
|
+
transcript: "",
|
|
45
|
+
audioPath,
|
|
46
|
+
scene,
|
|
47
|
+
guidancePrompt: ctx.guidancePrompt
|
|
48
|
+
});
|
|
49
|
+
const tags = applyUserContentTypeHints(tagResult.tags, ctx.userNotes, ctx.client.taxonomyVersion ?? ACTIVE_TAXONOMY_VERSION);
|
|
50
|
+
// 4. Cut the clip + thumbnail. Audio-only hunts emit an .m4a (no video) with a
|
|
51
|
+
// waveform PNG thumbnail; ordinary hunts emit an .mp4 cropped to the target
|
|
52
|
+
// aspect when requested.
|
|
53
|
+
const audioOnly = ctx.audioOnly === true;
|
|
54
|
+
const clipLocal = path.join(ctx.clipsDir, `${clipId}.${audioOnly ? "m4a" : "mp4"}`);
|
|
55
|
+
const thumbLocal = path.join(ctx.thumbsDir, `${clipId}.${audioOnly ? "png" : "jpg"}`);
|
|
56
|
+
let extractDetail = "";
|
|
57
|
+
if (audioOnly) {
|
|
58
|
+
const audioOk = await extractAudioClip({ videoPath, scene, outPath: clipLocal, onError: (detail) => (extractDetail = detail) });
|
|
59
|
+
if (!audioOk) {
|
|
60
|
+
throw new Error(`ffmpeg failed to extract audio for scene @${scene.start_time_sec}s${extractDetail ? ` — ${extractDetail}` : ""}`);
|
|
61
|
+
}
|
|
62
|
+
await extractWaveformThumbnail({ videoPath, scene, outPath: thumbLocal }).catch(() => false);
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
const clipOk = await extractClip({
|
|
66
|
+
videoPath,
|
|
67
|
+
scene,
|
|
68
|
+
outPath: clipLocal,
|
|
69
|
+
reencode: ctx.reencodeClips !== false,
|
|
70
|
+
cropFilter,
|
|
71
|
+
onError: (detail) => (extractDetail = detail)
|
|
72
|
+
});
|
|
73
|
+
if (!clipOk) {
|
|
74
|
+
throw new Error(`ffmpeg failed to extract clip for scene @${scene.start_time_sec}s${extractDetail ? ` — ${extractDetail}` : ""}`);
|
|
75
|
+
}
|
|
76
|
+
await extractThumbnail({ videoPath, scene, outPath: thumbLocal, cropFilter });
|
|
77
|
+
}
|
|
78
|
+
// 5. Embed the document text for semantic search (skipped when the client can't embed).
|
|
79
|
+
const embeddingText = buildClipEmbeddingText(tags, tagResult.description, ctx.userNotes);
|
|
80
|
+
let embedding;
|
|
81
|
+
if (ctx.client.canEmbed) {
|
|
82
|
+
[embedding] = await ctx.client.embedDocuments([embeddingText]);
|
|
83
|
+
}
|
|
84
|
+
const clip = {
|
|
85
|
+
clip_id: clipId,
|
|
86
|
+
source_video_id: input.sourceVideoId,
|
|
87
|
+
source_filename: input.sourceFilename,
|
|
88
|
+
start_time_sec: scene.start_time_sec,
|
|
89
|
+
end_time_sec: scene.end_time_sec,
|
|
90
|
+
duration_sec: scene.duration_sec,
|
|
91
|
+
file_path: ctx.clipStoredPath ? ctx.clipStoredPath(clipId, clipLocal) : clipLocal,
|
|
92
|
+
thumbnail_path: ctx.thumbStoredPath ? ctx.thumbStoredPath(clipId, thumbLocal) : thumbLocal,
|
|
93
|
+
tags,
|
|
94
|
+
description: tagResult.description,
|
|
95
|
+
embedding_text: embeddingText,
|
|
96
|
+
embedding,
|
|
97
|
+
taxonomy_version: ctx.client.taxonomyVersion ?? ACTIVE_TAXONOMY_VERSION,
|
|
98
|
+
tier: ctx.client.tier,
|
|
99
|
+
provider: ctx.client.provider,
|
|
100
|
+
embedding_model: ctx.client.embeddingModelId ?? undefined,
|
|
101
|
+
aspect: !audioOnly && cropFilter && ctx.crop ? ctx.crop.aspect : undefined,
|
|
102
|
+
media_kind: audioOnly ? "audio" : undefined,
|
|
103
|
+
user_notes: ctx.userNotes?.trim() || undefined,
|
|
104
|
+
owner_id: input.ownerId,
|
|
105
|
+
created_at: new Date().toISOString()
|
|
106
|
+
};
|
|
107
|
+
return clip;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Run the full local scan pipeline for one video with a bounded concurrency
|
|
111
|
+
* pool. (Cloud replaces this loop with a Step Functions Map over the scenes.)
|
|
112
|
+
*/
|
|
113
|
+
export async function scanVideo(opts) {
|
|
114
|
+
const probe = opts.probe ?? (await probeVideo(opts.videoPath));
|
|
115
|
+
const windows = normalizeWindows(opts.windows, probe.duration_sec);
|
|
116
|
+
const band = opts.durationBand ?? null;
|
|
117
|
+
// Keep detected shots separate until AFTER prompt refinement. This is
|
|
118
|
+
// important: judging a multi-shot duration span from one midpoint frame can
|
|
119
|
+
// approve unrelated opening/closing footage or miss text overlays in them.
|
|
120
|
+
let scenes = opts.scenes ??
|
|
121
|
+
await detectScenes(opts.videoPath, {
|
|
122
|
+
durationSec: probe.duration_sec,
|
|
123
|
+
windows,
|
|
124
|
+
...opts.sceneOptions
|
|
125
|
+
});
|
|
126
|
+
// Prompt-guided segmentation: let the guidance reshape which scenes become
|
|
127
|
+
// clips (keep/drop/merge/dedup) before we spend tagging tokens on them.
|
|
128
|
+
if (opts.ctx.guidancePrompt?.trim() && opts.ctx.refineSegmentation !== false) {
|
|
129
|
+
const refined = await refineScenesWithGuidance({
|
|
130
|
+
client: opts.ctx.client,
|
|
131
|
+
videoPath: opts.videoPath,
|
|
132
|
+
scenes,
|
|
133
|
+
guidancePrompt: opts.ctx.guidancePrompt,
|
|
134
|
+
workDir: opts.ctx.workDir,
|
|
135
|
+
targetClipCount: opts.targetClipCount
|
|
136
|
+
});
|
|
137
|
+
if (refined.providerError) {
|
|
138
|
+
// The guidance step failed because the AI provider/model itself errored
|
|
139
|
+
// (e.g. a text-only key that can't do the vision + structured selection).
|
|
140
|
+
// Tagging needs the same capability, so surface the provider's own reason
|
|
141
|
+
// — it's an AI-provider issue, not Vidfarm — instead of pressing on.
|
|
142
|
+
throw new Error(refined.providerError);
|
|
143
|
+
}
|
|
144
|
+
scenes = refined.scenes;
|
|
145
|
+
opts.onRefine?.(refined);
|
|
146
|
+
}
|
|
147
|
+
// Assemble prompt-approved, contiguous shots into useful clip durations.
|
|
148
|
+
// fitScenesToDurationBand never crosses a dropped gap.
|
|
149
|
+
scenes = fitScenesToDurationBand(scenes, band);
|
|
150
|
+
// Deterministic count/uniqueness cap: if more scenes survived than the target
|
|
151
|
+
// (refine skipped >60 scenes, or the model kept more than asked), keep an
|
|
152
|
+
// evenly-spaced — hence less duplicative — subset.
|
|
153
|
+
if (opts.targetClipCount && opts.targetClipCount > 0) {
|
|
154
|
+
scenes = capScenesEvenlyToCount(scenes, opts.targetClipCount);
|
|
155
|
+
}
|
|
156
|
+
const sourceVideoId = opts.sourceVideoId ?? createIdV7("srcvid");
|
|
157
|
+
const sourceFilename = opts.sourceFilename ?? path.basename(opts.videoPath);
|
|
158
|
+
const concurrency = Math.max(1, opts.concurrency ?? 3);
|
|
159
|
+
const clips = [];
|
|
160
|
+
let cursor = 0;
|
|
161
|
+
let completed = 0;
|
|
162
|
+
async function worker() {
|
|
163
|
+
while (cursor < scenes.length) {
|
|
164
|
+
const idx = cursor++;
|
|
165
|
+
const scene = scenes[idx];
|
|
166
|
+
try {
|
|
167
|
+
const clip = await processSceneToClip({
|
|
168
|
+
ctx: opts.ctx,
|
|
169
|
+
videoPath: opts.videoPath,
|
|
170
|
+
scene,
|
|
171
|
+
sourceVideoId,
|
|
172
|
+
sourceFilename,
|
|
173
|
+
ownerId: opts.ownerId
|
|
174
|
+
});
|
|
175
|
+
clips.push(clip);
|
|
176
|
+
completed++;
|
|
177
|
+
await opts.onClip?.(clip, completed, scenes.length);
|
|
178
|
+
}
|
|
179
|
+
catch (error) {
|
|
180
|
+
opts.onError?.(scene, error);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
await Promise.all(Array.from({ length: Math.min(concurrency, scenes.length) }, () => worker()));
|
|
185
|
+
// Preserve timeline order for a stable library.
|
|
186
|
+
clips.sort((a, b) => a.start_time_sec - b.start_time_sec);
|
|
187
|
+
return { source_video_id: sourceVideoId, probe, scenes, clips };
|
|
188
|
+
}
|
|
189
|
+
//# sourceMappingURL=scan.js.map
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
// Shared naming for imported raw video sources. The historical bug: a raw's
|
|
2
|
+
// display name + folder slug were derived by taking the last path segment of the
|
|
3
|
+
// source URL, so `https://youtube.com/watch?v=abc` collapsed to the useless
|
|
4
|
+
// folder "watch" (shared by every YouTube import). This resolves a human name
|
|
5
|
+
// with a sane preference order and, when a URL yields only routing words, falls
|
|
6
|
+
// back to a uuidv7 so each source still lands in its own stable folder.
|
|
7
|
+
//
|
|
8
|
+
// Shared by the clip-scan Lambda (infra/lambda) and the in-process serve/import
|
|
9
|
+
// path (src/app.ts) so both stay in lock-step.
|
|
10
|
+
import { uuidv7 } from "../../lib/ids.js";
|
|
11
|
+
// URL path segments that are routing words, not content names. Deriving a name
|
|
12
|
+
// from a URL must never keep one of these on its own (youtube.com/watch,
|
|
13
|
+
// tiktok.com/@user/video/<id> → the <id> is what matters, not "video").
|
|
14
|
+
const JUNK_URL_SEGMENTS = new Set([
|
|
15
|
+
"watch", "embed", "shorts", "short", "video", "videos", "v", "e", "p",
|
|
16
|
+
"index", "share", "status", "reel", "reels", "story", "stories", "play",
|
|
17
|
+
"player", "view", "download", "dl", "media", "source", "file", "files",
|
|
18
|
+
"stream", "streams", "live", "tv", "feed", "home", "www", "clip", "clips",
|
|
19
|
+
"post", "posts", "photo", "photos", "id", "intl", "m", "amp", "mobile", ""
|
|
20
|
+
]);
|
|
21
|
+
// Generic filenames our own fallbacks produce — treated as "no real name" so the
|
|
22
|
+
// resolver keeps looking (URL-derived → uuidv7) rather than naming a folder
|
|
23
|
+
// "source" / "video".
|
|
24
|
+
const GENERIC_NAMES = new Set([
|
|
25
|
+
"source", "video", "download", "media", "watch", "index", "clip", "movie",
|
|
26
|
+
"untitled", "file", "output", "temp", "tmp"
|
|
27
|
+
]);
|
|
28
|
+
function stripExtension(name) {
|
|
29
|
+
return name.replace(/\.[a-z0-9]{1,5}$/i, "");
|
|
30
|
+
}
|
|
31
|
+
function decodeSegment(seg) {
|
|
32
|
+
try {
|
|
33
|
+
return decodeURIComponent(seg);
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return seg;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/** True when a candidate name is empty, a route word, or one of our generic fallbacks. */
|
|
40
|
+
function isMeaninglessName(name) {
|
|
41
|
+
const trimmed = stripExtension(name.trim()).toLowerCase();
|
|
42
|
+
if (!trimmed)
|
|
43
|
+
return true;
|
|
44
|
+
if (GENERIC_NAMES.has(trimmed))
|
|
45
|
+
return true;
|
|
46
|
+
if (JUNK_URL_SEGMENTS.has(trimmed))
|
|
47
|
+
return true;
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Derive a human-friendly name from a media URL, or null when the URL yields
|
|
52
|
+
* only routing words. Examples:
|
|
53
|
+
* youtube.com/watch?v=abc → "youtube-abc"
|
|
54
|
+
* youtu.be/abc → "youtube-abc"
|
|
55
|
+
* tiktok.com/@user/video/7523 → "tiktok-7523"
|
|
56
|
+
* cdn.example.com/my-clip.mp4 → "my-clip"
|
|
57
|
+
* example.com/watch → null
|
|
58
|
+
*/
|
|
59
|
+
export function deriveMediaNameFromUrl(rawUrl) {
|
|
60
|
+
let u;
|
|
61
|
+
try {
|
|
62
|
+
u = new URL(rawUrl);
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
const host = u.hostname.replace(/^www\./, "").toLowerCase();
|
|
68
|
+
// YouTube: the stable video id beats any path segment.
|
|
69
|
+
if (host === "youtube.com" || host.endsWith(".youtube.com") || host === "youtube-nocookie.com") {
|
|
70
|
+
const vid = u.searchParams.get("v");
|
|
71
|
+
if (vid)
|
|
72
|
+
return `youtube-${vid}`;
|
|
73
|
+
}
|
|
74
|
+
if (host === "youtu.be") {
|
|
75
|
+
const seg = u.pathname.split("/").filter(Boolean)[0];
|
|
76
|
+
if (seg)
|
|
77
|
+
return `youtube-${decodeSegment(seg)}`;
|
|
78
|
+
}
|
|
79
|
+
const segments = u.pathname.split("/").filter(Boolean);
|
|
80
|
+
// Direct-file URL: the basename (sans extension) is usually the real name.
|
|
81
|
+
const last = segments[segments.length - 1];
|
|
82
|
+
if (last) {
|
|
83
|
+
const base = stripExtension(decodeSegment(last));
|
|
84
|
+
if (base && !JUNK_URL_SEGMENTS.has(base.toLowerCase())) {
|
|
85
|
+
return base;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// Otherwise walk back for the last non-routing segment (e.g. a TikTok/X post
|
|
89
|
+
// id) and label it with the host so it reads as "<site>-<id>".
|
|
90
|
+
for (let i = segments.length - 1; i >= 0; i--) {
|
|
91
|
+
const seg = segments[i];
|
|
92
|
+
if (!JUNK_URL_SEGMENTS.has(seg.toLowerCase())) {
|
|
93
|
+
const hostLabel = host.split(".")[0] || "video";
|
|
94
|
+
return `${hostLabel}-${stripExtension(decodeSegment(seg))}`;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
/** Canonical single-segment Raws folder slug, or null when the input is empty. */
|
|
100
|
+
export function slugifyFolderName(value) {
|
|
101
|
+
const slug = String(value ?? "")
|
|
102
|
+
.toLowerCase()
|
|
103
|
+
.replace(/\.[a-z0-9]+$/i, "")
|
|
104
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
105
|
+
.replace(/^-+|-+$/g, "")
|
|
106
|
+
.slice(0, 60);
|
|
107
|
+
return slug || null;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Resolve the display name + folder slug for an imported raw source. Preference:
|
|
111
|
+
* 1. the real media title (page metadata / RapidAPI lookup)
|
|
112
|
+
* 2. an explicit or uploaded filename
|
|
113
|
+
* 3. a name derived from the source URL (youtube-<id>, the file basename, …)
|
|
114
|
+
* 4. a uuidv7 — so a source with no meaningful name anywhere still gets its own
|
|
115
|
+
* stable, unique folder instead of a shared "watch"/"source" bucket.
|
|
116
|
+
*/
|
|
117
|
+
export function resolveRawSourceName(input) {
|
|
118
|
+
const title = (input.title ?? "").trim();
|
|
119
|
+
const provided = (input.providedFilename ?? "").trim();
|
|
120
|
+
const fromUrl = input.sourceUrl ? deriveMediaNameFromUrl(input.sourceUrl) : null;
|
|
121
|
+
const displayName = (title && !isMeaninglessName(title) ? title : "") ||
|
|
122
|
+
(provided && !isMeaninglessName(provided) ? stripExtension(provided) : "") ||
|
|
123
|
+
(fromUrl && !isMeaninglessName(fromUrl) ? fromUrl : "");
|
|
124
|
+
if (displayName) {
|
|
125
|
+
return { displayName, folderSlug: slugifyFolderName(displayName) || uuidv7() };
|
|
126
|
+
}
|
|
127
|
+
// No meaningful name anywhere — mint a stable unique folder/name.
|
|
128
|
+
const id = uuidv7();
|
|
129
|
+
return { displayName: `import-${id}`, folderSlug: id };
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=source-naming.js.map
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// Versioned tag taxonomy loader. The vocabulary lives in taxonomy.v1.json so it
|
|
2
|
+
// can evolve independently; clips record the version they were tagged with
|
|
3
|
+
// (Clip.taxonomy_version) so old clips stay valid when the vocab grows.
|
|
4
|
+
import taxonomyV1 from "./taxonomy.v1.json" with { type: "json" };
|
|
5
|
+
const REGISTRY = {
|
|
6
|
+
v1: taxonomyV1
|
|
7
|
+
};
|
|
8
|
+
export const ACTIVE_TAXONOMY_VERSION = "v1";
|
|
9
|
+
/** Get a taxonomy by version (defaults to the active one). */
|
|
10
|
+
export function getTaxonomy(version = ACTIVE_TAXONOMY_VERSION) {
|
|
11
|
+
const found = REGISTRY[version];
|
|
12
|
+
if (!found) {
|
|
13
|
+
throw new Error(`Unknown taxonomy version "${version}" (have: ${Object.keys(REGISTRY).join(", ")})`);
|
|
14
|
+
}
|
|
15
|
+
return found;
|
|
16
|
+
}
|
|
17
|
+
/** The multi-valued categorical fields (everything except `energy`, which is single). */
|
|
18
|
+
export const MULTI_VALUE_CATEGORIES = [
|
|
19
|
+
"content_type",
|
|
20
|
+
"subject",
|
|
21
|
+
"action",
|
|
22
|
+
"emotion",
|
|
23
|
+
"setting",
|
|
24
|
+
"composition",
|
|
25
|
+
"motion",
|
|
26
|
+
"audio_type"
|
|
27
|
+
];
|
|
28
|
+
/**
|
|
29
|
+
* Clamp an LLM-produced tag object to the controlled vocabulary: drop any value
|
|
30
|
+
* not in the taxonomy so a hallucinated tag can never poison the index. Returns
|
|
31
|
+
* a fully-populated RawTags-shaped object (missing categories become []).
|
|
32
|
+
*/
|
|
33
|
+
export function sanitizeTagValues(raw, version = ACTIVE_TAXONOMY_VERSION) {
|
|
34
|
+
const tax = getTaxonomy(version);
|
|
35
|
+
const cats = tax.categories;
|
|
36
|
+
const clampList = (values, allowed) => {
|
|
37
|
+
if (!Array.isArray(values))
|
|
38
|
+
return [];
|
|
39
|
+
const set = new Set(allowed);
|
|
40
|
+
const out = [];
|
|
41
|
+
for (const v of values) {
|
|
42
|
+
const s = String(v).trim().toLowerCase().replace(/\s+/g, "_");
|
|
43
|
+
if (set.has(s) && !out.includes(s))
|
|
44
|
+
out.push(s);
|
|
45
|
+
}
|
|
46
|
+
return out;
|
|
47
|
+
};
|
|
48
|
+
const clampOne = (value, allowed, fallback) => {
|
|
49
|
+
const s = String(value ?? "").trim().toLowerCase().replace(/\s+/g, "_");
|
|
50
|
+
return allowed.includes(s) ? s : fallback;
|
|
51
|
+
};
|
|
52
|
+
return {
|
|
53
|
+
content_type: clampList(raw.content_type, cats.content_type),
|
|
54
|
+
subject: clampList(raw.subject, cats.subject),
|
|
55
|
+
action: clampList(raw.action, cats.action),
|
|
56
|
+
emotion: clampList(raw.emotion, cats.emotion),
|
|
57
|
+
setting: clampList(raw.setting, cats.setting),
|
|
58
|
+
composition: clampList(raw.composition, cats.composition),
|
|
59
|
+
motion: clampList(raw.motion, cats.motion),
|
|
60
|
+
energy: clampOne(raw.energy, cats.energy, "medium"),
|
|
61
|
+
audio_type: clampList(raw.audio_type, cats.audio_type)
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/** A compact, prompt-friendly rendering of the vocabulary to inline in the tagging prompt. */
|
|
65
|
+
export function taxonomyForPrompt(version = ACTIVE_TAXONOMY_VERSION) {
|
|
66
|
+
const tax = getTaxonomy(version);
|
|
67
|
+
const lines = [];
|
|
68
|
+
for (const [cat, values] of Object.entries(tax.categories)) {
|
|
69
|
+
lines.push(`- ${cat}: ${values.join(", ")}`);
|
|
70
|
+
}
|
|
71
|
+
return lines.join("\n");
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=taxonomy.js.map
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "v1",
|
|
3
|
+
"notes": "Controlled vocabulary for VidFarm clip curation. Start small (~50 tags); expand based on real usage. Clips store the version they were tagged against so the vocab can evolve without breaking existing clips. content_type was added additively to v1 (2026-07-08) — clips tagged before then simply carry no content_type until re-scanned.",
|
|
4
|
+
"categories": {
|
|
5
|
+
"content_type": [
|
|
6
|
+
"talking_head",
|
|
7
|
+
"b_roll",
|
|
8
|
+
"product_shot",
|
|
9
|
+
"screen_recording",
|
|
10
|
+
"demo",
|
|
11
|
+
"reaction",
|
|
12
|
+
"interview",
|
|
13
|
+
"establishing",
|
|
14
|
+
"lifestyle",
|
|
15
|
+
"text_graphic"
|
|
16
|
+
],
|
|
17
|
+
"subject": [
|
|
18
|
+
"person",
|
|
19
|
+
"single_speaker",
|
|
20
|
+
"multiple_people",
|
|
21
|
+
"hands",
|
|
22
|
+
"product",
|
|
23
|
+
"screen_ui",
|
|
24
|
+
"text_on_screen",
|
|
25
|
+
"animal",
|
|
26
|
+
"food",
|
|
27
|
+
"vehicle",
|
|
28
|
+
"no_subject"
|
|
29
|
+
],
|
|
30
|
+
"action": [
|
|
31
|
+
"talking",
|
|
32
|
+
"laughing",
|
|
33
|
+
"looking_at_camera",
|
|
34
|
+
"reacting",
|
|
35
|
+
"pointing",
|
|
36
|
+
"typing",
|
|
37
|
+
"hands_on_keyboard",
|
|
38
|
+
"walking",
|
|
39
|
+
"eating",
|
|
40
|
+
"holding_product",
|
|
41
|
+
"unboxing",
|
|
42
|
+
"gesturing",
|
|
43
|
+
"still"
|
|
44
|
+
],
|
|
45
|
+
"emotion": [
|
|
46
|
+
"amused",
|
|
47
|
+
"surprised",
|
|
48
|
+
"excited",
|
|
49
|
+
"confused",
|
|
50
|
+
"deadpan",
|
|
51
|
+
"exasperated",
|
|
52
|
+
"serious",
|
|
53
|
+
"neutral",
|
|
54
|
+
"sad",
|
|
55
|
+
"angry"
|
|
56
|
+
],
|
|
57
|
+
"setting": [
|
|
58
|
+
"indoor",
|
|
59
|
+
"outdoor",
|
|
60
|
+
"office",
|
|
61
|
+
"home",
|
|
62
|
+
"studio",
|
|
63
|
+
"kitchen",
|
|
64
|
+
"car",
|
|
65
|
+
"nature",
|
|
66
|
+
"urban",
|
|
67
|
+
"abstract_or_graphic"
|
|
68
|
+
],
|
|
69
|
+
"composition": [
|
|
70
|
+
"close_up",
|
|
71
|
+
"medium_shot",
|
|
72
|
+
"wide_shot",
|
|
73
|
+
"centered",
|
|
74
|
+
"off_center",
|
|
75
|
+
"over_the_shoulder",
|
|
76
|
+
"top_down",
|
|
77
|
+
"product_shot"
|
|
78
|
+
],
|
|
79
|
+
"motion": [
|
|
80
|
+
"static",
|
|
81
|
+
"low",
|
|
82
|
+
"medium",
|
|
83
|
+
"high",
|
|
84
|
+
"camera_shake",
|
|
85
|
+
"pan_or_tilt",
|
|
86
|
+
"zoom"
|
|
87
|
+
],
|
|
88
|
+
"energy": [
|
|
89
|
+
"low",
|
|
90
|
+
"medium",
|
|
91
|
+
"high"
|
|
92
|
+
],
|
|
93
|
+
"audio_type": [
|
|
94
|
+
"dialogue",
|
|
95
|
+
"voiceover",
|
|
96
|
+
"music",
|
|
97
|
+
"sfx",
|
|
98
|
+
"ambient",
|
|
99
|
+
"silence"
|
|
100
|
+
]
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// Shared data model for VidFarm clip curation — the "third pillar" library
|
|
2
|
+
// (viral DNA + HTML formats + CLIPS). One scanned scene becomes one Clip
|
|
3
|
+
// record. This shape is IDENTICAL across the two deployment targets
|
|
4
|
+
// (devcli/local SQLite and AWS DynamoDB) so users can move between local and
|
|
5
|
+
// cloud without lock-in. See drafts/clip-hunting/vidfarm_clip_curation_handoff.md.
|
|
6
|
+
export {};
|
|
7
|
+
//# sourceMappingURL=types.js.map
|