dsh-drawai 0.3.4 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (533) hide show
  1. package/LICENSE +4142 -0
  2. package/README.md +43 -10
  3. package/lib/audio-media.js +147 -0
  4. package/lib/audio-service.js +66 -0
  5. package/lib/canvas-format.js +134 -0
  6. package/lib/client.js +5075 -181
  7. package/lib/comfyui-default-workflow.mjs +1825 -0
  8. package/lib/comfyui-workflow.mjs +426 -0
  9. package/lib/document-commands.js +12 -0
  10. package/lib/document-core.js +29 -0
  11. package/lib/drawing-tasks.js +26 -0
  12. package/lib/imagegen.mjs +596 -0
  13. package/lib/index.js +1758 -129
  14. package/lib/media.js +26 -13
  15. package/lib/mgstyle-core.js +75 -0
  16. package/lib/motion/apply.js +180 -0
  17. package/lib/motion/asset-pack-lock.js +10 -0
  18. package/lib/motion/asset-pack.js +335 -0
  19. package/lib/motion/assets.js +290 -0
  20. package/lib/motion/audio.js +103 -0
  21. package/lib/motion/audiolevel.js +63 -0
  22. package/lib/motion/audiomix.js +144 -0
  23. package/lib/motion/beats.js +337 -0
  24. package/lib/motion/builtin-assets.js +62 -0
  25. package/lib/motion/canvas-spec.js +442 -0
  26. package/lib/motion/capture.js +95 -0
  27. package/lib/motion/cards.js +80 -0
  28. package/lib/motion/collect.js +447 -0
  29. package/lib/motion/delivery.js +112 -0
  30. package/lib/motion/edge-speech.py +90 -0
  31. package/lib/motion/entry.jsx +73 -0
  32. package/lib/motion/export.js +200 -0
  33. package/lib/motion/ffmpeg.js +42 -0
  34. package/lib/motion/fonts.js +71 -0
  35. package/lib/motion/jobs.js +113 -0
  36. package/lib/motion/json.js +4 -0
  37. package/lib/motion/media-catalog.js +59 -0
  38. package/lib/motion/mgstyles/BRIEF-TEMPLATE.md +63 -0
  39. package/lib/motion/mgstyles/briefs/01-flat-vector.md +104 -0
  40. package/lib/motion/mgstyles/briefs/02-line-art.md +97 -0
  41. package/lib/motion/mgstyles/briefs/03-isometric.md +108 -0
  42. package/lib/motion/mgstyles/briefs/05-cel-boil.md +145 -0
  43. package/lib/motion/mgstyles/briefs/06-collage.md +149 -0
  44. package/lib/motion/mgstyles/briefs/07-liquid.md +106 -0
  45. package/lib/motion/mgstyles/briefs/08-morph.md +111 -0
  46. package/lib/motion/mgstyles/briefs/09-bauhaus.md +150 -0
  47. package/lib/motion/mgstyles/briefs/10-synthwave.md +151 -0
  48. package/lib/motion/mgstyles/briefs/12-aurora-glass.md +147 -0
  49. package/lib/motion/mgstyles/briefs/18-hanazi.md +140 -0
  50. package/lib/motion/mgstyles/briefs/19-paperclip.md +156 -0
  51. package/lib/motion/mgstyles/briefs/20-pixel.md +118 -0
  52. package/lib/motion/mgstyles/briefs/22-hud.md +118 -0
  53. package/lib/motion/mgstyles/catalog.d/01-flat-vector.json +34 -0
  54. package/lib/motion/mgstyles/catalog.d/02-line-art.json +34 -0
  55. package/lib/motion/mgstyles/catalog.d/03-isometric.json +35 -0
  56. package/lib/motion/mgstyles/catalog.d/05-cel-boil.json +28 -0
  57. package/lib/motion/mgstyles/catalog.d/06-collage.json +28 -0
  58. package/lib/motion/mgstyles/catalog.d/07-liquid.json +28 -0
  59. package/lib/motion/mgstyles/catalog.d/08-morph.json +28 -0
  60. package/lib/motion/mgstyles/catalog.d/09-bauhaus.json +28 -0
  61. package/lib/motion/mgstyles/catalog.d/10-synthwave.json +28 -0
  62. package/lib/motion/mgstyles/catalog.d/12-aurora-glass.json +28 -0
  63. package/lib/motion/mgstyles/catalog.d/18-hanazi.json +28 -0
  64. package/lib/motion/mgstyles/catalog.d/19-paperclip.json +28 -0
  65. package/lib/motion/mgstyles/catalog.d/20-pixel.json +28 -0
  66. package/lib/motion/mgstyles/catalog.d/22-hud.json +28 -0
  67. package/lib/motion/mgstyles/catalog.json +1356 -0
  68. package/lib/motion/mgstyles/upstream/README.md +147 -0
  69. package/lib/motion/mgstyles/upstream/README.zh-CN.md +147 -0
  70. package/lib/motion/mgstyles/upstream/demos/01-flat-vector/cues.json +1 -0
  71. package/lib/motion/mgstyles/upstream/demos/01-flat-vector/index.html +712 -0
  72. package/lib/motion/mgstyles/upstream/demos/02-line-art/cues.json +331 -0
  73. package/lib/motion/mgstyles/upstream/demos/02-line-art/film.js +676 -0
  74. package/lib/motion/mgstyles/upstream/demos/02-line-art/index.html +18 -0
  75. package/lib/motion/mgstyles/upstream/demos/02-line-art/tools/dump_timing.mjs +26 -0
  76. package/lib/motion/mgstyles/upstream/demos/03-isometric/cues.json +109 -0
  77. package/lib/motion/mgstyles/upstream/demos/03-isometric/index.html +25 -0
  78. package/lib/motion/mgstyles/upstream/demos/03-isometric/js/anim.js +82 -0
  79. package/lib/motion/mgstyles/upstream/demos/03-isometric/js/main.js +219 -0
  80. package/lib/motion/mgstyles/upstream/demos/03-isometric/js/night5.js +180 -0
  81. package/lib/motion/mgstyles/upstream/demos/03-isometric/js/render.js +202 -0
  82. package/lib/motion/mgstyles/upstream/demos/03-isometric/js/title.js +167 -0
  83. package/lib/motion/mgstyles/upstream/demos/03-isometric/js/title5.js +294 -0
  84. package/lib/motion/mgstyles/upstream/demos/03-isometric/js/ui.js +141 -0
  85. package/lib/motion/mgstyles/upstream/demos/03-isometric/js/world.js +1399 -0
  86. package/lib/motion/mgstyles/upstream/demos/05-cel-boil/cues.json +157 -0
  87. package/lib/motion/mgstyles/upstream/demos/05-cel-boil/index.html +34 -0
  88. package/lib/motion/mgstyles/upstream/demos/05-cel-boil/js/engine.js +326 -0
  89. package/lib/motion/mgstyles/upstream/demos/05-cel-boil/js/scene.js +2036 -0
  90. package/lib/motion/mgstyles/upstream/demos/06-collage/assets/gen/layout.json +157 -0
  91. package/lib/motion/mgstyles/upstream/demos/06-collage/cues.json +80 -0
  92. package/lib/motion/mgstyles/upstream/demos/06-collage/index.html +889 -0
  93. package/lib/motion/mgstyles/upstream/demos/07-liquid/cues.json +100 -0
  94. package/lib/motion/mgstyles/upstream/demos/07-liquid/index.html +108 -0
  95. package/lib/motion/mgstyles/upstream/demos/07-liquid/js/engine.js +153 -0
  96. package/lib/motion/mgstyles/upstream/demos/07-liquid/js/links.js +64 -0
  97. package/lib/motion/mgstyles/upstream/demos/07-liquid/js/lookdev.js +46 -0
  98. package/lib/motion/mgstyles/upstream/demos/07-liquid/js/scene.js +963 -0
  99. package/lib/motion/mgstyles/upstream/demos/07-liquid/js/shaders.js +647 -0
  100. package/lib/motion/mgstyles/upstream/demos/07-liquid/js/textsdf.js +114 -0
  101. package/lib/motion/mgstyles/upstream/demos/07-liquid/js/type.js +50 -0
  102. package/lib/motion/mgstyles/upstream/demos/08-morph/cues.json +131 -0
  103. package/lib/motion/mgstyles/upstream/demos/08-morph/glyphs.json +1 -0
  104. package/lib/motion/mgstyles/upstream/demos/08-morph/index.html +901 -0
  105. package/lib/motion/mgstyles/upstream/demos/09-bauhaus/cues.json +270 -0
  106. package/lib/motion/mgstyles/upstream/demos/09-bauhaus/film.js +697 -0
  107. package/lib/motion/mgstyles/upstream/demos/09-bauhaus/index.html +12 -0
  108. package/lib/motion/mgstyles/upstream/demos/10-synthwave/cues.json +44 -0
  109. package/lib/motion/mgstyles/upstream/demos/10-synthwave/index.html +1028 -0
  110. package/lib/motion/mgstyles/upstream/demos/12-aurora-glass/cues.json +67 -0
  111. package/lib/motion/mgstyles/upstream/demos/12-aurora-glass/index.html +16 -0
  112. package/lib/motion/mgstyles/upstream/demos/12-aurora-glass/main.js +967 -0
  113. package/lib/motion/mgstyles/upstream/demos/12-aurora-glass/shaders.js +468 -0
  114. package/lib/motion/mgstyles/upstream/demos/18-hanazi/cues.json +117 -0
  115. package/lib/motion/mgstyles/upstream/demos/18-hanazi/index.html +541 -0
  116. package/lib/motion/mgstyles/upstream/demos/19-paperclip/assets/countries-110m.json +1 -0
  117. package/lib/motion/mgstyles/upstream/demos/19-paperclip/assets/stickers/meta.json +34 -0
  118. package/lib/motion/mgstyles/upstream/demos/19-paperclip/cues.json +285 -0
  119. package/lib/motion/mgstyles/upstream/demos/19-paperclip/index.html +599 -0
  120. package/lib/motion/mgstyles/upstream/demos/20-pixel/cues.json +42 -0
  121. package/lib/motion/mgstyles/upstream/demos/20-pixel/film.js +914 -0
  122. package/lib/motion/mgstyles/upstream/demos/20-pixel/index.html +18 -0
  123. package/lib/motion/mgstyles/upstream/demos/22-hud/cues.json +108 -0
  124. package/lib/motion/mgstyles/upstream/demos/22-hud/index.html +125 -0
  125. package/lib/motion/mgstyles/upstream/demos/22-hud/js/holo.js +275 -0
  126. package/lib/motion/mgstyles/upstream/demos/22-hud/js/hud.js +855 -0
  127. package/lib/motion/mgstyles/upstream/demos/22-hud/js/post.js +150 -0
  128. package/lib/motion/mgstyles/upstream/demos/22-hud/js/util.js +65 -0
  129. package/lib/motion/mgstyles/upstream/demos/22-hud/js/v5.js +142 -0
  130. package/lib/motion/mgstyles/upstream/harness/render.mjs +319 -0
  131. package/lib/motion/mgstyles/upstream/manifest.json +269 -0
  132. package/lib/motion/mgstyles/upstream/prompts/01-flat-vector.md +98 -0
  133. package/lib/motion/mgstyles/upstream/prompts/02-line-art.md +95 -0
  134. package/lib/motion/mgstyles/upstream/prompts/03-isometric.md +97 -0
  135. package/lib/motion/mgstyles/upstream/prompts/05-cel-boil.md +95 -0
  136. package/lib/motion/mgstyles/upstream/prompts/06-collage.md +95 -0
  137. package/lib/motion/mgstyles/upstream/prompts/07-liquid.md +98 -0
  138. package/lib/motion/mgstyles/upstream/prompts/08-morph.md +95 -0
  139. package/lib/motion/mgstyles/upstream/prompts/09-bauhaus.md +96 -0
  140. package/lib/motion/mgstyles/upstream/prompts/10-synthwave.md +95 -0
  141. package/lib/motion/mgstyles/upstream/prompts/12-aurora-glass.md +96 -0
  142. package/lib/motion/mgstyles/upstream/prompts/18-hanazi.md +95 -0
  143. package/lib/motion/mgstyles/upstream/prompts/19-paperclip.md +96 -0
  144. package/lib/motion/mgstyles/upstream/prompts/20-pixel.md +96 -0
  145. package/lib/motion/mgstyles/upstream/prompts/22-hud.md +95 -0
  146. package/lib/motion/mgstyles/upstream/rubric.md +51 -0
  147. package/lib/motion/mgstyles/upstream/template.md +36 -0
  148. package/lib/motion/pixelqa.js +198 -0
  149. package/lib/motion/preview.js +95 -0
  150. package/lib/motion/project.js +395 -0
  151. package/lib/motion/qa.js +290 -0
  152. package/lib/motion/reference-media.jsx +10 -0
  153. package/lib/motion/render-settings.js +112 -0
  154. package/lib/motion/render.js +159 -0
  155. package/lib/motion/scene-kit.js +183 -0
  156. package/lib/motion/sfx.js +232 -0
  157. package/lib/motion/shotcraft/ClipCard.tsx +204 -0
  158. package/lib/motion/shotcraft/catalog.json +2461 -0
  159. package/lib/motion/shotcraft/upstream/SKILL.md +286 -0
  160. package/lib/motion/shotcraft/upstream/assets/lib/Caption.tsx +54 -0
  161. package/lib/motion/shotcraft/upstream/assets/lib/ClipCard.tsx +200 -0
  162. package/lib/motion/shotcraft/upstream/assets/lib/DigitRoll.tsx +45 -0
  163. package/lib/motion/shotcraft/upstream/assets/lib/FlashCut.tsx +20 -0
  164. package/lib/motion/shotcraft/upstream/assets/lib/FlatPanel.tsx +65 -0
  165. package/lib/motion/shotcraft/upstream/assets/lib/PageCam.tsx +151 -0
  166. package/lib/motion/shotcraft/upstream/assets/lib/VerticalTicker.tsx +129 -0
  167. package/lib/motion/shotcraft/upstream/assets/lib/helpers/camera.tsx +75 -0
  168. package/lib/motion/shotcraft/upstream/assets/lib/helpers/motion.ts +40 -0
  169. package/lib/motion/shotcraft/upstream/assets/lib/helpers/rand.ts +11 -0
  170. package/lib/motion/shotcraft/upstream/assets/lib/helpers/shake.ts +10 -0
  171. package/lib/motion/shotcraft/upstream/assets/scripts/__pycache__/sfx-source.cpython-313.pyc +0 -0
  172. package/lib/motion/shotcraft/upstream/assets/scripts/build-readme-cover.mjs +49 -0
  173. package/lib/motion/shotcraft/upstream/assets/scripts/capture-template.mjs +210 -0
  174. package/lib/motion/shotcraft/upstream/assets/scripts/sfx-source.py +423 -0
  175. package/lib/motion/shotcraft/upstream/assets/scripts/smoke-render-demos.py +170 -0
  176. package/lib/motion/shotcraft/upstream/assets/scripts/test_sfx_source.py +157 -0
  177. package/lib/motion/shotcraft/upstream/demos/README.md +120 -0
  178. package/lib/motion/shotcraft/upstream/demos/_fixtures/Brand.tsx +112 -0
  179. package/lib/motion/shotcraft/upstream/demos/_fixtures/Fixtures.tsx +647 -0
  180. package/lib/motion/shotcraft/upstream/demos/_fixtures/Look.tsx +316 -0
  181. package/lib/motion/shotcraft/upstream/demos/_fixtures/Motion.tsx +82 -0
  182. package/lib/motion/shotcraft/upstream/demos/_fixtures/PageCam2D.tsx +115 -0
  183. package/lib/motion/shotcraft/upstream/demos/_fixtures/Polish.tsx +243 -0
  184. package/lib/motion/shotcraft/upstream/demos/_textures/live-layout.json +389 -0
  185. package/lib/motion/shotcraft/upstream/demos/camera/basic-3d-scene/Basic3DScene.tsx +250 -0
  186. package/lib/motion/shotcraft/upstream/demos/camera/crash-zoom-punch/CrashImpactReal.tsx +305 -0
  187. package/lib/motion/shotcraft/upstream/demos/camera/crash-zoom-punch/CrashZoomReal.tsx +246 -0
  188. package/lib/motion/shotcraft/upstream/demos/camera/cursor-flyover/CursorFlyover.tsx +390 -0
  189. package/lib/motion/shotcraft/upstream/demos/camera/depth-layer-moves/MultiplaneReal.tsx +138 -0
  190. package/lib/motion/shotcraft/upstream/demos/camera/graze-face-tour/GrazeFaceTour.tsx +542 -0
  191. package/lib/motion/shotcraft/upstream/demos/camera/overhead-camera-moves/OverheadTabletopDrop.tsx +315 -0
  192. package/lib/motion/shotcraft/upstream/demos/camera/overhead-camera-moves/TiltReveal.tsx +299 -0
  193. package/lib/motion/shotcraft/upstream/demos/camera/space-camera-moves/DroneDiveLanding.tsx +173 -0
  194. package/lib/motion/shotcraft/upstream/demos/camera/space-camera-moves/ExplodedView.tsx +348 -0
  195. package/lib/motion/shotcraft/upstream/demos/camera/steep-tilt-glide/SteepTiltGlide.tsx +366 -0
  196. package/lib/motion/shotcraft/upstream/demos/camera/tension-camera-moves/BulletTimeFreezeOrbit.tsx +279 -0
  197. package/lib/motion/shotcraft/upstream/demos/camera/tension-camera-moves/PullBackIsolation.tsx +184 -0
  198. package/lib/motion/shotcraft/upstream/demos/camera/terminal-3d/Terminal3D.tsx +390 -0
  199. package/lib/motion/shotcraft/upstream/demos/data/before-after-slider-scrub/BeforeAfterSliderScrub.tsx +155 -0
  200. package/lib/motion/shotcraft/upstream/demos/data/chart-live-moves/AxisRescaleShockV2.tsx +303 -0
  201. package/lib/motion/shotcraft/upstream/demos/data/chart-live-moves/OscilloscopeStreamV2.tsx +290 -0
  202. package/lib/motion/shotcraft/upstream/demos/data/chart-live-moves/UnitDotSwarmRegroupV2.tsx +278 -0
  203. package/lib/motion/shotcraft/upstream/demos/data/gauge-readout-moves/NeedleSweepSelftest.tsx +307 -0
  204. package/lib/motion/shotcraft/upstream/demos/data/gauge-readout-moves/TapeScrollFixedPointer.tsx +215 -0
  205. package/lib/motion/shotcraft/upstream/demos/data/scroll-brake-moves/BrakeReticleLock.tsx +299 -0
  206. package/lib/motion/shotcraft/upstream/demos/data/scroll-brake-moves/ChangelogScrollBrake.tsx +232 -0
  207. package/lib/motion/shotcraft/upstream/demos/data/timeline-travel/TimelineTravel.tsx +290 -0
  208. package/lib/motion/shotcraft/upstream/demos/effects/assemble-then-type-flyin/AssembleThenTypeFlyin.tsx +273 -0
  209. package/lib/motion/shotcraft/upstream/demos/effects/aurora-bloom-bg-flip/AuroraBloomBgFlip.tsx +167 -0
  210. package/lib/motion/shotcraft/upstream/demos/effects/brand-frame-snap/BrandFrameSnap.tsx +536 -0
  211. package/lib/motion/shotcraft/upstream/demos/effects/fui-hud-moves/ReticleLockOn.tsx +164 -0
  212. package/lib/motion/shotcraft/upstream/demos/effects/glow-flyline-moves/FlylineArc.tsx +175 -0
  213. package/lib/motion/shotcraft/upstream/demos/effects/glow-flyline-moves/GlowOrbAmbient.tsx +197 -0
  214. package/lib/motion/shotcraft/upstream/demos/effects/glow-flyline-moves/OrbFlylineRelay.tsx +241 -0
  215. package/lib/motion/shotcraft/upstream/demos/effects/icon-performance-moves/AttentionBounce.tsx +279 -0
  216. package/lib/motion/shotcraft/upstream/demos/effects/impact-feedback/AnimeImpact.tsx +230 -0
  217. package/lib/motion/shotcraft/upstream/demos/effects/impact-feedback/HitCounter.tsx +345 -0
  218. package/lib/motion/shotcraft/upstream/demos/effects/light-play-moves/SheenSweepRetry.tsx +275 -0
  219. package/lib/motion/shotcraft/upstream/demos/effects/light-play-moves/SpotlightSweepReveal.tsx +229 -0
  220. package/lib/motion/shotcraft/upstream/demos/effects/radial-ripple-phone-chips/RadialRipplePhoneChips.tsx +312 -0
  221. package/lib/motion/shotcraft/upstream/demos/effects/riso-print-hits/RisoMisregistrationHit.tsx +222 -0
  222. package/lib/motion/shotcraft/upstream/demos/effects/scan-bracket-sweep/ScanBracketSweep.tsx +295 -0
  223. package/lib/motion/shotcraft/upstream/demos/effects/scanline-annotate-focus/ScanlineAnnotateFocus.tsx +329 -0
  224. package/lib/motion/shotcraft/upstream/demos/effects/scanline-assemble-flyin/ScanlineAssembleFlyin.tsx +315 -0
  225. package/lib/motion/shotcraft/upstream/demos/effects/slam-entrance-moves/ImpactBurstKit.tsx +264 -0
  226. package/lib/motion/shotcraft/upstream/demos/effects/slam-entrance-moves/KanadaPerspectiveSnap.tsx +260 -0
  227. package/lib/motion/shotcraft/upstream/demos/effects/slam-entrance-moves/ScoreSlam.tsx +263 -0
  228. package/lib/motion/shotcraft/upstream/demos/effects/spotlight-sweep-moves/CornerSpotlightReveal.tsx +123 -0
  229. package/lib/motion/shotcraft/upstream/demos/effects/spotlight-sweep-moves/GlowWakeSleepPanel.tsx +187 -0
  230. package/lib/motion/shotcraft/upstream/demos/effects/spotlight-sweep-moves/SlideSpotlightPan.tsx +175 -0
  231. package/lib/motion/shotcraft/upstream/demos/interaction/autolayout-gap-dial/AutolayoutGapDial.tsx +305 -0
  232. package/lib/motion/shotcraft/upstream/demos/interaction/canvas-materialize-moves/DiagramCascadeBuild.tsx +350 -0
  233. package/lib/motion/shotcraft/upstream/demos/interaction/canvas-materialize-moves/PanelToCanvasMaterialize.tsx +352 -0
  234. package/lib/motion/shotcraft/upstream/demos/interaction/chip-grid-single-select-blackout/ChipGridSingleSelectBlackout.tsx +218 -0
  235. package/lib/motion/shotcraft/upstream/demos/interaction/collab-cursor-moves/CursorCastEnsemble.tsx +297 -0
  236. package/lib/motion/shotcraft/upstream/demos/interaction/glass-pill-dictation-typing/GlassPillDictationTyping.tsx +157 -0
  237. package/lib/motion/shotcraft/upstream/demos/interaction/hashtag-to-pill-materialize/HashtagToPillMaterialize.tsx +218 -0
  238. package/lib/motion/shotcraft/upstream/demos/interaction/picker-carousel-feature-cycle/PickerCarouselFeatureCycle.tsx +202 -0
  239. package/lib/motion/shotcraft/upstream/demos/interaction/segmented-thumb-hero/SegmentedThumbHero.tsx +265 -0
  240. package/lib/motion/shotcraft/upstream/demos/interaction/theme-switch-moves/PaletteThemeRipple.tsx +457 -0
  241. package/lib/motion/shotcraft/upstream/demos/interaction/theme-switch-moves/ThemeSweepToggle.tsx +446 -0
  242. package/lib/motion/shotcraft/upstream/demos/interaction/type-and-filter/TypeAndFilter.tsx +249 -0
  243. package/lib/motion/shotcraft/upstream/demos/interaction/voice-waveform-live/VoiceWaveformLive.tsx +278 -0
  244. package/lib/motion/shotcraft/upstream/demos/opening/crane-rise-reveal/CraneRiseReveal.tsx +332 -0
  245. package/lib/motion/shotcraft/upstream/demos/opening/dataviz-landscape-open/DatavizLandscapeOpen.tsx +358 -0
  246. package/lib/motion/shotcraft/upstream/demos/opening/fracture/Fracture.tsx +201 -0
  247. package/lib/motion/shotcraft/upstream/demos/opening/icon-field-colorize/IconFieldColorize.tsx +200 -0
  248. package/lib/motion/shotcraft/upstream/demos/opening/letterspace-materialize/LetterspaceMaterialize.tsx +208 -0
  249. package/lib/motion/shotcraft/upstream/demos/opening/magician-card-flourish/MagicianCardFlourish.tsx +341 -0
  250. package/lib/motion/shotcraft/upstream/demos/opening/orbit-ring-title-open/OrbitRingTitleOpen.tsx +389 -0
  251. package/lib/motion/shotcraft/upstream/demos/opening/spotlight-hero-card/SpotlightHeroCard.tsx +293 -0
  252. package/lib/motion/shotcraft/upstream/demos/opening/stroke-segment-build/StrokeSegmentBuild.tsx +183 -0
  253. package/lib/motion/shotcraft/upstream/demos/opening/text-as-mask/TextAsMask.tsx +245 -0
  254. package/lib/motion/shotcraft/upstream/demos/outro/grain-dissolve/GrainDissolve.tsx +222 -0
  255. package/lib/motion/shotcraft/upstream/demos/outro/logo-shrink-wordmark-lockup/LogoShrinkWordmarkLockup.tsx +223 -0
  256. package/lib/motion/shotcraft/upstream/demos/outro/neon-triple-marquee/NeonTripleMarquee.tsx +217 -0
  257. package/lib/motion/shotcraft/upstream/demos/outro/outro-group-photo-launch/OutroGroupPhotoLaunch.tsx +343 -0
  258. package/lib/motion/shotcraft/upstream/demos/outro/ui-strip-away-outro/UiStripAwayOutro.tsx +357 -0
  259. package/lib/motion/shotcraft/upstream/demos/outro/ui-to-brand-morph/IconFlipBloomLogo.tsx +187 -0
  260. package/lib/motion/shotcraft/upstream/demos/outro/ui-to-brand-morph/InputMorphsIntoLogo.tsx +333 -0
  261. package/lib/motion/shotcraft/upstream/demos/rhythm/beat-cut-moves/PaparazziFlash.tsx +274 -0
  262. package/lib/motion/shotcraft/upstream/demos/rhythm/beat-step-list-theme-cycle/BeatStepListThemeCycle.tsx +270 -0
  263. package/lib/motion/shotcraft/upstream/demos/rhythm/panel-grid-moves/ComicPanelSplit.tsx +430 -0
  264. package/lib/motion/shotcraft/upstream/demos/rhythm/quad-split-parallel-scenes/QuadSplitParallelScenes.tsx +252 -0
  265. package/lib/motion/shotcraft/upstream/demos/rhythm/rhythm-interrupt-moves/JumpCutPunchIn.tsx +106 -0
  266. package/lib/motion/shotcraft/upstream/demos/rhythm/sakuga-timing-shift/SakugaTimingShift.tsx +288 -0
  267. package/lib/motion/shotcraft/upstream/demos/rhythm/speed-ramp-freeze/FreezeAnnotateReal.tsx +362 -0
  268. package/lib/motion/shotcraft/upstream/demos/rhythm/trailer-grammar-moves/TrailerBumper.tsx +168 -0
  269. package/lib/motion/shotcraft/upstream/demos/transition/bottom-push-stack-wipe/BottomPushStackWipe.tsx +310 -0
  270. package/lib/motion/shotcraft/upstream/demos/transition/card-flip-reveal/CardFlipReveal.tsx +261 -0
  271. package/lib/motion/shotcraft/upstream/demos/transition/card-flock-tumble/CardFlockTumble.tsx +459 -0
  272. package/lib/motion/shotcraft/upstream/demos/transition/circle-match-iris/CircleMatchIris.tsx +294 -0
  273. package/lib/motion/shotcraft/upstream/demos/transition/gradient-transition/GradientTransition.tsx +161 -0
  274. package/lib/motion/shotcraft/upstream/demos/transition/line-carry-transition/LineCarryTransition.tsx +248 -0
  275. package/lib/motion/shotcraft/upstream/demos/transition/mosaic-reframe/MosaicReframe.tsx +359 -0
  276. package/lib/motion/shotcraft/upstream/demos/transition/page-turn-transitions/BarnDoorSplit.tsx +227 -0
  277. package/lib/motion/shotcraft/upstream/demos/transition/print-texture-transitions/InkBleedReveal.tsx +162 -0
  278. package/lib/motion/shotcraft/upstream/demos/transition/shot-transitions/PortalWipeV2.tsx +248 -0
  279. package/lib/motion/shotcraft/upstream/demos/transition/shot-transitions/WhipBrakeReal.tsx +186 -0
  280. package/lib/motion/shotcraft/upstream/demos/transition/tear-streak-transitions/GlitchDisplace.tsx +355 -0
  281. package/lib/motion/shotcraft/upstream/demos/transition/transition-hidden-cut/InvisibleCut.tsx +272 -0
  282. package/lib/motion/shotcraft/upstream/demos/transition/transition-hidden-cut/LightLeakBurn.tsx +262 -0
  283. package/lib/motion/shotcraft/upstream/demos/transition/transition-hidden-cut/VersusSlam.tsx +217 -0
  284. package/lib/motion/shotcraft/upstream/demos/transition/transition-travel/LetterformZoom.tsx +285 -0
  285. package/lib/motion/shotcraft/upstream/demos/transition/white-flash-logo-simplify-cut/WhiteFlashLogoSimplifyCut.tsx +205 -0
  286. package/lib/motion/shotcraft/upstream/demos/transition/wipe-transitions/BlindsSlice.tsx +178 -0
  287. package/lib/motion/shotcraft/upstream/demos/typography/blur-slide/BlurSlide.tsx +147 -0
  288. package/lib/motion/shotcraft/upstream/demos/typography/brace-expand/BraceExpand.tsx +143 -0
  289. package/lib/motion/shotcraft/upstream/demos/typography/brand-ink-open/BrandInkOpen.tsx +134 -0
  290. package/lib/motion/shotcraft/upstream/demos/typography/countdown-arc-scatter/CountdownArcScatter.tsx +217 -0
  291. package/lib/motion/shotcraft/upstream/demos/typography/flying-words/FlyingWords.tsx +198 -0
  292. package/lib/motion/shotcraft/upstream/demos/typography/glitch-cycle/GlitchCycle.tsx +221 -0
  293. package/lib/motion/shotcraft/upstream/demos/typography/gradient-word-sweep/GradientWordSweep.tsx +312 -0
  294. package/lib/motion/shotcraft/upstream/demos/typography/lead-word-zoom-assemble/LeadWordZoomAssemble.tsx +261 -0
  295. package/lib/motion/shotcraft/upstream/demos/typography/marker-underline-title/MarkerUnderlineTitle.tsx +121 -0
  296. package/lib/motion/shotcraft/upstream/demos/typography/outline-word-fill/OutlineWordFill.tsx +153 -0
  297. package/lib/motion/shotcraft/upstream/demos/typography/paper-title-card/PaperTitleCard.tsx +179 -0
  298. package/lib/motion/shotcraft/upstream/demos/typography/pill-chip-slot-cycle-handled/PillChipSlotCycleHandled.tsx +140 -0
  299. package/lib/motion/shotcraft/upstream/demos/typography/pill-slot-cycle/PillSlotCycle.tsx +309 -0
  300. package/lib/motion/shotcraft/upstream/demos/typography/text-column-converge/TextColumnConverge.tsx +191 -0
  301. package/lib/motion/shotcraft/upstream/demos/typography/title-demote-to-label/TitleDemoteToLabel.tsx +292 -0
  302. package/lib/motion/shotcraft/upstream/demos/typography/type-assembly-moves/LetterformDriftAssembly.tsx +135 -0
  303. package/lib/motion/shotcraft/upstream/demos/typography/type-assembly-moves/SplitTextStagger.tsx +124 -0
  304. package/lib/motion/shotcraft/upstream/demos/typography/type-assembly-moves/TextOnPath.tsx +255 -0
  305. package/lib/motion/shotcraft/upstream/demos/typography/type-entrance-moves/LetterDropPhysics.tsx +173 -0
  306. package/lib/motion/shotcraft/upstream/demos/typography/type-rhythm-sync/FontWeightPump.tsx +225 -0
  307. package/lib/motion/shotcraft/upstream/demos/typography/type-rhythm-sync/KaraokeFillSync.tsx +141 -0
  308. package/lib/motion/shotcraft/upstream/demos/typography/typewriter-moves/TypewriterErrorRetype.tsx +137 -0
  309. package/lib/motion/shotcraft/upstream/demos/typography/vertical-word-roll-blur-cycle/VerticalWordRollBlurCycle.tsx +147 -0
  310. package/lib/motion/shotcraft/upstream/demos/typography/word-relay-filmstrip/WordRelayFilmstrip.tsx +409 -0
  311. package/lib/motion/shotcraft/upstream/demos/typography/word-relay-geometry/WordRelayGeometry.tsx +280 -0
  312. package/lib/motion/shotcraft/upstream/demos/ui-entrance/avatar-bracket-carousel/AvatarBracketCarousel.tsx +271 -0
  313. package/lib/motion/shotcraft/upstream/demos/ui-entrance/bezier-source-converge-merge/BezierSourceConvergeMerge.tsx +385 -0
  314. package/lib/motion/shotcraft/upstream/demos/ui-entrance/card-stack/CardStack.tsx +312 -0
  315. package/lib/motion/shotcraft/upstream/demos/ui-entrance/carousel-3d/Carousel3D.tsx +303 -0
  316. package/lib/motion/shotcraft/upstream/demos/ui-entrance/clipcard-looping/ClipCardLooping.tsx +77 -0
  317. package/lib/motion/shotcraft/upstream/demos/ui-entrance/cloner-depth-echo/ClonerDepthEcho.tsx +311 -0
  318. package/lib/motion/shotcraft/upstream/demos/ui-entrance/deck-deal-flyin/DeckDealFlyin.tsx +307 -0
  319. package/lib/motion/shotcraft/upstream/demos/ui-entrance/doc-park-left-pill-deal/DocParkLeftPillDeal.tsx +349 -0
  320. package/lib/motion/shotcraft/upstream/demos/ui-entrance/document-typewriter-reveal/DocumentTypewriterReveal.tsx +255 -0
  321. package/lib/motion/shotcraft/upstream/demos/ui-entrance/draw-svg-trace/DrawSvgTrace.tsx +244 -0
  322. package/lib/motion/shotcraft/upstream/demos/ui-entrance/element-body-moves/ContactShadowLift.tsx +217 -0
  323. package/lib/motion/shotcraft/upstream/demos/ui-entrance/integration-hub-map/IntegrationHubMap.tsx +652 -0
  324. package/lib/motion/shotcraft/upstream/demos/ui-entrance/list-reveal/ListReveal.tsx +153 -0
  325. package/lib/motion/shotcraft/upstream/demos/ui-entrance/list-stack-press/ListStackPress.tsx +238 -0
  326. package/lib/motion/shotcraft/upstream/demos/ui-entrance/neon-frame-forerun/NeonFrameForerun.tsx +324 -0
  327. package/lib/motion/shotcraft/upstream/demos/ui-entrance/neon-frame-orbit-drop/NeonFrameForerunOrbit.tsx +354 -0
  328. package/lib/motion/shotcraft/upstream/demos/ui-entrance/page-waterfall-wall/PageWaterfallWall.tsx +101 -0
  329. package/lib/motion/shotcraft/upstream/demos/ui-entrance/page-waterfall-wall/VerticalTicker.tsx +129 -0
  330. package/lib/motion/shotcraft/upstream/demos/ui-entrance/product-card-progressive-assemble/ProductCardProgressiveAssemble.tsx +297 -0
  331. package/lib/motion/shotcraft/upstream/demos/ui-entrance/radial-wave/RadialWave.tsx +57 -0
  332. package/lib/motion/shotcraft/upstream/demos/ui-entrance/research-card-stack-scroll/ResearchCardStackScroll.tsx +155 -0
  333. package/lib/motion/shotcraft/upstream/demos/ui-entrance/row-embed/RowEmbed.tsx +175 -0
  334. package/lib/motion/shotcraft/upstream/demos/ui-entrance/value-stagger-gradient/ValueStaggerGradient.tsx +153 -0
  335. package/lib/motion/shotcraft/upstream/demos/ui-entrance/wall-reveal-moves/BentoLightUp.tsx +146 -0
  336. package/lib/motion/shotcraft/upstream/demos/ui-entrance/wall-reveal-moves/WireframeDrawOn.tsx +226 -0
  337. package/lib/motion/shotcraft/upstream/gallery/api/library.json +1 -0
  338. package/lib/motion/shotcraft/upstream/manifest.json +2715 -0
  339. package/lib/motion/shotcraft/upstream/references/aesthetic-rules.md +193 -0
  340. package/lib/motion/shotcraft/upstream/references/final-review.md +121 -0
  341. package/lib/motion/shotcraft/upstream/references/guided-free-creation.md +199 -0
  342. package/lib/motion/shotcraft/upstream/references/jianying-export.md +183 -0
  343. package/lib/motion/shotcraft/upstream/references/music-beat-sync.md +257 -0
  344. package/lib/motion/shotcraft/upstream/references/pipeline.md +382 -0
  345. package/lib/motion/shotcraft/upstream/references/publish-upload-post.md +111 -0
  346. package/lib/motion/shotcraft/upstream/references/sequences/promo-energy-arc.md +50 -0
  347. package/lib/motion/shotcraft/upstream/references/sfx-sourcing.md +95 -0
  348. package/lib/motion/shotcraft/upstream/references/shots/camera/basic-3d-scene.md +41 -0
  349. package/lib/motion/shotcraft/upstream/references/shots/camera/crash-zoom-punch.md +52 -0
  350. package/lib/motion/shotcraft/upstream/references/shots/camera/cursor-flyover.md +39 -0
  351. package/lib/motion/shotcraft/upstream/references/shots/camera/depth-layer-moves.md +39 -0
  352. package/lib/motion/shotcraft/upstream/references/shots/camera/graze-face-tour.md +57 -0
  353. package/lib/motion/shotcraft/upstream/references/shots/camera/overhead-camera-moves.md +51 -0
  354. package/lib/motion/shotcraft/upstream/references/shots/camera/space-camera-moves.md +46 -0
  355. package/lib/motion/shotcraft/upstream/references/shots/camera/steep-tilt-glide.md +52 -0
  356. package/lib/motion/shotcraft/upstream/references/shots/camera/tension-camera-moves.md +43 -0
  357. package/lib/motion/shotcraft/upstream/references/shots/camera/terminal-3d.md +42 -0
  358. package/lib/motion/shotcraft/upstream/references/shots/data/before-after-slider-scrub.md +48 -0
  359. package/lib/motion/shotcraft/upstream/references/shots/data/chart-live-moves.md +52 -0
  360. package/lib/motion/shotcraft/upstream/references/shots/data/gauge-readout-moves.md +45 -0
  361. package/lib/motion/shotcraft/upstream/references/shots/data/scroll-brake-moves.md +46 -0
  362. package/lib/motion/shotcraft/upstream/references/shots/data/timeline-travel.md +44 -0
  363. package/lib/motion/shotcraft/upstream/references/shots/effects/assemble-then-type-flyin.md +63 -0
  364. package/lib/motion/shotcraft/upstream/references/shots/effects/aurora-bloom-bg-flip.md +54 -0
  365. package/lib/motion/shotcraft/upstream/references/shots/effects/brand-frame-snap.md +58 -0
  366. package/lib/motion/shotcraft/upstream/references/shots/effects/fui-hud-moves.md +35 -0
  367. package/lib/motion/shotcraft/upstream/references/shots/effects/glow-flyline-moves.md +53 -0
  368. package/lib/motion/shotcraft/upstream/references/shots/effects/icon-performance-moves.md +42 -0
  369. package/lib/motion/shotcraft/upstream/references/shots/effects/impact-feedback.md +50 -0
  370. package/lib/motion/shotcraft/upstream/references/shots/effects/light-play-moves.md +46 -0
  371. package/lib/motion/shotcraft/upstream/references/shots/effects/radial-ripple-phone-chips.md +56 -0
  372. package/lib/motion/shotcraft/upstream/references/shots/effects/riso-print-hits.md +49 -0
  373. package/lib/motion/shotcraft/upstream/references/shots/effects/scan-bracket-sweep.md +50 -0
  374. package/lib/motion/shotcraft/upstream/references/shots/effects/scanline-annotate-focus.md +53 -0
  375. package/lib/motion/shotcraft/upstream/references/shots/effects/scanline-assemble-flyin.md +53 -0
  376. package/lib/motion/shotcraft/upstream/references/shots/effects/slam-entrance-moves.md +55 -0
  377. package/lib/motion/shotcraft/upstream/references/shots/effects/spotlight-sweep-moves.md +52 -0
  378. package/lib/motion/shotcraft/upstream/references/shots/interaction/autolayout-gap-dial.md +60 -0
  379. package/lib/motion/shotcraft/upstream/references/shots/interaction/canvas-materialize-moves.md +57 -0
  380. package/lib/motion/shotcraft/upstream/references/shots/interaction/chip-grid-single-select-blackout.md +51 -0
  381. package/lib/motion/shotcraft/upstream/references/shots/interaction/collab-cursor-moves.md +41 -0
  382. package/lib/motion/shotcraft/upstream/references/shots/interaction/glass-pill-dictation-typing.md +53 -0
  383. package/lib/motion/shotcraft/upstream/references/shots/interaction/hashtag-to-pill-materialize.md +60 -0
  384. package/lib/motion/shotcraft/upstream/references/shots/interaction/picker-carousel-feature-cycle.md +55 -0
  385. package/lib/motion/shotcraft/upstream/references/shots/interaction/segmented-thumb-hero.md +57 -0
  386. package/lib/motion/shotcraft/upstream/references/shots/interaction/theme-switch-moves.md +47 -0
  387. package/lib/motion/shotcraft/upstream/references/shots/interaction/type-and-filter.md +37 -0
  388. package/lib/motion/shotcraft/upstream/references/shots/interaction/voice-waveform-live.md +54 -0
  389. package/lib/motion/shotcraft/upstream/references/shots/opening/brand-ink-open.md +36 -0
  390. package/lib/motion/shotcraft/upstream/references/shots/opening/crane-rise-reveal.md +56 -0
  391. package/lib/motion/shotcraft/upstream/references/shots/opening/dataviz-landscape-open.md +56 -0
  392. package/lib/motion/shotcraft/upstream/references/shots/opening/fracture.md +40 -0
  393. package/lib/motion/shotcraft/upstream/references/shots/opening/icon-field-colorize.md +60 -0
  394. package/lib/motion/shotcraft/upstream/references/shots/opening/letterspace-materialize.md +57 -0
  395. package/lib/motion/shotcraft/upstream/references/shots/opening/magician-card-flourish.md +73 -0
  396. package/lib/motion/shotcraft/upstream/references/shots/opening/orbit-ring-title-open.md +87 -0
  397. package/lib/motion/shotcraft/upstream/references/shots/opening/spotlight-hero-card.md +55 -0
  398. package/lib/motion/shotcraft/upstream/references/shots/opening/stroke-segment-build.md +50 -0
  399. package/lib/motion/shotcraft/upstream/references/shots/opening/text-as-mask.md +59 -0
  400. package/lib/motion/shotcraft/upstream/references/shots/outro/grain-dissolve.md +38 -0
  401. package/lib/motion/shotcraft/upstream/references/shots/outro/logo-shrink-wordmark-lockup.md +38 -0
  402. package/lib/motion/shotcraft/upstream/references/shots/outro/neon-triple-marquee.md +59 -0
  403. package/lib/motion/shotcraft/upstream/references/shots/outro/outro-group-photo-launch.md +39 -0
  404. package/lib/motion/shotcraft/upstream/references/shots/outro/ui-strip-away-outro.md +61 -0
  405. package/lib/motion/shotcraft/upstream/references/shots/outro/ui-to-brand-morph.md +52 -0
  406. package/lib/motion/shotcraft/upstream/references/shots/rhythm/beat-cut-moves.md +50 -0
  407. package/lib/motion/shotcraft/upstream/references/shots/rhythm/beat-step-list-theme-cycle.md +66 -0
  408. package/lib/motion/shotcraft/upstream/references/shots/rhythm/panel-grid-moves.md +43 -0
  409. package/lib/motion/shotcraft/upstream/references/shots/rhythm/quad-split-parallel-scenes.md +38 -0
  410. package/lib/motion/shotcraft/upstream/references/shots/rhythm/rhythm-interrupt-moves.md +38 -0
  411. package/lib/motion/shotcraft/upstream/references/shots/rhythm/sakuga-timing-shift.md +56 -0
  412. package/lib/motion/shotcraft/upstream/references/shots/rhythm/speed-ramp-freeze.md +44 -0
  413. package/lib/motion/shotcraft/upstream/references/shots/rhythm/trailer-grammar-moves.md +38 -0
  414. package/lib/motion/shotcraft/upstream/references/shots/transition/bottom-push-stack-wipe.md +63 -0
  415. package/lib/motion/shotcraft/upstream/references/shots/transition/card-flip-reveal.md +57 -0
  416. package/lib/motion/shotcraft/upstream/references/shots/transition/card-flock-tumble.md +63 -0
  417. package/lib/motion/shotcraft/upstream/references/shots/transition/circle-match-iris.md +58 -0
  418. package/lib/motion/shotcraft/upstream/references/shots/transition/gradient-transition.md +45 -0
  419. package/lib/motion/shotcraft/upstream/references/shots/transition/line-carry-transition.md +55 -0
  420. package/lib/motion/shotcraft/upstream/references/shots/transition/mosaic-reframe.md +49 -0
  421. package/lib/motion/shotcraft/upstream/references/shots/transition/page-turn-transitions.md +42 -0
  422. package/lib/motion/shotcraft/upstream/references/shots/transition/print-texture-transitions.md +42 -0
  423. package/lib/motion/shotcraft/upstream/references/shots/transition/shot-transitions.md +45 -0
  424. package/lib/motion/shotcraft/upstream/references/shots/transition/tear-streak-transitions.md +48 -0
  425. package/lib/motion/shotcraft/upstream/references/shots/transition/transition-hidden-cut.md +55 -0
  426. package/lib/motion/shotcraft/upstream/references/shots/transition/transition-travel.md +43 -0
  427. package/lib/motion/shotcraft/upstream/references/shots/transition/white-flash-logo-simplify-cut.md +41 -0
  428. package/lib/motion/shotcraft/upstream/references/shots/transition/wipe-transitions.md +42 -0
  429. package/lib/motion/shotcraft/upstream/references/shots/typography/blur-slide.md +59 -0
  430. package/lib/motion/shotcraft/upstream/references/shots/typography/brace-expand.md +55 -0
  431. package/lib/motion/shotcraft/upstream/references/shots/typography/countdown-arc-scatter.md +65 -0
  432. package/lib/motion/shotcraft/upstream/references/shots/typography/document-typewriter-reveal.md +42 -0
  433. package/lib/motion/shotcraft/upstream/references/shots/typography/flying-words.md +64 -0
  434. package/lib/motion/shotcraft/upstream/references/shots/typography/glitch-cycle.md +64 -0
  435. package/lib/motion/shotcraft/upstream/references/shots/typography/gradient-word-sweep.md +54 -0
  436. package/lib/motion/shotcraft/upstream/references/shots/typography/lead-word-zoom-assemble.md +91 -0
  437. package/lib/motion/shotcraft/upstream/references/shots/typography/marker-underline-title.md +51 -0
  438. package/lib/motion/shotcraft/upstream/references/shots/typography/outline-word-fill.md +66 -0
  439. package/lib/motion/shotcraft/upstream/references/shots/typography/paper-title-card.md +38 -0
  440. package/lib/motion/shotcraft/upstream/references/shots/typography/pill-chip-slot-cycle-handled.md +66 -0
  441. package/lib/motion/shotcraft/upstream/references/shots/typography/pill-slot-cycle.md +58 -0
  442. package/lib/motion/shotcraft/upstream/references/shots/typography/text-column-converge.md +61 -0
  443. package/lib/motion/shotcraft/upstream/references/shots/typography/title-demote-to-label.md +64 -0
  444. package/lib/motion/shotcraft/upstream/references/shots/typography/type-assembly-moves.md +45 -0
  445. package/lib/motion/shotcraft/upstream/references/shots/typography/type-entrance-moves.md +41 -0
  446. package/lib/motion/shotcraft/upstream/references/shots/typography/type-rhythm-sync.md +51 -0
  447. package/lib/motion/shotcraft/upstream/references/shots/typography/typewriter-moves.md +42 -0
  448. package/lib/motion/shotcraft/upstream/references/shots/typography/vertical-word-roll-blur-cycle.md +63 -0
  449. package/lib/motion/shotcraft/upstream/references/shots/typography/word-relay-filmstrip.md +57 -0
  450. package/lib/motion/shotcraft/upstream/references/shots/typography/word-relay-geometry.md +75 -0
  451. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/avatar-bracket-carousel.md +59 -0
  452. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/bezier-source-converge-merge.md +67 -0
  453. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/card-stack.md +60 -0
  454. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/carousel-3d.md +66 -0
  455. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/cloner-depth-echo.md +40 -0
  456. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/deck-deal-flyin.md +45 -0
  457. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/doc-park-left-pill-deal.md +62 -0
  458. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/draw-svg-trace.md +55 -0
  459. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/element-body-moves.md +37 -0
  460. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/integration-hub-map.md +55 -0
  461. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/list-reveal.md +50 -0
  462. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/list-stack-press.md +43 -0
  463. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/neon-frame-forerun.md +55 -0
  464. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/neon-frame-orbit-drop.md +51 -0
  465. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/page-waterfall-wall.md +51 -0
  466. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/product-card-progressive-assemble.md +56 -0
  467. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/radial-wave.md +49 -0
  468. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/research-card-stack-scroll.md +57 -0
  469. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/row-embed.md +36 -0
  470. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/value-stagger-gradient.md +64 -0
  471. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/wall-reveal-moves.md +47 -0
  472. package/lib/motion/shotcraft/upstream/references/sound-design.md +170 -0
  473. package/lib/motion/shotcraft/upstream/references/workbench.md +157 -0
  474. package/lib/motion/shotcraft/upstream/template/TEMPLATE.md +103 -0
  475. package/lib/motion/shotcraft/upstream/template/THEMES.md +44 -0
  476. package/lib/motion/shotcraft/upstream/template/package.json +20 -0
  477. package/lib/motion/shotcraft/upstream/template/remotion.config.ts +6 -0
  478. package/lib/motion/shotcraft/upstream/template/src/Root.tsx +17 -0
  479. package/lib/motion/shotcraft/upstream/template/src/aifl/Caption.tsx +65 -0
  480. package/lib/motion/shotcraft/upstream/template/src/aifl/DigitRoll.tsx +48 -0
  481. package/lib/motion/shotcraft/upstream/template/src/aifl/FlashCut.tsx +24 -0
  482. package/lib/motion/shotcraft/upstream/template/src/aifl/Main.tsx +192 -0
  483. package/lib/motion/shotcraft/upstream/template/src/aifl/PaperTitleCard.tsx +111 -0
  484. package/lib/motion/shotcraft/upstream/template/src/aifl/live/PageCam.tsx +150 -0
  485. package/lib/motion/shotcraft/upstream/template/src/aifl/live/SceneDetail.tsx +158 -0
  486. package/lib/motion/shotcraft/upstream/template/src/aifl/live/SceneFlyIn.tsx +468 -0
  487. package/lib/motion/shotcraft/upstream/template/src/aifl/live/SceneOpen.tsx +552 -0
  488. package/lib/motion/shotcraft/upstream/template/src/aifl/live/SceneOutroLive.tsx +411 -0
  489. package/lib/motion/shotcraft/upstream/template/src/aifl/live/ScenePapers.tsx +200 -0
  490. package/lib/motion/shotcraft/upstream/template/src/aifl/live/SceneWbr.tsx +324 -0
  491. package/lib/motion/shotcraft/upstream/template/src/aifl/live-layout.json +389 -0
  492. package/lib/motion/shotcraft/upstream/template/src/index.ts +4 -0
  493. package/lib/motion/shotcraft/upstream/template/src/themes/ThemedFilm.tsx +7 -0
  494. package/lib/motion/shotcraft/upstream/template/src/themes/manifest.tsx +68 -0
  495. package/lib/motion/shotcraft/upstream/template/src/themes/palette-assets.json +1 -0
  496. package/lib/motion/shotcraft/upstream/template/src/themes/palette-assets.ts +24 -0
  497. package/lib/motion/shotcraft/upstream/template/src/themes/palettes.json +74 -0
  498. package/lib/motion/shotcraft/upstream/template/src/themes/visual-theme.tsx +91 -0
  499. package/lib/motion/shotcraft/upstream/template/src/workbench.ts +213 -0
  500. package/lib/motion/shotcraft/upstream/template/tsconfig.json +15 -0
  501. package/lib/motion/spec.js +113 -0
  502. package/lib/motion/style-engine.js +153 -0
  503. package/lib/motion/style-frames.js +268 -0
  504. package/lib/motion/sync.js +214 -0
  505. package/lib/motion/theme.js +121 -0
  506. package/lib/motion/voice.js +417 -0
  507. package/lib/motion-agent-files.js +82 -0
  508. package/lib/motion-agent-protocol.js +66 -0
  509. package/lib/motion-agent-tools.js +196 -0
  510. package/lib/motion-agent.js +171 -0
  511. package/lib/motion-capture.js +92 -0
  512. package/lib/motion-core.js +238 -0
  513. package/lib/motion-film-review.js +71 -0
  514. package/lib/motion-library.js +210 -0
  515. package/lib/motion-planner.js +254 -0
  516. package/lib/motion-project.js +80 -0
  517. package/lib/motion-render-worker.js +145 -0
  518. package/lib/motion-session.js +32 -0
  519. package/lib/motion-source.js +49 -0
  520. package/lib/motion-tasks.js +452 -0
  521. package/lib/motion-vision.js +43 -0
  522. package/lib/mxfile.js +27 -7
  523. package/lib/routing/node-runtime.js +14 -0
  524. package/lib/routing/router.js +211 -0
  525. package/lib/routing/vendor/README.md +96 -0
  526. package/lib/routing/vendor/libavoid-routing.js +753 -0
  527. package/lib/routing/vendor/libavoid.min.js +59 -0
  528. package/lib/shotcard-core.js +39 -0
  529. package/lib/shotcard-reference.js +56 -0
  530. package/lib/style-kernel.js +37 -3
  531. package/lib/task-patches.js +26 -2
  532. package/lib/video-export.js +17 -2
  533. package/package.json +29 -4
@@ -0,0 +1,210 @@
1
+ // capture-template.mjs — 把真实产品页面变成宣传片素材的三件套采集脚本模板
2
+ // 通用页面截图管线模板:BASE/输出目录/路由/选择器全部提升为 CONFIG
3
+ //
4
+ // 产出三件套(宣传片 3D 页面平面的全部素材来源):
5
+ // 1. 每个路由的全页 2x 截图(deviceScaleFactor: 2,供 3D 场景贴图不糊)
6
+ // 2. per-element cutout(元素级 PNG,含 omitBackground 透明抠图,供"浮起的 UI 芯片")
7
+ // 3. layout.json(每个元素在整页坐标系里的 bbox,供动画按真实版式定位)
8
+ //
9
+ // 用法:
10
+ // node capture-template.mjs
11
+ //
12
+ // 前置条件(三条都是硬前提):
13
+ // 1. 目标产品在本地跑起来(CONFIG.BASE 可访问);
14
+ // 2. npm i puppeteer(本脚本唯一依赖);
15
+ // 3. 【红线】假数据先注入 —— 截图前页面必须已填充"虚构但真实感"的演示数据。
16
+ // 空库/lorem ipsum 截出来的图直接废片;真实客户数据则不能出片。
17
+ // 先做数据注入(seed 脚本 / fixture 环境),确认页面肉眼可看后再跑本脚本。
18
+ //
19
+ // 改造指南:只改下面的 CONFIG。每个 page 条目 = 一个路由;
20
+ // boxes 只记坐标(进 layout.json),cutouts 记坐标 + 存元素 PNG。
21
+
22
+ import puppeteer from 'puppeteer';
23
+ import fs from 'fs';
24
+ import path from 'path';
25
+ import { fileURLToPath } from 'url';
26
+
27
+ // ======================================================================
28
+ // CONFIG — 按目标产品修改(下面的值是假想的 "acme-crm" 产品示例)
29
+ // ======================================================================
30
+ const CONFIG = {
31
+ // 目标产品本地地址(先把产品跑起来)
32
+ BASE: 'http://localhost:3000',
33
+
34
+ // 截图与 layout.json 的输出目录(相对本脚本所在目录;通常指向
35
+ // Remotion 项目的 public/textures/live,供 staticFile() 引用)
36
+ OUT_DIR: '../../public/textures/live',
37
+ LAYOUT_JSON: '../../src/live-layout.json',
38
+
39
+ // 视口:1920x1080 @2x 是 3D 页面平面贴图的经验值
40
+ VIEWPORT: { width: 1920, height: 1080, deviceScaleFactor: 2 },
41
+
42
+ // 每页导航完成后的额外静置毫秒数(等字体/异步数据;见 settle())
43
+ SETTLE_MS: 600,
44
+
45
+ // 路由清单:每条 = 一个要采集的页面
46
+ PAGES: [
47
+ {
48
+ name: 'home', // layout.json 里的 key,也是全页截图文件名前缀
49
+ path: '/', // 相对 BASE 的路由
50
+ // waitMs: 1500, // 可选:本页额外等待(如实时协作数据同步)
51
+ // 只记坐标不出图的元素(进 layout.<name>.boxes.<key>)
52
+ boxes: [
53
+ // all: true → 记所有匹配元素的 bbox 数组;否则只记第一个
54
+ { key: 'sections', selector: 'main h2', all: true },
55
+ ],
56
+ // 元素级截图(cutout PNG + bbox)。omitBackground: true 出透明底,
57
+ // 用于要"浮"在页面平面上方、自带材质合成的 UI 芯片
58
+ cutouts: [
59
+ { name: 'nav', selector: 'header, [role="banner"]' },
60
+ { name: 'card', selector: 'article', all: true, max: 12 }, // card1.png, card2.png…
61
+ { name: 'float-search', selector: 'input', parent: true, omitBackground: true },
62
+ { name: 'float-filter', selector: 'main button', omitBackground: true },
63
+ ],
64
+ // 可选:把某些元素设为 visibility:hidden 后再截一张全页图,
65
+ // 作为"元素飞入"镜头的空底板(<name>-empty.png)
66
+ hideForEmptyPlate: 'article',
67
+ },
68
+ {
69
+ name: 'detail',
70
+ // 详情页路由写死一条演示数据的 id(假想示例;换成你产品里
71
+ // 注入的假数据实体 id)
72
+ path: '/customers/demo-0001',
73
+ boxes: [
74
+ { key: 'rows', selector: 'table tbody tr, [role="row"]', all: true, max: 8 },
75
+ ],
76
+ cutouts: [],
77
+ // 可选:截图前在页面里执行的交互(如点开某个 tab),
78
+ // 返回后额外等 interactWaitMs 再补一张 <name>-after.png 全页图
79
+ interact: () => {
80
+ const tab = [...document.querySelectorAll('button, [role="tab"], a')]
81
+ .find((e) => /订单/.test(e.textContent ?? ''));
82
+ if (tab) { tab.click(); return true; }
83
+ return false;
84
+ },
85
+ interactWaitMs: 800,
86
+ },
87
+ {
88
+ name: 'reports',
89
+ path: '/reports/weekly',
90
+ waitMs: 1500, // 例:等编辑器/协作文档内容同步进来
91
+ boxes: [
92
+ { key: 'paras', selector: '.ProseMirror > p', all: true },
93
+ ],
94
+ cutouts: [],
95
+ },
96
+ ],
97
+ };
98
+ // ======================================================================
99
+ // 以下为通用采集逻辑,一般无需修改
100
+ // ======================================================================
101
+
102
+ const here = path.dirname(fileURLToPath(import.meta.url));
103
+ const outDir = path.resolve(here, CONFIG.OUT_DIR);
104
+ const layoutPath = path.resolve(here, CONFIG.LAYOUT_JSON);
105
+ fs.mkdirSync(outDir, { recursive: true });
106
+ fs.mkdirSync(path.dirname(layoutPath), { recursive: true });
107
+
108
+ const browser = await puppeteer.launch();
109
+ const page = await browser.newPage();
110
+ await page.setViewport(CONFIG.VIEWPORT);
111
+
112
+ // 等字体加载完再静置片刻——避免截到 FOUT/骨架屏
113
+ const settle = async () => {
114
+ await page.evaluate(() => document.fonts.ready);
115
+ await new Promise((r) => setTimeout(r, CONFIG.SETTLE_MS));
116
+ };
117
+
118
+ // 元素 bbox 换算到整页坐标系(fullPage 截图的坐标系)
119
+ const pageBox = (el) =>
120
+ el.evaluate((e) => {
121
+ const r = e.getBoundingClientRect();
122
+ return { x: r.x + window.scrollX, y: r.y + window.scrollY, w: r.width, h: r.height };
123
+ });
124
+
125
+ const layout = { pageW: CONFIG.VIEWPORT.width };
126
+
127
+ for (const pg of CONFIG.PAGES) {
128
+ await page.goto(`${CONFIG.BASE}${pg.path}`, { waitUntil: 'networkidle0' });
129
+ await settle();
130
+ if (pg.waitMs) await new Promise((r) => setTimeout(r, pg.waitMs));
131
+
132
+ const entry = { pageH: await page.evaluate(() => document.documentElement.scrollHeight) };
133
+ layout[pg.name] = entry;
134
+
135
+ // ---- 1. 全页 2x 截图 ----
136
+ await page.screenshot({ path: `${outDir}/${pg.name}-full.png`, fullPage: true });
137
+ console.log(`captured ${pg.name}-full`, entry.pageH);
138
+
139
+ // ---- 2. boxes:只记坐标 ----
140
+ entry.boxes = {};
141
+ for (const b of pg.boxes ?? []) {
142
+ const els = await page.$$(b.selector);
143
+ const picked = b.all ? els.slice(0, b.max ?? els.length) : els.slice(0, 1);
144
+ const boxes = [];
145
+ for (const el of picked) boxes.push(await pageBox(el));
146
+ entry.boxes[b.key] = b.all ? boxes : boxes[0] ?? null;
147
+ console.log(` boxes.${b.key}:`, boxes.length);
148
+ }
149
+
150
+ // ---- 3. cutouts:元素 PNG + bbox ----
151
+ entry.cutouts = [];
152
+ for (const c of pg.cutouts ?? []) {
153
+ let els = await page.$$(c.selector);
154
+ if (c.parent) {
155
+ // 取匹配元素的父容器(如 input 的圆角外壳)
156
+ const parents = [];
157
+ for (const el of els) {
158
+ const h = await el.evaluateHandle((e) => e.parentElement);
159
+ const p = h.asElement();
160
+ if (p) parents.push(p);
161
+ }
162
+ els = parents;
163
+ }
164
+ const picked = c.all ? els.slice(0, c.max ?? els.length) : els.slice(0, 1);
165
+ for (let i = 0; i < picked.length; i++) {
166
+ const file = c.all ? `${c.name}${i + 1}.png` : `${c.name}.png`;
167
+ const bb = await pageBox(picked[i]);
168
+ try {
169
+ await picked[i].screenshot({
170
+ path: `${outDir}/${file}`,
171
+ omitBackground: !!c.omitBackground,
172
+ });
173
+ } catch (e) {
174
+ console.log(` cutout miss ${file}:`, e.message);
175
+ continue;
176
+ }
177
+ entry.cutouts.push({ file, ...bb });
178
+ console.log(` captured ${file}`, bb);
179
+ }
180
+ if (picked.length === 0) console.log(` cutout miss ${c.name} (no match: ${c.selector})`);
181
+ }
182
+
183
+ // ---- 4. 页内交互后的补充全页图(可选) ----
184
+ if (pg.interact) {
185
+ const did = await page.evaluate(pg.interact);
186
+ if (did) {
187
+ await new Promise((r) => setTimeout(r, pg.interactWaitMs ?? 800));
188
+ await page.screenshot({ path: `${outDir}/${pg.name}-after.png`, fullPage: true });
189
+ entry.afterPageH = await page.evaluate(() => document.documentElement.scrollHeight);
190
+ console.log(`captured ${pg.name}-after`);
191
+ }
192
+ }
193
+
194
+ // ---- 5. 空底板(可选):隐藏元素后再截一张,供飞入镜头 ----
195
+ if (pg.hideForEmptyPlate) {
196
+ await page.evaluate((sel) => {
197
+ document.querySelectorAll(sel).forEach((el) => { el.style.visibility = 'hidden'; });
198
+ }, pg.hideForEmptyPlate);
199
+ await page.screenshot({ path: `${outDir}/${pg.name}-empty.png`, fullPage: true });
200
+ console.log(`captured ${pg.name}-empty`);
201
+ // 复原,避免影响同页后续步骤(当前实现里本步骤已是该页最后一步)
202
+ await page.evaluate((sel) => {
203
+ document.querySelectorAll(sel).forEach((el) => { el.style.visibility = ''; });
204
+ }, pg.hideForEmptyPlate);
205
+ }
206
+ }
207
+
208
+ fs.writeFileSync(layoutPath, JSON.stringify(layout, null, 1));
209
+ console.log('wrote', layoutPath);
210
+ await browser.close();
@@ -0,0 +1,423 @@
1
+ #!/usr/bin/env python3
2
+ """Find Freesound SFX and freeze licensed MP3 previews in a video project.
3
+
4
+ Python standard library; get/analyze also require ffmpeg and ffprobe.
5
+ No API key, global cache, login, or proxy configuration changes.
6
+ """
7
+
8
+ import argparse
9
+ import array
10
+ from concurrent.futures import ThreadPoolExecutor
11
+ from datetime import datetime, timezone
12
+ import hashlib
13
+ from html.parser import HTMLParser
14
+ import json
15
+ import math
16
+ from pathlib import Path
17
+ import re
18
+ import shutil
19
+ import subprocess
20
+ import sys
21
+ import tempfile
22
+ from urllib.parse import unquote, urlencode, urljoin, urlparse
23
+ from urllib.request import Request, urlopen
24
+
25
+
26
+ SITE = "https://freesound.org"
27
+ MANIFEST = "audio-manifest.json"
28
+ CREDITS = "FREESOUND-CREDITS.md"
29
+ MAX_BYTES = 32 * 1024 * 1024
30
+ VOID = set("area base br col embed hr img input link meta param source track wbr".split())
31
+
32
+
33
+ class Node:
34
+ def __init__(self, tag="root", attrs=()):
35
+ self.tag, self.attrs, self.children = tag, dict(attrs), []
36
+
37
+ def nodes(self):
38
+ yield self
39
+ for child in self.children:
40
+ if isinstance(child, Node):
41
+ yield from child.nodes()
42
+
43
+ def text(self):
44
+ return " ".join(c.text() if isinstance(c, Node) else c for c in self.children).strip()
45
+
46
+ def has_class(self, name):
47
+ return name in self.attrs.get("class", "").split()
48
+
49
+
50
+ class Document(HTMLParser):
51
+ def __init__(self, content):
52
+ super().__init__(convert_charrefs=True)
53
+ self.root = Node()
54
+ self.stack = [self.root]
55
+ self.feed(content)
56
+ self.close()
57
+
58
+ def handle_starttag(self, tag, attrs):
59
+ node = Node(tag, attrs)
60
+ self.stack[-1].children.append(node)
61
+ if tag not in VOID:
62
+ self.stack.append(node)
63
+
64
+ def handle_startendtag(self, tag, attrs):
65
+ self.handle_starttag(tag, attrs)
66
+ if tag not in VOID:
67
+ self.handle_endtag(tag)
68
+
69
+ def handle_endtag(self, tag):
70
+ for i in range(len(self.stack) - 1, 0, -1):
71
+ if self.stack[i].tag == tag:
72
+ del self.stack[i:]
73
+ break
74
+
75
+ def handle_data(self, data):
76
+ self.stack[-1].children.append(data)
77
+
78
+
79
+ def request(url):
80
+ req = Request(url, headers={"User-Agent": "video-shotcraft/1.0 (Freesound SFX sourcing)"})
81
+ with urlopen(req, timeout=30) as response:
82
+ data = response.read(MAX_BYTES + 1)
83
+ if len(data) > MAX_BYTES:
84
+ raise ValueError("Response exceeds 32 MiB; select a shorter sound or download the original manually")
85
+ return data, response.geturl(), response.headers.get_content_type()
86
+
87
+
88
+ def web_page(url):
89
+ data, final_url, content_type = request(url)
90
+ if urlparse(final_url).hostname != "freesound.org" or content_type != "text/html":
91
+ raise ValueError("Expected a Freesound HTML page")
92
+ return Document(data.decode("utf-8")), final_url
93
+
94
+
95
+ def sound_id(value):
96
+ value = value.removeprefix("fs:")
97
+ if not re.fullmatch(r"[1-9][0-9]*", value):
98
+ raise ValueError(f"Invalid Freesound ID: {value!r}; use fs:12345")
99
+ return value
100
+
101
+
102
+ def license_code(url):
103
+ parsed = urlparse(url)
104
+ if parsed.scheme not in {"http", "https"} or parsed.hostname not in {"creativecommons.org", "www.creativecommons.org"}:
105
+ return None
106
+ path = parsed.path.rstrip("/")
107
+ if path == "/publicdomain/zero/1.0":
108
+ return "CC0-1.0"
109
+ match = re.fullmatch(r"/licenses/by/(1\.0|2\.0|2\.5|3\.0|4\.0)", path)
110
+ return f"CC-BY-{match[1]}" if match else None
111
+
112
+
113
+ def check_license(code, policy):
114
+ if policy == "any":
115
+ return
116
+ if code == "CC0-1.0" or (policy == "by" and code and code.startswith("CC-BY-")):
117
+ return
118
+ raise ValueError(f"License {code or 'unknown'} is outside --license {policy}; CC BY requires --license by")
119
+
120
+
121
+ def player_data(node, page_author=None):
122
+ attrs = node.attrs
123
+ sid = sound_id(attrs["data-sound-id"])
124
+ title, author = attrs.get("data-title"), page_author or attrs.get("data-username")
125
+ duration = float(attrs["data-duration"])
126
+ if not title or not author or not math.isfinite(duration) or duration <= 0:
127
+ raise ValueError(f"Incomplete Freesound metadata for fs:{sid}")
128
+ preview = attrs.get("data-mp3", "")
129
+ parsed = urlparse(preview)
130
+ if (parsed.scheme != "https" or parsed.hostname != "cdn.freesound.org"
131
+ or not re.fullmatch(rf"/previews/[0-9]+/{sid}_[0-9]+-(lq|hq)\.mp3", parsed.path)):
132
+ raise ValueError(f"Missing or unexpected preview URL for fs:{sid}")
133
+ return {"id": f"fs:{sid}", "title": title, "author": author,
134
+ "duration_seconds": duration, "preview_url": preview.replace("-lq.mp3", "-hq.mp3"),
135
+ "downloads": int(attrs.get("data-num-downloads") or 0)}
136
+
137
+
138
+ def stats(nodes):
139
+ rating, count = None, None
140
+ for node in nodes:
141
+ label = node.attrs.get("aria-label", "")
142
+ if label.startswith("Average rating of "):
143
+ rating = float(label.removeprefix("Average rating of "))
144
+ if node.has_class("bw-rating__count"):
145
+ found = re.search(r"\(([0-9]+)(?: ratings?)?\)", node.text())
146
+ if found:
147
+ count = int(found[1])
148
+ return {"rating": rating, "rating_count": count}
149
+
150
+
151
+ def parse_search(doc):
152
+ rows = []
153
+ for block in doc.root.nodes():
154
+ if not block.has_class("bw-search__result"):
155
+ continue
156
+ nodes = list(block.nodes())
157
+ player = next((n for n in nodes if n.attrs.get("data-sound-id")), None)
158
+ if player is None:
159
+ raise ValueError("Freesound search markup changed: result has no sound metadata")
160
+ row = player_data(player)
161
+ links = [urljoin(SITE, n.attrs.get("href", "")) for n in nodes if n.tag == "a"]
162
+ row["page_url"] = next((u for u in links if u.endswith(f"/sounds/{sound_id(row['id'])}/")), None)
163
+ row["license_label"] = next((n.attrs["title"][9:] for n in nodes
164
+ if n.attrs.get("title", "").startswith("License: ")), "unknown")
165
+ desc = next((n for n in nodes if any(c.startswith("max-h-60px")
166
+ for c in n.attrs.get("class", "").split())), None)
167
+ row["description"] = " ".join((desc.attrs.get("title") or desc.text()).split()) if desc else ""
168
+ row.update(stats(nodes))
169
+ rows.append(row)
170
+ if not rows and not re.search(r"no (?:sounds|results)|0 sounds|couldn't find|did not match", doc.root.text(), re.I):
171
+ raise ValueError("No recognizable search results; check Freesound availability or update the parser")
172
+ return rows
173
+
174
+
175
+ def search(args):
176
+ if args.license == "any":
177
+ labels = None
178
+ filters = []
179
+ else:
180
+ labels = {"Creative Commons 0"} | ({"Attribution"} if args.license == "by" else set())
181
+ filters = ['license:("Creative Commons 0" OR "Attribution")' if args.license == "by"
182
+ else 'license:"Creative Commons 0"']
183
+ filters.append(f"duration:[{args.min_duration} TO {args.max_duration if args.max_duration is not None else '*'}]")
184
+ found = {}
185
+ for page in range(1, args.pages + 1):
186
+ query = urlencode({"q": args.query, "f": " ".join(filters), "page": page, "s": "Automatic by relevance"})
187
+ doc, _ = web_page(f"{SITE}/search/?{query}")
188
+ for row in parse_search(doc):
189
+ # Enforce locally too; never trust a server-side filter alone.
190
+ if labels is not None and row["license_label"] not in labels:
191
+ continue
192
+ if row["duration_seconds"] < args.min_duration:
193
+ continue
194
+ if args.max_duration is not None and row["duration_seconds"] > args.max_duration:
195
+ continue
196
+ found.setdefault(row["id"], row)
197
+ result = {"provider": "freesound", "query": args.query, "license_policy": args.license,
198
+ "ordering": "relevance; popularity is supporting evidence only",
199
+ "results": list(found.values())[:args.limit]}
200
+ emit(result, args.output)
201
+ return 0
202
+
203
+
204
+ def detail(sid):
205
+ doc, final_url = web_page(f"{SITE}/s/{sid}/")
206
+ nodes = list(doc.root.nodes())
207
+ player = next((n for n in nodes if n.attrs.get("data-sound-id") == sid and n.attrs.get("data-mp3")), None)
208
+ canonical = re.fullmatch(rf"/people/([^/]+)/sounds/{sid}/", urlparse(final_url).path)
209
+ if player is None or canonical is None:
210
+ raise ValueError(f"Missing canonical sound page for fs:{sid}")
211
+ # Large players omit data-username; the canonical page supplies the creator.
212
+ row = player_data(player, unquote(canonical[1]))
213
+ row["page_url"] = final_url
214
+ licenses = {n.attrs["href"] for n in nodes if n.tag == "a"
215
+ and urlparse(n.attrs.get("href", "")).hostname in {"creativecommons.org", "www.creativecommons.org"}}
216
+ if len(licenses) != 1:
217
+ raise ValueError(f"Missing or ambiguous license on fs:{sid}")
218
+ row["license_url"] = licenses.pop()
219
+ row["license"] = license_code(row["license_url"])
220
+ row["description"] = next((n.attrs.get("content", "") for n in nodes
221
+ if n.attrs.get("name") == "twitter:description"), "")
222
+ row.update(stats(nodes))
223
+ return row
224
+
225
+
226
+ def require_audio_tools():
227
+ if not all(shutil.which(name) for name in ("ffmpeg", "ffprobe")):
228
+ raise ValueError("get/analyze require ffmpeg and ffprobe on PATH")
229
+
230
+
231
+ def analyze(path):
232
+ require_audio_tools()
233
+ path = Path(path).resolve()
234
+ probe = subprocess.run(["ffprobe", "-v", "error", "-select_streams", "a:0", "-show_entries",
235
+ "stream=codec_name,sample_rate,channels:format=duration", "-of", "json", str(path)],
236
+ capture_output=True, text=True, check=True)
237
+ info = json.loads(probe.stdout)
238
+ stream = info["streams"][0]
239
+ channels = int(stream["channels"])
240
+ if not 1 <= channels <= 8:
241
+ raise ValueError("Expected an audio file with 1–8 channels")
242
+ rate, window = 48000, 0.01
243
+ block_size = int(rate * window) * channels * 4
244
+ energies, peak, squares, samples = [], 0.0, 0.0, 0
245
+ # Preserve channels: downmixing first can hide opposite-polarity peaks.
246
+ with tempfile.TemporaryFile() as errors:
247
+ with subprocess.Popen(["ffmpeg", "-v", "error", "-i", str(path), "-map", "0:a:0", "-ar", str(rate),
248
+ "-f", "f32le", "-acodec", "pcm_f32le", "pipe:1"],
249
+ stdout=subprocess.PIPE, stderr=errors) as process:
250
+ while True:
251
+ raw = process.stdout.read(block_size)
252
+ if not raw:
253
+ break
254
+ values = array.array("f")
255
+ values.frombytes(raw)
256
+ if sys.byteorder != "little":
257
+ values.byteswap()
258
+ energy = sum(v * v for v in values)
259
+ energies.append(math.sqrt(energy / len(values)))
260
+ peak = max(peak, max(abs(v) for v in values))
261
+ squares += energy
262
+ samples += len(values)
263
+ if process.wait() != 0:
264
+ errors.seek(0)
265
+ raise ValueError(errors.read().decode("utf-8", errors="replace"))
266
+ if not samples or not peak:
267
+ raise ValueError("Audio is empty or silent")
268
+ loudest = max(energies)
269
+ active = [i for i, e in enumerate(energies) if e >= loudest * 0.01]
270
+ duration = samples / (rate * channels)
271
+ return {"codec": stream["codec_name"], "sample_rate": int(stream["sample_rate"]), "channels": channels,
272
+ "duration_seconds": round(duration, 6), "sample_peak_dbfs": round(20 * math.log10(peak), 3),
273
+ "rms_dbfs": round(20 * math.log10(math.sqrt(squares / samples)), 3),
274
+ "energy_window_seconds": window,
275
+ "energy_peak_seconds": round(min(duration, (energies.index(loudest) + 0.5) * window), 6),
276
+ "active_start_seconds": round(active[0] * window, 6),
277
+ "active_end_seconds": round(min(duration, (active[-1] + 1) * window), 6),
278
+ "activity_threshold_db_below_peak_rms": -40}
279
+
280
+
281
+ def write_json(path, data):
282
+ path = Path(path)
283
+ path.parent.mkdir(parents=True, exist_ok=True)
284
+ with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8", dir=path.parent, delete=False) as tmp:
285
+ json.dump(data, tmp, ensure_ascii=False, indent=2, allow_nan=False)
286
+ tmp.write("\n")
287
+ Path(tmp.name).replace(path)
288
+
289
+
290
+ def emit(data, output=None):
291
+ if output:
292
+ write_json(output, data)
293
+ print(json.dumps(data, ensure_ascii=False, indent=2, allow_nan=False))
294
+
295
+
296
+ def digest(path):
297
+ return hashlib.sha256(Path(path).read_bytes()).hexdigest()
298
+
299
+
300
+ def write_credits(out, rows):
301
+ # Plain text inside a fence keeps uploaded titles from becoming Markdown links/instructions.
302
+ lines = ["# Freesound audio credits", "", "Generated from audio-manifest.json; preserve with the video.", "",
303
+ "CC BY entries require attribution in the published video's credits/description.", "",
304
+ "Downloaded files are lossy MP3 previews, not uploader originals.", ""]
305
+ for row in rows:
306
+ lines += ["~~~text", f"{row['file']} — {row['title']} by {row['author']}".replace("~", "\\~"),
307
+ row["page_url"], f"{row['license']}: {row['license_url']}",
308
+ "Edits: " + ("; ".join(row.get("changes", [])) or "None recorded; update if edited."), "~~~", ""]
309
+ (out / CREDITS).write_text("\n".join(lines), encoding="utf-8")
310
+
311
+
312
+ def download_one(sid, out, policy, existing):
313
+ row = detail(sid) # Refresh the actual license, even when search/cache metadata is stale.
314
+ check_license(row["license"], policy)
315
+ dst = out / f"fs-{sid}.mp3"
316
+ old = next((r for r in existing if r["id"] == row["id"]), None)
317
+ if dst.exists():
318
+ if old is None or old.get("file") != dst.name or digest(dst) != old.get("sha256"):
319
+ raise ValueError(f"Refusing to overwrite untracked/modified file: {dst}")
320
+ return {**old, **row, "analysis": analyze(dst), "license_checked_at": datetime.now(timezone.utc).isoformat()}
321
+ payload, final_url, mime = request(row["preview_url"])
322
+ if urlparse(final_url).hostname != "cdn.freesound.org" or mime not in {"audio/mpeg", "audio/mp3", "application/octet-stream"}:
323
+ raise ValueError("Expected a Freesound MP3 preview, not an HTML error page")
324
+ with tempfile.NamedTemporaryFile(dir=out, suffix=".mp3", delete=False) as tmp:
325
+ tmp.write(payload)
326
+ tmp_path = Path(tmp.name)
327
+ try:
328
+ measurements = analyze(tmp_path) # Decode successfully before admitting the file.
329
+ if measurements["codec"] != "mp3":
330
+ raise ValueError("Preview is not MP3 audio")
331
+ row.update({"provider": "freesound", "file": dst.name, "sha256": digest(tmp_path),
332
+ "download_url": final_url, "download_kind": "mp3-preview-hq", "is_preview": True,
333
+ "downloaded_at": datetime.now(timezone.utc).isoformat(),
334
+ "license_checked_at": datetime.now(timezone.utc).isoformat(), "changes": [], "analysis": measurements})
335
+ # Exclusive creation: another process/user's file is never replaced.
336
+ with dst.open("xb") as target:
337
+ target.write(payload)
338
+ return row
339
+ finally:
340
+ tmp_path.unlink(missing_ok=True)
341
+
342
+
343
+ def get(args):
344
+ require_audio_tools()
345
+ ids = list(dict.fromkeys(sound_id(value) for value in args.ids))
346
+ out = Path(args.out_dir).resolve()
347
+ out.mkdir(parents=True, exist_ok=True)
348
+ lock = out / ".freesound-download.lock"
349
+ try:
350
+ handle = lock.open("x")
351
+ except FileExistsError:
352
+ raise ValueError(f"Download already active in {out}; remove {lock.name} only if a previous run crashed")
353
+ try:
354
+ with handle:
355
+ return get_locked(args, ids, out)
356
+ finally:
357
+ lock.unlink(missing_ok=True)
358
+
359
+
360
+ def get_locked(args, ids, out):
361
+ manifest_path = out / MANIFEST
362
+ manifest = json.loads(manifest_path.read_text(encoding="utf-8")) if manifest_path.exists() else {"version": 1, "assets": []}
363
+ if manifest.get("version") != 1 or not isinstance(manifest.get("assets"), list):
364
+ raise ValueError("Unsupported audio manifest; use a separate output directory")
365
+ existing = manifest["assets"]
366
+
367
+ def run(sid):
368
+ try:
369
+ return {"ok": True, "asset": download_one(sid, out, args.license, existing)}
370
+ except (OSError, ValueError, KeyError, IndexError, subprocess.SubprocessError) as error:
371
+ return {"ok": False, "id": f"fs:{sid}", "error": str(error)}
372
+
373
+ results = []
374
+ with ThreadPoolExecutor(max_workers=args.workers) as pool:
375
+ for result in pool.map(run, ids):
376
+ results.append(result)
377
+ if result["ok"]:
378
+ row = result["asset"]
379
+ manifest["assets"] = [r for r in manifest["assets"] if r["id"] != row["id"]] + [row]
380
+ write_json(manifest_path, manifest)
381
+ write_credits(out, manifest["assets"])
382
+ emit({"out_dir": str(out), "results": results})
383
+ return 0 if all(r["ok"] for r in results) else 1
384
+
385
+
386
+ def main(argv=None):
387
+ parser = argparse.ArgumentParser(description=__doc__)
388
+ commands = parser.add_subparsers(dest="command", required=True)
389
+ find = commands.add_parser("search", help="Search public Freesound pages; emit JSON candidates")
390
+ find.add_argument("query")
391
+ find.add_argument("--license", choices=("cc0", "by", "any"), default="any", help="any does not filter by license; by permits CC0 + CC BY")
392
+ find.add_argument("--min-duration", type=float, default=0)
393
+ find.add_argument("--max-duration", type=float)
394
+ find.add_argument("--limit", type=int, default=8)
395
+ find.add_argument("--pages", type=int, choices=range(1, 4), default=1)
396
+ find.add_argument("--output", help="Optional project-local JSON path")
397
+ fetch = commands.add_parser("get", help="Download IDs, verify licenses/audio, save manifest and credits")
398
+ fetch.add_argument("ids", nargs="+")
399
+ fetch.add_argument("--out-dir", required=True, help="Video project's public/audio/sfx directory")
400
+ fetch.add_argument("--license", choices=("cc0", "by", "any"), default="any")
401
+ fetch.add_argument("--workers", type=int, choices=range(1, 5), default=3)
402
+ measure = commands.add_parser("analyze", help="Measure decoded audio (10 ms RMS peak; not LUFS/true peak)")
403
+ measure.add_argument("file")
404
+ measure.add_argument("--output")
405
+ args = parser.parse_args(argv)
406
+ try:
407
+ if args.command == "search":
408
+ if not 1 <= args.limit <= 45 or not math.isfinite(args.min_duration) or args.min_duration < 0:
409
+ raise ValueError("Use --limit 1–45 and a nonnegative finite --min-duration")
410
+ if args.max_duration is not None and (not math.isfinite(args.max_duration) or args.max_duration <= args.min_duration):
411
+ raise ValueError("--max-duration must be finite and greater than --min-duration")
412
+ return search(args)
413
+ if args.command == "get":
414
+ return get(args)
415
+ emit(analyze(args.file), args.output)
416
+ return 0
417
+ except (OSError, ValueError, KeyError, IndexError, subprocess.SubprocessError) as error:
418
+ print(f"sfx-source: {error}", file=sys.stderr)
419
+ return 1
420
+
421
+
422
+ if __name__ == "__main__":
423
+ sys.exit(main())