@hypit/hypit 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +26 -0
- package/README.md +203 -0
- package/README.zh-CN.md +195 -0
- package/bin/hypit.mjs +46 -0
- package/dist/public/artifact.d.ts +75 -0
- package/dist/public/author-kit.d.ts +559 -0
- package/dist/public/browser-capture.d.ts +56 -0
- package/dist/public/caption.d.ts +751 -0
- package/dist/public/composition.d.ts +672 -0
- package/dist/public/endpoint-kit.d.ts +366 -0
- package/dist/public/generation.d.ts +591 -0
- package/dist/public/hyperframes.d.ts +774 -0
- package/dist/public/media.d.ts +708 -0
- package/dist/public/model-kit.d.ts +523 -0
- package/dist/public/narrative.d.ts +315 -0
- package/dist/public/program-space.d.ts +193 -0
- package/dist/public/runtime-kit.d.ts +427 -0
- package/dist/public/semantic-track.d.ts +418 -0
- package/dist/public/spatial.d.ts +1193 -0
- package/dist/public/speech.d.ts +391 -0
- package/dist/public/studio-adapter.d.ts +686 -0
- package/dist/public/svs.d.ts +311 -0
- package/dist/public/temporal-markup.d.ts +343 -0
- package/dist/public/temporal.d.ts +497 -0
- package/dist/public/text.d.ts +817 -0
- package/dist/public/visual-ir.d.ts +98 -0
- package/docs/public/hypit-logo-dark.svg +36 -0
- package/docs/public/hypit-logo-light.svg +38 -0
- package/examples/README.md +24 -0
- package/examples/minimal-author-package/README.md +18 -0
- package/examples/minimal-author-package/packages/example-component/README.md +44 -0
- package/examples/minimal-author-package/packages/example-component/package.json +27 -0
- package/examples/minimal-author-package/packages/example-component/preview/Box.png +0 -0
- package/examples/minimal-author-package/packages/example-component/preview/build.svrun +6 -0
- package/examples/minimal-author-package/packages/example-component/preview/preview.svml +47 -0
- package/examples/minimal-author-package/packages/example-component/preview/recipes.svs +8 -0
- package/examples/minimal-author-package/packages/example-component/src/activation.ts +10 -0
- package/examples/minimal-author-package/packages/example-component/src/component.ts +44 -0
- package/examples/minimal-author-package/packages/example-component/src/fragment.ts +32 -0
- package/examples/minimal-author-package/packages/example-component/src/index.ts +6 -0
- package/examples/minimal-author-package/packages/example-component/src/manifest.ts +82 -0
- package/examples/minimal-author-package/packages/example-component/src/style.ts +13 -0
- package/examples/minimal-author-package/packages/example-component/src/surface.ts +41 -0
- package/examples/minimal-author-package/packages/example-component/src/temporal.ts +6 -0
- package/examples/minimal-author-package/packages/example-component/tsconfig.json +18 -0
- package/examples/semantic-composition/README.md +35 -0
- package/examples/semantic-composition/chat.svml +25 -0
- package/examples/semantic-composition/chat.svrun +6 -0
- package/examples/semantic-composition/chat.svs +4 -0
- package/examples/semantic-composition/hypit.runtime.json +8 -0
- package/examples/semantic-composition/packages/chat-scene/README.md +29 -0
- package/examples/semantic-composition/packages/chat-scene/package.json +11 -0
- package/examples/semantic-composition/packages/chat-scene/src/activation.ts +112 -0
- package/examples/semantic-composition/packages/chat-scene/src/render.ts +64 -0
- package/examples/semantic-composition/packages/chat-scene/tsconfig.json +18 -0
- package/examples/semantic-composition/packages/responsive-explainer/README.md +45 -0
- package/examples/semantic-composition/packages/responsive-explainer/package.json +11 -0
- package/examples/semantic-composition/packages/responsive-explainer/src/activation.ts +90 -0
- package/examples/semantic-composition/packages/responsive-explainer/src/render.ts +68 -0
- package/examples/semantic-composition/packages/responsive-explainer/tsconfig.json +18 -0
- package/package.json +259 -7
- package/packages/artifact/README.md +10 -0
- package/packages/artifact/package.json +16 -0
- package/packages/artifact/src/activation.ts +8 -0
- package/packages/artifact/src/index.ts +24 -0
- package/packages/audio-track/README.md +43 -0
- package/packages/audio-track/package.json +36 -0
- package/packages/audio-track/src/activation.ts +20 -0
- package/packages/audio-track/src/component.ts +40 -0
- package/packages/audio-track/src/fragment.ts +63 -0
- package/packages/audio-track/src/index.ts +7 -0
- package/packages/audio-track/src/manifest.ts +151 -0
- package/packages/audio-track/src/program.ts +254 -0
- package/packages/audio-track/src/surface.ts +187 -0
- package/packages/audio-track/src/types.ts +62 -0
- package/packages/audio-track-studio/README.md +3 -0
- package/packages/audio-track-studio/package.json +14 -0
- package/packages/audio-track-studio/src/activation.ts +7 -0
- package/packages/audio-track-studio/src/index.ts +72 -0
- package/packages/author-kit/README.md +31 -0
- package/packages/author-kit/package.json +16 -0
- package/packages/author-kit/src/index.ts +59 -0
- package/packages/background-removal/README.md +29 -0
- package/packages/background-removal/package.json +16 -0
- package/packages/background-removal/src/activation.ts +18 -0
- package/packages/background-removal/src/component.ts +32 -0
- package/packages/background-removal/src/fragment.ts +18 -0
- package/packages/background-removal/src/index.ts +6 -0
- package/packages/background-removal/src/manifest.ts +45 -0
- package/packages/background-removal/src/program.ts +19 -0
- package/packages/background-removal/src/surface.ts +27 -0
- package/packages/background-removal/src/types.ts +5 -0
- package/packages/browser-capture/README.md +82 -0
- package/packages/browser-capture/package.json +15 -0
- package/packages/browser-capture/src/browser.ts +19 -0
- package/packages/browser-capture/src/index.ts +198 -0
- package/packages/build-result/package.json +13 -0
- package/packages/build-result/src/decode.ts +286 -0
- package/packages/build-result/src/index.ts +25 -0
- package/packages/build-result/src/store.ts +505 -0
- package/packages/build-result/src/types.ts +354 -0
- package/packages/build-result/src/writer.ts +184 -0
- package/packages/build-result-fs/README.md +29 -0
- package/packages/build-result-fs/package.json +17 -0
- package/packages/build-result-fs/src/activation.ts +62 -0
- package/packages/build-result-fs/src/index.ts +1 -0
- package/packages/build-result-kit/README.md +10 -0
- package/packages/build-result-kit/package.json +15 -0
- package/packages/build-result-kit/src/index.ts +144 -0
- package/packages/build-result-s3/README.md +49 -0
- package/packages/build-result-s3/package.json +19 -0
- package/packages/build-result-s3/src/activation.ts +71 -0
- package/packages/build-result-s3/src/client.ts +254 -0
- package/packages/build-result-s3/src/index.ts +4 -0
- package/packages/build-result-s3/src/repository.ts +361 -0
- package/packages/caption/README.md +77 -0
- package/packages/caption/package.json +28 -0
- package/packages/caption/src/activation.ts +17 -0
- package/packages/caption/src/component.ts +38 -0
- package/packages/caption/src/display.ts +111 -0
- package/packages/caption/src/error.ts +9 -0
- package/packages/caption/src/fragment.ts +26 -0
- package/packages/caption/src/index.ts +33 -0
- package/packages/caption/src/manifest.ts +97 -0
- package/packages/caption/src/style.ts +170 -0
- package/packages/caption/src/surface.ts +96 -0
- package/packages/caption/src/temporalize.ts +124 -0
- package/packages/caption/src/types.ts +71 -0
- package/packages/caption-fine/README.md +108 -0
- package/packages/caption-fine/package.json +30 -0
- package/packages/caption-fine/preview/Track.png +0 -0
- package/packages/caption-fine/src/activation.ts +32 -0
- package/packages/caption-fine/src/component.ts +58 -0
- package/packages/caption-fine/src/fragment.ts +112 -0
- package/packages/caption-fine/src/index.ts +19 -0
- package/packages/caption-fine/src/manifest.ts +383 -0
- package/packages/caption-fine/src/recipe.ts +120 -0
- package/packages/caption-fine/src/render.ts +1133 -0
- package/packages/caption-fine/src/schedule.ts +126 -0
- package/packages/caption-fine/src/spacing.ts +69 -0
- package/packages/caption-fine/src/style.ts +370 -0
- package/packages/caption-fine/src/surface.ts +152 -0
- package/packages/caption-fine/src/types.ts +191 -0
- package/packages/caption-fine-studio/README.md +3 -0
- package/packages/caption-fine-studio/package.json +15 -0
- package/packages/caption-fine-studio/src/activation.ts +7 -0
- package/packages/caption-fine-studio/src/index.ts +163 -0
- package/packages/cli/README.md +51 -0
- package/packages/cli/package.json +33 -0
- package/packages/cli/src/arguments.ts +605 -0
- package/packages/cli/src/build-planning.ts +460 -0
- package/packages/cli/src/command.ts +202 -0
- package/packages/cli/src/commands/environment.ts +391 -0
- package/packages/cli/src/commands/execution.ts +321 -0
- package/packages/cli/src/commands/results.ts +187 -0
- package/packages/cli/src/commands/types.ts +9 -0
- package/packages/cli/src/distribution.ts +74 -0
- package/packages/cli/src/index.ts +30 -0
- package/packages/cli/src/machine-view.ts +28 -0
- package/packages/cli/src/main.ts +654 -0
- package/packages/cli/src/oauth.ts +228 -0
- package/packages/cli/src/observation.ts +203 -0
- package/packages/cli/src/output.ts +1007 -0
- package/packages/cli/src/paths.ts +7 -0
- package/packages/cli/src/project-context.ts +38 -0
- package/packages/cli/src/reachability.ts +91 -0
- package/packages/cli/src/result-export.ts +148 -0
- package/packages/cli/src/result-query.ts +46 -0
- package/packages/cli/src/run-file.ts +186 -0
- package/packages/cli/src/runtime-port.ts +13 -0
- package/packages/cli/src/runtime-selection.ts +94 -0
- package/packages/cli/src/runtime-view.ts +15 -0
- package/packages/cli/src/source-discovery.ts +150 -0
- package/packages/cli/src/source-packages.ts +91 -0
- package/packages/cli/src/view.ts +228 -0
- package/packages/comment-sticker/README.md +41 -0
- package/packages/comment-sticker/package.json +29 -0
- package/packages/comment-sticker/preview/Track.png +0 -0
- package/packages/comment-sticker/src/activation.ts +32 -0
- package/packages/comment-sticker/src/author.ts +148 -0
- package/packages/comment-sticker/src/component.ts +97 -0
- package/packages/comment-sticker/src/fragment.ts +124 -0
- package/packages/comment-sticker/src/index.ts +8 -0
- package/packages/comment-sticker/src/manifest.ts +373 -0
- package/packages/comment-sticker/src/program.ts +610 -0
- package/packages/comment-sticker/src/surface.ts +172 -0
- package/packages/comment-sticker/src/types.ts +109 -0
- package/packages/comment-sticker-studio/README.md +3 -0
- package/packages/comment-sticker-studio/package.json +14 -0
- package/packages/comment-sticker-studio/src/activation.ts +7 -0
- package/packages/comment-sticker-studio/src/index.ts +132 -0
- package/packages/compiler-markup-node/README.md +12 -0
- package/packages/compiler-markup-node/package.json +22 -0
- package/packages/compiler-markup-node/src/index.ts +59 -0
- package/packages/compiler-node/README.md +60 -0
- package/packages/compiler-node/package.json +25 -0
- package/packages/compiler-node/src/compiler.ts +207 -0
- package/packages/compiler-node/src/error.ts +11 -0
- package/packages/compiler-node/src/graph-query.ts +51 -0
- package/packages/compiler-node/src/index.ts +27 -0
- package/packages/compiler-node/src/modules.ts +107 -0
- package/packages/compiler-node/src/run.ts +355 -0
- package/packages/component-kit/README.md +97 -0
- package/packages/component-kit/package.json +13 -0
- package/packages/component-kit/src/index.ts +155 -0
- package/packages/composition/README.md +53 -0
- package/packages/composition/package.json +29 -0
- package/packages/composition/src/activation.ts +3 -0
- package/packages/composition/src/component.ts +10 -0
- package/packages/composition/src/index.ts +18 -0
- package/packages/composition/src/manifest.ts +13 -0
- package/packages/composition/src/schema.ts +194 -0
- package/packages/composition/src/track.ts +1403 -0
- package/packages/core/README.md +22 -0
- package/packages/core/package.json +13 -0
- package/packages/core/src/error.ts +12 -0
- package/packages/core/src/graph.ts +341 -0
- package/packages/core/src/index.ts +32 -0
- package/packages/core/src/link.ts +240 -0
- package/packages/core/src/machine.ts +60 -0
- package/packages/core/src/plan.ts +504 -0
- package/packages/core/src/reducer.ts +428 -0
- package/packages/core/src/reference.ts +25 -0
- package/packages/core/src/slice.ts +108 -0
- package/packages/credential-store-env/README.md +11 -0
- package/packages/credential-store-env/package.json +17 -0
- package/packages/credential-store-env/src/activation.ts +25 -0
- package/packages/credential-store-env/src/index.ts +23 -0
- package/packages/credential-store-os/README.md +13 -0
- package/packages/credential-store-os/package.json +22 -0
- package/packages/credential-store-os/runtime/windows-credential.ps1 +59 -0
- package/packages/credential-store-os/src/activation.ts +32 -0
- package/packages/credential-store-os/src/index.ts +2 -0
- package/packages/credential-store-os/src/store.ts +196 -0
- package/packages/deck-track/README.md +52 -0
- package/packages/deck-track/package.json +35 -0
- package/packages/deck-track/preview/DepthStack.png +0 -0
- package/packages/deck-track/src/activation.ts +32 -0
- package/packages/deck-track/src/author.ts +217 -0
- package/packages/deck-track/src/component.ts +86 -0
- package/packages/deck-track/src/fragment.ts +148 -0
- package/packages/deck-track/src/index.ts +16 -0
- package/packages/deck-track/src/lower.ts +370 -0
- package/packages/deck-track/src/manifest.ts +438 -0
- package/packages/deck-track/src/program.ts +367 -0
- package/packages/deck-track/src/surface.ts +286 -0
- package/packages/deck-track/src/types.ts +119 -0
- package/packages/deck-track-studio/README.md +3 -0
- package/packages/deck-track-studio/package.json +14 -0
- package/packages/deck-track-studio/src/activation.ts +7 -0
- package/packages/deck-track-studio/src/index.ts +134 -0
- package/packages/driver-node/README.md +23 -0
- package/packages/driver-node/package.json +18 -0
- package/packages/driver-node/src/driver.ts +616 -0
- package/packages/driver-node/src/index.ts +10 -0
- package/packages/driver-node/src/planning.ts +67 -0
- package/packages/driver-node/src/registry.ts +234 -0
- package/packages/driver-node/src/resources.ts +36 -0
- package/packages/driver-node/src/types.ts +69 -0
- package/packages/elaborator/README.md +74 -0
- package/packages/elaborator/package.json +16 -0
- package/packages/elaborator/src/author.ts +308 -0
- package/packages/elaborator/src/fragment.ts +505 -0
- package/packages/elaborator/src/frontend-facet.ts +30 -0
- package/packages/elaborator/src/index.ts +78 -0
- package/packages/elaborator/src/source.ts +608 -0
- package/packages/endpoint-kit/README.md +111 -0
- package/packages/endpoint-kit/package.json +14 -0
- package/packages/endpoint-kit/src/index.ts +488 -0
- package/packages/estimate/README.md +32 -0
- package/packages/estimate/package.json +20 -0
- package/packages/estimate/src/activation.ts +11 -0
- package/packages/estimate/src/index.ts +16 -0
- package/packages/estimate/src/manifest.ts +34 -0
- package/packages/estimate/src/policy.ts +70 -0
- package/packages/estimate/src/program.ts +196 -0
- package/packages/estimate/src/types.ts +15 -0
- package/packages/film/README.md +49 -0
- package/packages/film/package.json +34 -0
- package/packages/film/src/activation.ts +16 -0
- package/packages/film/src/component.ts +61 -0
- package/packages/film/src/fragment.ts +83 -0
- package/packages/film/src/index.ts +11 -0
- package/packages/film/src/manifest.ts +152 -0
- package/packages/film/src/program.ts +110 -0
- package/packages/film/src/recipe.ts +24 -0
- package/packages/film/src/surface.ts +146 -0
- package/packages/film/src/types.ts +26 -0
- package/packages/film-studio/package.json +16 -0
- package/packages/film-studio/src/activation.ts +8 -0
- package/packages/film-studio/src/index.ts +17 -0
- package/packages/fonts-open/README.md +77 -0
- package/packages/fonts-open/package.json +130 -0
- package/packages/fonts-open/src/activation.ts +30 -0
- package/packages/fonts-open/src/catalog.ts +317 -0
- package/packages/fonts-open/src/index.ts +13 -0
- package/packages/fonts-open/src/manifest.ts +83 -0
- package/packages/fonts-open/src/surface.ts +239 -0
- package/packages/generation/README.md +19 -0
- package/packages/generation/package.json +23 -0
- package/packages/generation/src/activation.ts +9 -0
- package/packages/generation/src/component.ts +77 -0
- package/packages/generation/src/identity.ts +80 -0
- package/packages/generation/src/index.ts +79 -0
- package/packages/generation/src/manifest.ts +65 -0
- package/packages/generation/src/mapping.ts +231 -0
- package/packages/generation/src/ports.ts +222 -0
- package/packages/generation/src/request.ts +427 -0
- package/packages/generation/src/schema.ts +62 -0
- package/packages/generation/src/types.ts +17 -0
- package/packages/gpt-image/README.md +54 -0
- package/packages/gpt-image/package.json +34 -0
- package/packages/gpt-image/src/activation.ts +39 -0
- package/packages/gpt-image/src/fragment.ts +101 -0
- package/packages/gpt-image/src/index.ts +203 -0
- package/packages/gpt-image/src/surface.ts +194 -0
- package/packages/gpt-image-kits/README.md +44 -0
- package/packages/gpt-image-kits/kits/phone-ugc-v1.svs +34 -0
- package/packages/gpt-image-kits/package.json +15 -0
- package/packages/grok-imagine/README.md +21 -0
- package/packages/grok-imagine/package.json +21 -0
- package/packages/grok-imagine/src/activation.ts +29 -0
- package/packages/grok-imagine/src/index.ts +174 -0
- package/packages/grok-imagine/src/surface.ts +149 -0
- package/packages/host/README.md +5 -0
- package/packages/host/package.json +10 -0
- package/packages/host/src/facet.ts +6 -0
- package/packages/host/src/frontend.ts +56 -0
- package/packages/host/src/index.ts +9 -0
- package/packages/hyperframes/README.md +69 -0
- package/packages/hyperframes/package.json +36 -0
- package/packages/hyperframes/src/activation.ts +8 -0
- package/packages/hyperframes/src/browser-program.ts +79 -0
- package/packages/hyperframes/src/component.ts +32 -0
- package/packages/hyperframes/src/document.ts +827 -0
- package/packages/hyperframes/src/fragment.ts +32 -0
- package/packages/hyperframes/src/index.ts +13 -0
- package/packages/hyperframes/src/manifest.ts +67 -0
- package/packages/hyperframes/src/project.ts +118 -0
- package/packages/hyperframes/src/text.ts +1179 -0
- package/packages/hyperframes/src/types.ts +38 -0
- package/packages/image-compose/README.md +25 -0
- package/packages/image-compose/package.json +18 -0
- package/packages/image-compose/src/activation.ts +21 -0
- package/packages/image-compose/src/component.ts +100 -0
- package/packages/image-compose/src/fragment.ts +54 -0
- package/packages/image-compose/src/index.ts +10 -0
- package/packages/image-compose/src/manifest.ts +119 -0
- package/packages/image-compose/src/program.ts +75 -0
- package/packages/image-compose/src/surface.ts +83 -0
- package/packages/image-compose/src/types.ts +22 -0
- package/packages/image-transform/README.md +29 -0
- package/packages/image-transform/package.json +21 -0
- package/packages/image-transform/src/activation.ts +33 -0
- package/packages/image-transform/src/component.ts +67 -0
- package/packages/image-transform/src/fragment.ts +25 -0
- package/packages/image-transform/src/index.ts +13 -0
- package/packages/image-transform/src/manifest.ts +209 -0
- package/packages/image-transform/src/program.ts +23 -0
- package/packages/image-transform/src/surface.ts +216 -0
- package/packages/image-transform/src/types.ts +20 -0
- package/packages/interview-emoji-reveal/README.md +39 -0
- package/packages/interview-emoji-reveal/package.json +23 -0
- package/packages/interview-emoji-reveal/preview/Track.png +0 -0
- package/packages/interview-emoji-reveal/src/activation.ts +14 -0
- package/packages/interview-emoji-reveal/src/component.ts +45 -0
- package/packages/interview-emoji-reveal/src/fragment.ts +62 -0
- package/packages/interview-emoji-reveal/src/index.ts +16 -0
- package/packages/interview-emoji-reveal/src/manifest.ts +174 -0
- package/packages/interview-emoji-reveal/src/program.ts +242 -0
- package/packages/interview-emoji-reveal/src/style.ts +47 -0
- package/packages/interview-emoji-reveal/src/surface.ts +110 -0
- package/packages/interview-emoji-reveal/src/types.ts +52 -0
- package/packages/markup/README.md +81 -0
- package/packages/markup/package.json +16 -0
- package/packages/markup/src/element.ts +39 -0
- package/packages/markup/src/error.ts +20 -0
- package/packages/markup/src/frontend.ts +518 -0
- package/packages/markup/src/host-facet.ts +115 -0
- package/packages/markup/src/index.ts +25 -0
- package/packages/markup/src/registry.ts +83 -0
- package/packages/markup/src/syntax.ts +271 -0
- package/packages/markup/src/types.ts +246 -0
- package/packages/media/README.md +37 -0
- package/packages/media/package.json +19 -0
- package/packages/media/src/activation.ts +38 -0
- package/packages/media/src/component.ts +16 -0
- package/packages/media/src/frame-range.ts +33 -0
- package/packages/media/src/identity.ts +227 -0
- package/packages/media/src/index.ts +15 -0
- package/packages/media/src/manifest.ts +122 -0
- package/packages/media/src/render.ts +140 -0
- package/packages/media/src/schema.ts +69 -0
- package/packages/media/src/surface.ts +150 -0
- package/packages/media/src/types.ts +121 -0
- package/packages/media-execution/README.md +28 -0
- package/packages/media-execution/package.json +17 -0
- package/packages/media-execution/src/execute.ts +1322 -0
- package/packages/media-execution/src/index.ts +16 -0
- package/packages/media-execution/src/probe.ts +315 -0
- package/packages/media-execution/src/surface.ts +286 -0
- package/packages/media-execution/src/toolchain.ts +81 -0
- package/packages/media-execution/src/webp.ts +200 -0
- package/packages/media-pipeline/README.md +113 -0
- package/packages/media-pipeline/package.json +27 -0
- package/packages/media-pipeline/src/activation.ts +47 -0
- package/packages/media-pipeline/src/audio-plan.ts +113 -0
- package/packages/media-pipeline/src/component.ts +373 -0
- package/packages/media-pipeline/src/fragment.ts +167 -0
- package/packages/media-pipeline/src/index.ts +46 -0
- package/packages/media-pipeline/src/manifest.ts +653 -0
- package/packages/media-pipeline/src/operations.ts +248 -0
- package/packages/media-pipeline/src/selection.ts +110 -0
- package/packages/media-pipeline/src/surface.ts +389 -0
- package/packages/media-pipeline/src/types.ts +175 -0
- package/packages/media-track/README.md +197 -0
- package/packages/media-track/package.json +29 -0
- package/packages/media-track/preview/Track.png +0 -0
- package/packages/media-track/preview/build.svrun +5 -0
- package/packages/media-track/preview/flush.png +0 -0
- package/packages/media-track/preview/preview.svml +47 -0
- package/packages/media-track/preview/recipes.svs +23 -0
- package/packages/media-track/preview/rounded.png +0 -0
- package/packages/media-track/src/activation.ts +20 -0
- package/packages/media-track/src/author.ts +317 -0
- package/packages/media-track/src/component.ts +162 -0
- package/packages/media-track/src/fragment.ts +57 -0
- package/packages/media-track/src/index.ts +65 -0
- package/packages/media-track/src/layers.ts +308 -0
- package/packages/media-track/src/lower.ts +335 -0
- package/packages/media-track/src/manifest.ts +624 -0
- package/packages/media-track/src/motion.ts +387 -0
- package/packages/media-track/src/performance.ts +37 -0
- package/packages/media-track/src/presentation.ts +35 -0
- package/packages/media-track/src/program.ts +565 -0
- package/packages/media-track/src/sampling.ts +118 -0
- package/packages/media-track/src/sequence.ts +403 -0
- package/packages/media-track/src/sounds.ts +71 -0
- package/packages/media-track/src/surface.ts +668 -0
- package/packages/media-track/src/types.ts +311 -0
- package/packages/media-track-studio/README.md +3 -0
- package/packages/media-track-studio/package.json +15 -0
- package/packages/media-track-studio/src/activation.ts +7 -0
- package/packages/media-track-studio/src/index.ts +235 -0
- package/packages/mimo-speech/README.md +33 -0
- package/packages/mimo-speech/package.json +21 -0
- package/packages/mimo-speech/src/activation.ts +32 -0
- package/packages/mimo-speech/src/fragment.ts +11 -0
- package/packages/mimo-speech/src/index.ts +160 -0
- package/packages/mimo-speech/src/surface.ts +193 -0
- package/packages/minimax-h3/README.md +21 -0
- package/packages/minimax-h3/package.json +21 -0
- package/packages/minimax-h3/src/activation.ts +14 -0
- package/packages/minimax-h3/src/index.ts +188 -0
- package/packages/minimax-h3/src/surface.ts +87 -0
- package/packages/model-kit/README.md +52 -0
- package/packages/model-kit/package.json +19 -0
- package/packages/model-kit/src/index.ts +728 -0
- package/packages/nano-banana/README.md +23 -0
- package/packages/nano-banana/package.json +21 -0
- package/packages/nano-banana/src/activation.ts +14 -0
- package/packages/nano-banana/src/index.ts +172 -0
- package/packages/nano-banana/src/surface.ts +120 -0
- package/packages/narrative/README.md +7 -0
- package/packages/narrative/package.json +17 -0
- package/packages/narrative/src/activation.ts +7 -0
- package/packages/narrative/src/component.ts +33 -0
- package/packages/narrative/src/identity.ts +131 -0
- package/packages/narrative/src/index.ts +11 -0
- package/packages/narrative/src/manifest.ts +30 -0
- package/packages/narrative/src/schema.ts +89 -0
- package/packages/narrative/src/types.ts +141 -0
- package/packages/package-loader-node/README.md +23 -0
- package/packages/package-loader-node/package.json +15 -0
- package/packages/package-loader-node/src/contribution.ts +97 -0
- package/packages/package-loader-node/src/distribution-resolution.ts +146 -0
- package/packages/package-loader-node/src/index.ts +27 -0
- package/packages/package-loader-node/src/loader.ts +301 -0
- package/packages/package-loader-node/src/location.ts +279 -0
- package/packages/package-loader-node/src/types.ts +60 -0
- package/packages/program-space/README.md +20 -0
- package/packages/program-space/package.json +17 -0
- package/packages/program-space/src/activation.ts +19 -0
- package/packages/program-space/src/index.ts +107 -0
- package/packages/program-space/src/surface.ts +55 -0
- package/packages/protocol/README.md +11 -0
- package/packages/protocol/package.json +10 -0
- package/packages/protocol/src/build-id.ts +52 -0
- package/packages/protocol/src/build.ts +265 -0
- package/packages/protocol/src/canonical.ts +37 -0
- package/packages/protocol/src/error.ts +11 -0
- package/packages/protocol/src/identity.ts +59 -0
- package/packages/protocol/src/index.ts +9 -0
- package/packages/protocol/src/module.ts +67 -0
- package/packages/protocol/src/value.ts +88 -0
- package/packages/provider-hyperframes-aws-lambda/README.md +30 -0
- package/packages/provider-hyperframes-aws-lambda/package.json +30 -0
- package/packages/provider-hyperframes-aws-lambda/src/activation.ts +97 -0
- package/packages/provider-hyperframes-aws-lambda/src/client.ts +114 -0
- package/packages/provider-hyperframes-aws-lambda/src/index.ts +21 -0
- package/packages/provider-hyperframes-aws-lambda/src/provider.ts +522 -0
- package/packages/provider-hyperframes-local/README.md +129 -0
- package/packages/provider-hyperframes-local/package.json +34 -0
- package/packages/provider-hyperframes-local/src/activation.ts +107 -0
- package/packages/provider-hyperframes-local/src/capture-process.ts +110 -0
- package/packages/provider-hyperframes-local/src/capture-worker.ts +17 -0
- package/packages/provider-hyperframes-local/src/capture.ts +184 -0
- package/packages/provider-hyperframes-local/src/index.ts +10 -0
- package/packages/provider-hyperframes-local/src/options.ts +19 -0
- package/packages/provider-hyperframes-local/src/output.ts +73 -0
- package/packages/provider-hyperframes-local/src/process.ts +83 -0
- package/packages/provider-hyperframes-local/src/program.ts +58 -0
- package/packages/provider-hyperframes-local/src/provider.ts +62 -0
- package/packages/provider-hyperframes-local/src/render.ts +114 -0
- package/packages/provider-hyperframes-local/src/sampling.ts +89 -0
- package/packages/provider-hypihub/README.md +143 -0
- package/packages/provider-hypihub/package.json +26 -0
- package/packages/provider-hypihub/src/activation.ts +91 -0
- package/packages/provider-hypihub/src/index.ts +2 -0
- package/packages/provider-hypihub/src/mapping.ts +145 -0
- package/packages/provider-hypihub/src/oauth.ts +113 -0
- package/packages/provider-hypihub/src/provider.ts +566 -0
- package/packages/provider-hypihub/src/routes.ts +134 -0
- package/packages/provider-hypihub/src/upload.ts +402 -0
- package/packages/provider-image-opencv-local/README.md +16 -0
- package/packages/provider-image-opencv-local/package.json +27 -0
- package/packages/provider-image-opencv-local/runtime/raster_execute.py +299 -0
- package/packages/provider-image-opencv-local/src/activation.ts +61 -0
- package/packages/provider-image-opencv-local/src/deployment.ts +68 -0
- package/packages/provider-image-opencv-local/src/index.ts +2 -0
- package/packages/provider-image-opencv-local/src/program.ts +60 -0
- package/packages/provider-image-opencv-local/src/provider.ts +155 -0
- package/packages/provider-kie/README.md +134 -0
- package/packages/provider-kie/package.json +43 -0
- package/packages/provider-kie/src/activation.ts +71 -0
- package/packages/provider-kie/src/index.ts +10 -0
- package/packages/provider-kie/src/mapping.ts +182 -0
- package/packages/provider-kie/src/provider.ts +657 -0
- package/packages/provider-kie/src/routes.ts +150 -0
- package/packages/provider-media-local/README.md +58 -0
- package/packages/provider-media-local/package.json +28 -0
- package/packages/provider-media-local/src/activation.ts +65 -0
- package/packages/provider-media-local/src/index.ts +2 -0
- package/packages/provider-media-local/src/program.ts +19 -0
- package/packages/provider-media-local/src/provider.ts +150 -0
- package/packages/provider-whisperx-local/README.md +44 -0
- package/packages/provider-whisperx-local/package.json +27 -0
- package/packages/provider-whisperx-local/src/activation.ts +93 -0
- package/packages/provider-whisperx-local/src/index.ts +5 -0
- package/packages/provider-whisperx-local/src/program.ts +157 -0
- package/packages/provider-whisperx-local/src/provider.ts +191 -0
- package/packages/provider-xiaomi-mimo/README.md +32 -0
- package/packages/provider-xiaomi-mimo/package.json +24 -0
- package/packages/provider-xiaomi-mimo/src/activation.ts +57 -0
- package/packages/provider-xiaomi-mimo/src/index.ts +5 -0
- package/packages/provider-xiaomi-mimo/src/provider.ts +214 -0
- package/packages/ranking/README.md +67 -0
- package/packages/ranking/package.json +33 -0
- package/packages/ranking/preview/Column.svg +29 -0
- package/packages/ranking/preview/TierBoard.png +0 -0
- package/packages/ranking/preview/TierBoard.svg +36 -0
- package/packages/ranking/preview/TopThree.png +0 -0
- package/packages/ranking/src/activation.ts +38 -0
- package/packages/ranking/src/component.ts +271 -0
- package/packages/ranking/src/fragment.ts +206 -0
- package/packages/ranking/src/index.ts +26 -0
- package/packages/ranking/src/manifest.ts +807 -0
- package/packages/ranking/src/render.ts +698 -0
- package/packages/ranking/src/schedule.ts +895 -0
- package/packages/ranking/src/style.ts +272 -0
- package/packages/ranking/src/surface.ts +322 -0
- package/packages/ranking/src/tier.ts +176 -0
- package/packages/ranking/src/types.ts +277 -0
- package/packages/ranking-studio/README.md +3 -0
- package/packages/ranking-studio/package.json +14 -0
- package/packages/ranking-studio/src/activation.ts +7 -0
- package/packages/ranking-studio/src/index.ts +255 -0
- package/packages/raster/README.md +7 -0
- package/packages/raster/package.json +14 -0
- package/packages/raster/src/activation.ts +7 -0
- package/packages/raster/src/index.ts +7 -0
- package/packages/raster/src/manifest.ts +14 -0
- package/packages/raster/src/program.ts +128 -0
- package/packages/raster/src/schema.ts +53 -0
- package/packages/raster/src/types.ts +61 -0
- package/packages/render-hyperframes/README.md +58 -0
- package/packages/render-hyperframes/package.json +33 -0
- package/packages/render-hyperframes/src/activation.ts +18 -0
- package/packages/render-hyperframes/src/component.ts +37 -0
- package/packages/render-hyperframes/src/fragment.ts +73 -0
- package/packages/render-hyperframes/src/index.ts +6 -0
- package/packages/render-hyperframes/src/manifest.ts +100 -0
- package/packages/render-hyperframes/src/product.ts +29 -0
- package/packages/render-hyperframes/src/surface.ts +65 -0
- package/packages/resource-store-fs/README.md +13 -0
- package/packages/resource-store-fs/package.json +14 -0
- package/packages/resource-store-fs/src/index.ts +2 -0
- package/packages/resource-store-fs/src/store.ts +154 -0
- package/packages/resource-store-s3/README.md +15 -0
- package/packages/resource-store-s3/package.json +15 -0
- package/packages/resource-store-s3/src/client.ts +138 -0
- package/packages/resource-store-s3/src/index.ts +10 -0
- package/packages/resource-store-s3/src/store.ts +258 -0
- package/packages/run/README.md +23 -0
- package/packages/run/package.json +16 -0
- package/packages/run/src/candidate.ts +33 -0
- package/packages/run/src/facet.ts +59 -0
- package/packages/run/src/frontend-facet.ts +23 -0
- package/packages/run/src/frontend.ts +62 -0
- package/packages/run/src/graph.ts +65 -0
- package/packages/run/src/index.ts +33 -0
- package/packages/run/src/registry.ts +24 -0
- package/packages/run/src/resolve.ts +213 -0
- package/packages/run/src/types.ts +147 -0
- package/packages/run-markup/README.md +60 -0
- package/packages/run-markup/package.json +22 -0
- package/packages/run-markup/src/activation.ts +10 -0
- package/packages/run-markup/src/frontend.ts +16 -0
- package/packages/run-markup/src/index.ts +5 -0
- package/packages/run-markup/src/syntax.ts +304 -0
- package/packages/runtime/README.md +39 -0
- package/packages/runtime/package.json +15 -0
- package/packages/runtime/src/capacity.ts +51 -0
- package/packages/runtime/src/catalog.ts +30 -0
- package/packages/runtime/src/credentials.ts +124 -0
- package/packages/runtime/src/environment.ts +6 -0
- package/packages/runtime/src/execution.ts +125 -0
- package/packages/runtime/src/index.ts +24 -0
- package/packages/runtime/src/operations.ts +99 -0
- package/packages/runtime/src/scheduler.ts +281 -0
- package/packages/runtime/src/submission.ts +24 -0
- package/packages/runtime/src/types.ts +157 -0
- package/packages/runtime-host-node/README.md +25 -0
- package/packages/runtime-host-node/package.json +19 -0
- package/packages/runtime-host-node/src/index.ts +421 -0
- package/packages/runtime-host-node/src/packages.ts +158 -0
- package/packages/runtime-kit/README.md +42 -0
- package/packages/runtime-kit/package.json +16 -0
- package/packages/runtime-kit/src/index.ts +307 -0
- package/packages/runtime-kit/src/request-deadline.ts +17 -0
- package/packages/runtime-local/README.md +119 -0
- package/packages/runtime-local/package.json +33 -0
- package/packages/runtime-local/src/actions.ts +20 -0
- package/packages/runtime-local/src/config.ts +1229 -0
- package/packages/runtime-local/src/control.ts +120 -0
- package/packages/runtime-local/src/credentials.ts +61 -0
- package/packages/runtime-local/src/host.ts +174 -0
- package/packages/runtime-local/src/index.ts +36 -0
- package/packages/runtime-local/src/process-control.ts +59 -0
- package/packages/runtime-local/src/process-logs.ts +33 -0
- package/packages/runtime-local/src/programs.ts +441 -0
- package/packages/runtime-local/src/result-writer.ts +246 -0
- package/packages/runtime-local/src/runtime.ts +342 -0
- package/packages/runtime-local/src/types.ts +100 -0
- package/packages/runtime-local/src/worker-process.ts +343 -0
- package/packages/runtime-local/src/worker.ts +449 -0
- package/packages/screen-overlay/README.md +17 -0
- package/packages/screen-overlay/package.json +31 -0
- package/packages/screen-overlay/preview/Track.png +0 -0
- package/packages/screen-overlay/src/activation.ts +15 -0
- package/packages/screen-overlay/src/component.ts +20 -0
- package/packages/screen-overlay/src/fragment.ts +40 -0
- package/packages/screen-overlay/src/index.ts +7 -0
- package/packages/screen-overlay/src/manifest.ts +305 -0
- package/packages/screen-overlay/src/program.ts +321 -0
- package/packages/screen-overlay/src/surface.ts +90 -0
- package/packages/screen-overlay/src/types.ts +69 -0
- package/packages/screen-overlay-studio/README.md +3 -0
- package/packages/screen-overlay-studio/package.json +14 -0
- package/packages/screen-overlay-studio/src/activation.ts +7 -0
- package/packages/screen-overlay-studio/src/index.ts +80 -0
- package/packages/script/README.md +137 -0
- package/packages/script/package.json +22 -0
- package/packages/script/src/activation.ts +20 -0
- package/packages/script/src/edit.ts +174 -0
- package/packages/script/src/error.ts +13 -0
- package/packages/script/src/format.ts +83 -0
- package/packages/script/src/index.ts +20 -0
- package/packages/script/src/lexical.ts +133 -0
- package/packages/script/src/manifest.ts +95 -0
- package/packages/script/src/narrative.ts +232 -0
- package/packages/script/src/parser.ts +850 -0
- package/packages/script/src/surface.ts +132 -0
- package/packages/script/src/types.ts +125 -0
- package/packages/script-studio/package.json +13 -0
- package/packages/script-studio/src/activation.ts +8 -0
- package/packages/script-studio/src/index.ts +61 -0
- package/packages/seedance/README.md +19 -0
- package/packages/seedance/package.json +22 -0
- package/packages/seedance/src/activation.ts +38 -0
- package/packages/seedance/src/fragment.ts +46 -0
- package/packages/seedance/src/index.ts +329 -0
- package/packages/seedance/src/surface.ts +355 -0
- package/packages/seedance-kits/README.md +153 -0
- package/packages/seedance-kits/kits/broll-v1.svs +52 -0
- package/packages/seedance-kits/kits/call-v1.svs +55 -0
- package/packages/seedance-kits/kits/camera-reference-v1.svs +9 -0
- package/packages/seedance-kits/kits/motion-reference-v1.svs +9 -0
- package/packages/seedance-kits/kits/podcast-v1.svs +55 -0
- package/packages/seedance-kits/kits/speaker-v1.svs +136 -0
- package/packages/seedance-kits/kits/street-interview-v1.svs +49 -0
- package/packages/seedance-kits/package.json +24 -0
- package/packages/seedream/README.md +21 -0
- package/packages/seedream/package.json +21 -0
- package/packages/seedream/src/activation.ts +12 -0
- package/packages/seedream/src/index.ts +162 -0
- package/packages/seedream/src/surface.ts +84 -0
- package/packages/semantic-take-adjust/package.json +21 -0
- package/packages/semantic-take-adjust/src/activation.ts +22 -0
- package/packages/semantic-take-adjust/src/component.ts +31 -0
- package/packages/semantic-take-adjust/src/fragment.ts +24 -0
- package/packages/semantic-take-adjust/src/index.ts +12 -0
- package/packages/semantic-take-adjust/src/manifest.ts +57 -0
- package/packages/semantic-take-adjust/src/program.ts +49 -0
- package/packages/semantic-take-adjust/src/surface.ts +96 -0
- package/packages/semantic-take-adjust/src/types.ts +9 -0
- package/packages/semantic-track/README.md +21 -0
- package/packages/semantic-track/package.json +22 -0
- package/packages/semantic-track/src/activation.ts +8 -0
- package/packages/semantic-track/src/component.ts +24 -0
- package/packages/semantic-track/src/identity.ts +58 -0
- package/packages/semantic-track/src/index.ts +6 -0
- package/packages/semantic-track/src/location.ts +81 -0
- package/packages/semantic-track/src/manifest.ts +38 -0
- package/packages/semantic-track/src/projection.ts +71 -0
- package/packages/semantic-track/src/types.ts +26 -0
- package/packages/source/README.md +13 -0
- package/packages/source/package.json +13 -0
- package/packages/source/src/header.ts +80 -0
- package/packages/source/src/index.ts +17 -0
- package/packages/source/src/unit.ts +38 -0
- package/packages/spatial/README.md +77 -0
- package/packages/spatial/package.json +24 -0
- package/packages/spatial/src/activation.ts +43 -0
- package/packages/spatial/src/author.ts +59 -0
- package/packages/spatial/src/component.ts +45 -0
- package/packages/spatial/src/fragment.ts +36 -0
- package/packages/spatial/src/geometry.ts +238 -0
- package/packages/spatial/src/index.ts +37 -0
- package/packages/spatial/src/manifest.ts +341 -0
- package/packages/spatial/src/region-timeline.ts +69 -0
- package/packages/spatial/src/schema.ts +47 -0
- package/packages/spatial/src/surface.ts +319 -0
- package/packages/spatial/src/types.ts +101 -0
- package/packages/speech/README.md +16 -0
- package/packages/speech/package.json +19 -0
- package/packages/speech/src/activation.ts +7 -0
- package/packages/speech/src/component.ts +43 -0
- package/packages/speech/src/identity.ts +73 -0
- package/packages/speech/src/index.ts +6 -0
- package/packages/speech/src/manifest.ts +30 -0
- package/packages/speech/src/materialize.ts +103 -0
- package/packages/speech/src/schema.ts +42 -0
- package/packages/speech/src/types.ts +54 -0
- package/packages/speech-alignment/README.md +29 -0
- package/packages/speech-alignment/package.json +25 -0
- package/packages/speech-alignment/src/activation.ts +8 -0
- package/packages/speech-alignment/src/align.ts +235 -0
- package/packages/speech-alignment/src/component.ts +37 -0
- package/packages/speech-alignment/src/error.ts +9 -0
- package/packages/speech-alignment/src/index.ts +7 -0
- package/packages/speech-alignment/src/local.ts +64 -0
- package/packages/speech-alignment/src/locate.ts +473 -0
- package/packages/speech-alignment/src/manifest.ts +37 -0
- package/packages/speech-alignment/src/normalize.ts +79 -0
- package/packages/speech-alignment/src/types.ts +30 -0
- package/packages/speech-evidence/README.md +3 -0
- package/packages/speech-evidence/package.json +16 -0
- package/packages/speech-evidence/src/activation.ts +3 -0
- package/packages/speech-evidence/src/index.ts +102 -0
- package/packages/speech-track/README.md +51 -0
- package/packages/speech-track/package.json +28 -0
- package/packages/speech-track/preview/Track.png +0 -0
- package/packages/speech-track/src/activation.ts +19 -0
- package/packages/speech-track/src/component.ts +45 -0
- package/packages/speech-track/src/fragment.ts +74 -0
- package/packages/speech-track/src/index.ts +7 -0
- package/packages/speech-track/src/manifest.ts +73 -0
- package/packages/speech-track/src/program.ts +64 -0
- package/packages/speech-track/src/surface.ts +35 -0
- package/packages/speech-track/src/types.ts +6 -0
- package/packages/speech-track-studio/README.md +3 -0
- package/packages/speech-track-studio/package.json +14 -0
- package/packages/speech-track-studio/src/activation.ts +7 -0
- package/packages/speech-track-studio/src/index.ts +40 -0
- package/packages/store-sqlite/README.md +14 -0
- package/packages/store-sqlite/package.json +18 -0
- package/packages/store-sqlite/src/index.ts +2 -0
- package/packages/store-sqlite/src/store.ts +1101 -0
- package/packages/studio/README.md +125 -0
- package/packages/studio/index.html +10 -0
- package/packages/studio/package.json +50 -0
- package/packages/studio/src/build-library.ts +281 -0
- package/packages/studio/src/companion-assembly.ts +39 -0
- package/packages/studio/src/compile.ts +166 -0
- package/packages/studio/src/domain.ts +139 -0
- package/packages/studio/src/execute.ts +57 -0
- package/packages/studio/src/fallback-companions.ts +98 -0
- package/packages/studio/src/observe.ts +202 -0
- package/packages/studio/src/parameter-values.ts +81 -0
- package/packages/studio/src/parameters.ts +550 -0
- package/packages/studio/src/preview/render.ts +51 -0
- package/packages/studio/src/preview/runtime-shim.ts +184 -0
- package/packages/studio/src/programme.ts +276 -0
- package/packages/studio/src/run.ts +85 -0
- package/packages/studio/src/server.ts +725 -0
- package/packages/studio/src/session.ts +111 -0
- package/packages/studio/src/shared.ts +315 -0
- package/packages/studio/src/snapshot.ts +454 -0
- package/packages/studio/src/source-transaction.ts +57 -0
- package/packages/studio/src/storyboard.ts +133 -0
- package/packages/studio/src/studio-preflight.ts +198 -0
- package/packages/studio/src/studio-registry.ts +406 -0
- package/packages/studio/src/studio-trace.ts +136 -0
- package/packages/studio/src/style.css +1525 -0
- package/packages/studio/src/surface-preview.ts +35 -0
- package/packages/studio/src/temporal-graph.ts +227 -0
- package/packages/studio/src/ui/code.ts +450 -0
- package/packages/studio/src/ui/icons.ts +51 -0
- package/packages/studio/src/ui/library.ts +378 -0
- package/packages/studio/src/ui/main.ts +1001 -0
- package/packages/studio/src/ui/markers.ts +152 -0
- package/packages/studio/src/ui/material-preview.ts +176 -0
- package/packages/studio/src/ui/overlay.ts +144 -0
- package/packages/studio/src/ui/resize.ts +74 -0
- package/packages/studio/src/ui/selection.ts +178 -0
- package/packages/studio/src/ui/stage.ts +231 -0
- package/packages/studio/src/ui/syntax.ts +195 -0
- package/packages/studio/src/ui/timeline.ts +1041 -0
- package/packages/studio/src/ui/writeback.ts +10 -0
- package/packages/studio/src/ui/zoom.ts +129 -0
- package/packages/studio/start.ts +143 -0
- package/packages/studio-adapter/README.md +186 -0
- package/packages/studio-adapter/package.json +14 -0
- package/packages/studio-adapter/src/index.ts +850 -0
- package/packages/svs/README.md +37 -0
- package/packages/svs/package.json +21 -0
- package/packages/svs/src/activation.ts +11 -0
- package/packages/svs/src/format.ts +16 -0
- package/packages/svs/src/frontend.ts +33 -0
- package/packages/svs/src/index.ts +5 -0
- package/packages/svs/src/manifest.ts +17 -0
- package/packages/svs/src/parser.ts +279 -0
- package/packages/svs/src/types.ts +26 -0
- package/packages/temporal/README.md +31 -0
- package/packages/temporal/package.json +20 -0
- package/packages/temporal/src/activation.ts +9 -0
- package/packages/temporal/src/component.ts +68 -0
- package/packages/temporal/src/index.ts +110 -0
- package/packages/temporal/src/location.ts +67 -0
- package/packages/temporal/src/projection.ts +280 -0
- package/packages/temporal/src/rational.ts +80 -0
- package/packages/temporal/src/sample.ts +43 -0
- package/packages/temporal/src/schedule.ts +52 -0
- package/packages/temporal/src/types.ts +124 -0
- package/packages/temporal-markup/README.md +75 -0
- package/packages/temporal-markup/package.json +18 -0
- package/packages/temporal-markup/src/index.ts +537 -0
- package/packages/temporal-markup/src/space.ts +59 -0
- package/packages/text/README.md +59 -0
- package/packages/text/package.json +21 -0
- package/packages/text/src/activation.ts +33 -0
- package/packages/text/src/component.ts +72 -0
- package/packages/text/src/fragment.ts +57 -0
- package/packages/text/src/frontend.ts +39 -0
- package/packages/text/src/index.ts +27 -0
- package/packages/text/src/manifest.ts +170 -0
- package/packages/text/src/program.ts +418 -0
- package/packages/text/src/surface.ts +212 -0
- package/packages/text/src/svs.ts +189 -0
- package/packages/text/src/types.ts +82 -0
- package/packages/transport-aws-lambda/README.md +20 -0
- package/packages/transport-aws-lambda/package.json +14 -0
- package/packages/transport-aws-lambda/src/index.ts +131 -0
- package/packages/typography-track/README.md +50 -0
- package/packages/typography-track/package.json +34 -0
- package/packages/typography-track/preview/Mask.png +0 -0
- package/packages/typography-track/preview/Track.png +0 -0
- package/packages/typography-track/src/activation.ts +36 -0
- package/packages/typography-track/src/component.ts +93 -0
- package/packages/typography-track/src/fragment.ts +30 -0
- package/packages/typography-track/src/index.ts +11 -0
- package/packages/typography-track/src/manifest.ts +556 -0
- package/packages/typography-track/src/program.ts +622 -0
- package/packages/typography-track/src/surface.ts +842 -0
- package/packages/typography-track/src/types.ts +109 -0
- package/packages/typography-track-studio/README.md +3 -0
- package/packages/typography-track-studio/package.json +14 -0
- package/packages/typography-track-studio/src/activation.ts +7 -0
- package/packages/typography-track-studio/src/index.ts +116 -0
- package/packages/validation/README.md +11 -0
- package/packages/validation/package.json +15 -0
- package/packages/validation/src/index.ts +100 -0
- package/packages/video-cli/README.md +181 -0
- package/packages/video-cli/package.json +37 -0
- package/packages/video-cli/src/capture.ts +174 -0
- package/packages/video-cli/src/cli.ts +109 -0
- package/packages/video-cli/src/compiler.ts +32 -0
- package/packages/video-cli/src/creation.ts +449 -0
- package/packages/video-cli/src/distribution.ts +88 -0
- package/packages/video-cli/src/index.ts +50 -0
- package/packages/video-cli/src/media.ts +701 -0
- package/packages/video-cli/src/package-selection.ts +34 -0
- package/packages/video-cli/src/process.ts +71 -0
- package/packages/video-cli/src/studio-distribution.ts +19 -0
- package/packages/video-cli/src/transcript.ts +71 -0
- package/packages/video-cli/src/vocabulary.ts +386 -0
- package/packages/visual-ir/README.md +36 -0
- package/packages/visual-ir/package.json +16 -0
- package/packages/visual-ir/src/activation.ts +3 -0
- package/packages/visual-ir/src/index.ts +7 -0
- package/packages/visual-ir/src/style.ts +222 -0
- package/packages/volcengine-matting/README.md +89 -0
- package/packages/volcengine-matting/package.json +16 -0
- package/packages/volcengine-matting/src/activation.ts +13 -0
- package/packages/volcengine-matting/src/index.ts +45 -0
- package/packages/volcengine-matting/src/surface.ts +48 -0
- package/packages/whisperx/README.md +34 -0
- package/packages/whisperx/package.json +28 -0
- package/packages/whisperx/src/activation.ts +18 -0
- package/packages/whisperx/src/component.ts +64 -0
- package/packages/whisperx/src/evidence.ts +69 -0
- package/packages/whisperx/src/fragment.ts +76 -0
- package/packages/whisperx/src/index.ts +14 -0
- package/packages/whisperx/src/manifest.ts +92 -0
- package/packages/whisperx/src/surface.ts +90 -0
- package/packages/whisperx/src/transcript.ts +97 -0
- package/packages/whisperx/src/types.ts +9 -0
- package/packages/workspace/README.md +4 -0
- package/packages/workspace/package.json +14 -0
- package/packages/workspace/src/index.ts +38 -0
- package/packages/workspace-fs-node/README.md +13 -0
- package/packages/workspace-fs-node/package.json +15 -0
- package/packages/workspace-fs-node/src/index.ts +6 -0
- package/packages/workspace-fs-node/src/workspace.ts +226 -0
- package/packages/yt-dlp/README.md +31 -0
- package/packages/yt-dlp/package.json +10 -0
- package/packages/yt-dlp/src/download.ts +104 -0
- package/packages/yt-dlp/src/index.ts +1 -0
- package/services/image-opencv/README.md +33 -0
- package/services/image-opencv/package.json +14 -0
- package/services/image-opencv/pyproject.toml +13 -0
- package/services/image-opencv/uv.lock +56 -0
- package/services/whisperx/README.md +82 -0
- package/services/whisperx/package.json +15 -0
- package/services/whisperx/pyproject.toml +26 -0
- package/services/whisperx/src/hypit_whisperx_service/__init__.py +6 -0
- package/services/whisperx/src/hypit_whisperx_service/__main__.py +29 -0
- package/services/whisperx/src/hypit_whisperx_service/application.py +88 -0
- package/services/whisperx/src/hypit_whisperx_service/audio.py +66 -0
- package/services/whisperx/src/hypit_whisperx_service/check.py +35 -0
- package/services/whisperx/src/hypit_whisperx_service/config.py +81 -0
- package/services/whisperx/src/hypit_whisperx_service/engine.py +173 -0
- package/services/whisperx/src/hypit_whisperx_service/prepare.py +19 -0
- package/services/whisperx/src/hypit_whisperx_service/resources.py +41 -0
- package/services/whisperx/src/hypit_whisperx_service/server.py +101 -0
- package/services/whisperx/uv.lock +3381 -0
- package/services/yt-dlp/README.md +19 -0
- package/services/yt-dlp/package.json +14 -0
- package/services/yt-dlp/pyproject.toml +13 -0
- package/services/yt-dlp/uv.lock +23 -0
- package/index.js +0 -1
|
@@ -0,0 +1,1322 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { createReadStream } from "node:fs";
|
|
4
|
+
import { mkdtemp, open, readFile, rm, stat, writeFile } from "node:fs/promises";
|
|
5
|
+
import { tmpdir } from "node:os";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
import { verifyMediaFrameRange, mediaFrameRangeSamples, mediaTypes, sealMediaInspection, sealMuxedMedia, sealSynchronizedMedia, sealTimelineAudio, verifyMediaInspection, verifyMediaStreamSelection, verifyRenderedVisual, verifySynchronizedMedia, verifyTimelineAudio } from "@hypit/media";
|
|
8
|
+
import type { MediaAudioStream, MediaInspection, MediaRational, MediaStream, MediaStreamSelection, MediaTimestamp, MediaVideoStream, MuxedMedia, RenderedVisual, SynchronizedMedia, TimelineAudio } from "@hypit/media";
|
|
9
|
+
import type { ProgramSpace } from "@hypit/program-space";
|
|
10
|
+
import { assertSpeechEvidenceAudioIdentity, sealSpeechEvidenceAudio, speechEvidenceSampleBoundary, speechTypes } from "@hypit/speech";
|
|
11
|
+
import type { SpeechEvidenceAudio } from "@hypit/speech";
|
|
12
|
+
import {
|
|
13
|
+
mediaPipelineCapabilities,
|
|
14
|
+
verifyAudioExtractionRequest,
|
|
15
|
+
verifyAudioProgramPlan,
|
|
16
|
+
verifyFrameExtractionRequest,
|
|
17
|
+
verifyMediaTransformProgram,
|
|
18
|
+
type ExtractAudioNeed,
|
|
19
|
+
type ExtractFrameNeed,
|
|
20
|
+
type InspectMediaNeed,
|
|
21
|
+
type MuxMediaNeed,
|
|
22
|
+
type NormalizeMediaNeed,
|
|
23
|
+
type ProjectSpeechEvidenceAudioNeed,
|
|
24
|
+
type RenderAudioNeed,
|
|
25
|
+
type RenderStillVideoNeed,
|
|
26
|
+
type TransformMediaNeed,
|
|
27
|
+
verifyStillVideoRequest,
|
|
28
|
+
} from "@hypit/media-pipeline";
|
|
29
|
+
import type { AudioProgramClip, AudioProgramPlan, MediaTransformOperation } from "@hypit/media-pipeline";
|
|
30
|
+
import {
|
|
31
|
+
canonicalize,
|
|
32
|
+
} from "@hypit/protocol";
|
|
33
|
+
import type { BlobRef, CanonicalValue, StoredValue } from "@hypit/protocol";
|
|
34
|
+
|
|
35
|
+
import { parseMediaInspection } from "./probe.js";
|
|
36
|
+
import {
|
|
37
|
+
compositeAnimatedWebpFrame,
|
|
38
|
+
createAnimatedWebpCanvas,
|
|
39
|
+
pamRgba,
|
|
40
|
+
parseAnimatedWebp,
|
|
41
|
+
} from "./webp.js";
|
|
42
|
+
import type { AnimatedWebp } from "./webp.js";
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Where the bytes live and which binaries transform them.
|
|
46
|
+
*
|
|
47
|
+
* These byte operations are the whole of Hypit's media execution, and they
|
|
48
|
+
* are written once. A local Provider supplies the Build's own ResourceStore and
|
|
49
|
+
* the ffmpeg on its PATH; a Lambda Provider supplies an S3-backed gateway and
|
|
50
|
+
* the ffmpeg carried by its deployment. Nothing below knows which it is, so the two
|
|
51
|
+
* deployments cannot drift into computing different media from one Need.
|
|
52
|
+
*/
|
|
53
|
+
export type MediaArtifactGateway = {
|
|
54
|
+
get(source: BlobRef): Promise<Uint8Array | undefined>;
|
|
55
|
+
open(source: BlobRef): Promise<AsyncIterable<Uint8Array> | undefined>;
|
|
56
|
+
put(bytes: Uint8Array, mediaType: string): Promise<BlobRef>;
|
|
57
|
+
putFile(path: string, mediaType: string): Promise<BlobRef>;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export type MediaExecutionEnvironment = {
|
|
61
|
+
readonly artifacts: MediaArtifactGateway;
|
|
62
|
+
readonly ffmpegPath: string;
|
|
63
|
+
readonly ffprobePath: string;
|
|
64
|
+
/** Deployment fact for dynamically linked tool bundles; absent for ordinary local binaries. */
|
|
65
|
+
readonly sharedLibraryPath?: string;
|
|
66
|
+
readonly processTimeoutMs: number;
|
|
67
|
+
readonly maxProbeOutputBytes: number;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export type MediaOperationResult = {
|
|
71
|
+
readonly value: StoredValue;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
function assert(condition: unknown, message: string): asserts condition {
|
|
75
|
+
if (!condition) throw new Error(message);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function positiveInteger(value: number, subject: string): number {
|
|
79
|
+
assert(Number.isSafeInteger(value) && value > 0, `${subject} must be a positive integer`);
|
|
80
|
+
return value;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function object(value: unknown, subject: string): Record<string, unknown> {
|
|
84
|
+
assert(value !== null && typeof value === "object" && !Array.isArray(value), `${subject} must be an object`);
|
|
85
|
+
return value as Record<string, unknown>;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function runProcess(args: {
|
|
89
|
+
readonly executable: string;
|
|
90
|
+
readonly argv: readonly string[];
|
|
91
|
+
readonly timeoutMs: number;
|
|
92
|
+
readonly maxStdoutBytes: number;
|
|
93
|
+
readonly sharedLibraryPath?: string;
|
|
94
|
+
}): Promise<Uint8Array> {
|
|
95
|
+
return await new Promise((resolve, reject) => {
|
|
96
|
+
const child = spawn(args.executable, [...args.argv], {
|
|
97
|
+
shell: false,
|
|
98
|
+
windowsHide: true,
|
|
99
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
100
|
+
env: {
|
|
101
|
+
PATH: process.env.PATH ?? "",
|
|
102
|
+
...(args.sharedLibraryPath === undefined ? {} : { LD_LIBRARY_PATH: args.sharedLibraryPath }),
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
const stdout: Buffer[] = [];
|
|
106
|
+
let stdoutBytes = 0;
|
|
107
|
+
let stderr = "";
|
|
108
|
+
let settled = false;
|
|
109
|
+
const finish = (error?: Error): void => {
|
|
110
|
+
if (settled) return;
|
|
111
|
+
settled = true;
|
|
112
|
+
clearTimeout(timer);
|
|
113
|
+
if (error !== undefined) reject(error);
|
|
114
|
+
else resolve(Buffer.concat(stdout));
|
|
115
|
+
};
|
|
116
|
+
const timer = setTimeout(() => {
|
|
117
|
+
child.kill("SIGKILL");
|
|
118
|
+
finish(new Error(`${args.executable} timed out`));
|
|
119
|
+
}, args.timeoutMs);
|
|
120
|
+
child.stdout.on("data", (chunk: Buffer) => {
|
|
121
|
+
stdoutBytes += chunk.byteLength;
|
|
122
|
+
if (stdoutBytes > args.maxStdoutBytes) {
|
|
123
|
+
child.kill("SIGKILL");
|
|
124
|
+
finish(new Error(`${args.executable} output exceeded the configured limit`));
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
stdout.push(chunk);
|
|
128
|
+
});
|
|
129
|
+
child.stderr.on("data", (chunk: Buffer) => {
|
|
130
|
+
stderr = `${stderr}${chunk.toString()}`.slice(-8_000);
|
|
131
|
+
});
|
|
132
|
+
child.on("error", (error) => finish(error));
|
|
133
|
+
child.on("close", (code) => {
|
|
134
|
+
if (code !== 0) finish(new Error(`${args.executable} exited ${String(code)}: ${stderr}`));
|
|
135
|
+
else finish();
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async function version(executable: string, timeoutMs: number, sharedLibraryPath?: string): Promise<string> {
|
|
141
|
+
const bytes = await runProcess({ executable, argv: ["-version"], timeoutMs, maxStdoutBytes: 64 * 1024,
|
|
142
|
+
...(sharedLibraryPath === undefined ? {} : { sharedLibraryPath }) });
|
|
143
|
+
const line = Buffer.from(bytes).toString("utf8").split(/\r?\n/u, 1)[0]?.trim();
|
|
144
|
+
assert(line !== undefined && line.length > 0, `${executable} returned no version`);
|
|
145
|
+
return line;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
async function sourceBytes(env: MediaExecutionEnvironment, source: BlobRef): Promise<Uint8Array> {
|
|
149
|
+
const bytes = await env.artifacts.get(source);
|
|
150
|
+
assert(bytes !== undefined, `Media source ${source.resource} is unavailable`);
|
|
151
|
+
assert(bytes.byteLength === source.size, `Media source ${source.resource} size differs`);
|
|
152
|
+
return bytes;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
async function stageArtifact(
|
|
156
|
+
env: MediaExecutionEnvironment,
|
|
157
|
+
source: BlobRef,
|
|
158
|
+
path: string,
|
|
159
|
+
): Promise<void> {
|
|
160
|
+
const chunks = await env.artifacts.open(source);
|
|
161
|
+
assert(chunks !== undefined, `Media source ${source.resource} is unavailable`);
|
|
162
|
+
const file = await open(path, "w");
|
|
163
|
+
let size = 0;
|
|
164
|
+
try {
|
|
165
|
+
for await (const chunk of chunks) {
|
|
166
|
+
await file.write(chunk);
|
|
167
|
+
size += chunk.byteLength;
|
|
168
|
+
}
|
|
169
|
+
} finally {
|
|
170
|
+
await file.close();
|
|
171
|
+
}
|
|
172
|
+
assert(size === source.size, `Media source ${source.resource} size differs`);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function timestampFraction(value: MediaTimestamp): { numerator: bigint; denominator: bigint } {
|
|
176
|
+
return {
|
|
177
|
+
numerator: BigInt(value.ticks) * BigInt(value.timeBase.numerator),
|
|
178
|
+
denominator: BigInt(value.timeBase.denominator),
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function compareTimestamp(left: MediaTimestamp, right: MediaTimestamp): number {
|
|
183
|
+
const a = timestampFraction(left);
|
|
184
|
+
const b = timestampFraction(right);
|
|
185
|
+
const x = a.numerator * b.denominator;
|
|
186
|
+
const y = b.numerator * a.denominator;
|
|
187
|
+
return x < y ? -1 : x > y ? 1 : 0;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function roundPositive(numerator: bigint, denominator: bigint): number {
|
|
191
|
+
assert(numerator >= 0n && denominator > 0n, "Media duration is invalid");
|
|
192
|
+
const value = (numerator * 2n + denominator) / (denominator * 2n);
|
|
193
|
+
assert(value <= BigInt(Number.MAX_SAFE_INTEGER), "Media duration exceeds safe arithmetic");
|
|
194
|
+
return Number(value);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function ceilPositive(numerator: bigint, denominator: bigint): number {
|
|
198
|
+
assert(numerator >= 0n && denominator > 0n, "Media duration is invalid");
|
|
199
|
+
const value = (numerator + denominator - 1n) / denominator;
|
|
200
|
+
assert(value <= BigInt(Number.MAX_SAFE_INTEGER), "Media duration exceeds safe arithmetic");
|
|
201
|
+
return Number(value);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function duration(value: { startPts?: MediaTimestamp; endPts?: MediaTimestamp }): { numerator: bigint; denominator: bigint } {
|
|
205
|
+
assert(value.startPts !== undefined && value.endPts !== undefined, "Selected media stream has no presentation interval");
|
|
206
|
+
const start = timestampFraction(value.startPts);
|
|
207
|
+
const end = timestampFraction(value.endPts);
|
|
208
|
+
const numerator = end.numerator * start.denominator - start.numerator * end.denominator;
|
|
209
|
+
const denominator = end.denominator * start.denominator;
|
|
210
|
+
assert(numerator > 0n, "Selected media stream has an empty presentation interval");
|
|
211
|
+
return { numerator, denominator };
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function signedOffset(origin: MediaTimestamp, value: MediaTimestamp, rate: number): number {
|
|
215
|
+
const a = timestampFraction(origin);
|
|
216
|
+
const b = timestampFraction(value);
|
|
217
|
+
const numerator = (b.numerator * a.denominator - a.numerator * b.denominator) * BigInt(rate);
|
|
218
|
+
const denominator = b.denominator * a.denominator;
|
|
219
|
+
const magnitude = roundPositive(numerator < 0n ? -numerator : numerator, denominator);
|
|
220
|
+
return numerator < 0n ? -magnitude : magnitude;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function stream<T extends MediaStream["kind"]>(
|
|
224
|
+
inspection: MediaInspection,
|
|
225
|
+
index: number | undefined,
|
|
226
|
+
kind: T,
|
|
227
|
+
): Extract<MediaStream, { readonly kind: T }> | undefined {
|
|
228
|
+
if (index === undefined) return undefined;
|
|
229
|
+
const result = inspection.streams.find((item) => item.index === index);
|
|
230
|
+
assert(result?.kind === kind, `Selected ${kind} stream ${index} is absent`);
|
|
231
|
+
assert(result.timingStatus === "admissible", `Selected ${kind} stream ${index} timing is ${result.timingStatus}`);
|
|
232
|
+
return result as Extract<MediaStream, { readonly kind: T }>;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
type NormalizationPlan = {
|
|
236
|
+
readonly video?: MediaVideoStream;
|
|
237
|
+
readonly audio?: MediaAudioStream;
|
|
238
|
+
readonly frameCount: number;
|
|
239
|
+
readonly sampleFrames: number;
|
|
240
|
+
readonly audioTrimStartSamples: number;
|
|
241
|
+
readonly audioTrimEndSamples: number;
|
|
242
|
+
readonly audioHeadSamples: number;
|
|
243
|
+
readonly audioContentSamples: number;
|
|
244
|
+
readonly audioTailSamples: number;
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
function normalizationPlan(
|
|
248
|
+
inspection: MediaInspection,
|
|
249
|
+
selection: MediaStreamSelection,
|
|
250
|
+
frameRate: MediaRational,
|
|
251
|
+
): NormalizationPlan {
|
|
252
|
+
assert(Number.isSafeInteger(frameRate.numerator) && frameRate.numerator > 0
|
|
253
|
+
&& Number.isSafeInteger(frameRate.denominator) && frameRate.denominator > 0,
|
|
254
|
+
"Target frame rate is invalid");
|
|
255
|
+
const video = stream(inspection, selection.videoStreamIndex, "video");
|
|
256
|
+
const audio = stream(inspection, selection.audioStreamIndex, "audio");
|
|
257
|
+
if (video !== undefined && selection.spanAuthority !== "video") {
|
|
258
|
+
throw new Error("A synchronized visual currently requires video span authority; use audio authority only for audio-only media");
|
|
259
|
+
}
|
|
260
|
+
const authority = selection.spanAuthority === "video" ? video : audio;
|
|
261
|
+
assert(authority?.startPts !== undefined && authority.endPts !== undefined, "Span authority stream is absent");
|
|
262
|
+
const span = duration(authority);
|
|
263
|
+
const scaledNumerator = span.numerator * BigInt(frameRate.numerator);
|
|
264
|
+
const scaledDenominator = span.denominator * BigInt(frameRate.denominator);
|
|
265
|
+
const frameCount = selection.spanAuthority === "video"
|
|
266
|
+
? roundPositive(scaledNumerator, scaledDenominator)
|
|
267
|
+
: ceilPositive(scaledNumerator, scaledDenominator);
|
|
268
|
+
assert(frameCount > 0, "Normalized media would contain no frame interval");
|
|
269
|
+
const sampleFrames = roundPositive(
|
|
270
|
+
BigInt(frameCount) * 48_000n * BigInt(frameRate.denominator),
|
|
271
|
+
BigInt(frameRate.numerator),
|
|
272
|
+
);
|
|
273
|
+
if (audio === undefined) {
|
|
274
|
+
return {
|
|
275
|
+
...(video === undefined ? {} : { video }),
|
|
276
|
+
frameCount,
|
|
277
|
+
sampleFrames,
|
|
278
|
+
audioTrimStartSamples: 0,
|
|
279
|
+
audioTrimEndSamples: 0,
|
|
280
|
+
audioHeadSamples: 0,
|
|
281
|
+
audioContentSamples: 0,
|
|
282
|
+
audioTailSamples: 0,
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
assert(audio.startPts !== undefined && audio.endPts !== undefined, "Selected audio has no presentation interval");
|
|
286
|
+
const audioStart = signedOffset(authority.startPts, audio.startPts, 48_000);
|
|
287
|
+
const audioEnd = signedOffset(authority.startPts, audio.endPts, 48_000);
|
|
288
|
+
const contentStart = Math.max(0, audioStart);
|
|
289
|
+
const contentEnd = Math.min(sampleFrames, audioEnd);
|
|
290
|
+
assert(audioEnd > audioStart && contentEnd > contentStart, "Selected audio is outside the authoritative media span");
|
|
291
|
+
return {
|
|
292
|
+
...(video === undefined ? {} : { video }),
|
|
293
|
+
audio,
|
|
294
|
+
frameCount,
|
|
295
|
+
sampleFrames,
|
|
296
|
+
audioTrimStartSamples: Math.max(0, -audioStart),
|
|
297
|
+
audioTrimEndSamples: Math.max(0, audioEnd - sampleFrames),
|
|
298
|
+
audioHeadSamples: contentStart,
|
|
299
|
+
audioContentSamples: contentEnd - contentStart,
|
|
300
|
+
audioTailSamples: sampleFrames - contentEnd,
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
async function inspectFile(args: {
|
|
305
|
+
readonly source: BlobRef;
|
|
306
|
+
readonly input: string;
|
|
307
|
+
readonly ffprobePath: string;
|
|
308
|
+
readonly timeoutMs: number;
|
|
309
|
+
readonly maxProbeOutputBytes: number;
|
|
310
|
+
readonly sharedLibraryPath?: string;
|
|
311
|
+
}): Promise<MediaInspection> {
|
|
312
|
+
const [raw, ffprobeVersion] = await Promise.all([
|
|
313
|
+
runProcess({
|
|
314
|
+
executable: args.ffprobePath,
|
|
315
|
+
argv: ["-v", "error", "-print_format", "json", "-show_format", "-show_streams", "-show_frames", args.input],
|
|
316
|
+
timeoutMs: args.timeoutMs,
|
|
317
|
+
maxStdoutBytes: args.maxProbeOutputBytes,
|
|
318
|
+
...(args.sharedLibraryPath === undefined ? {} : { sharedLibraryPath: args.sharedLibraryPath }),
|
|
319
|
+
}),
|
|
320
|
+
version(args.ffprobePath, args.timeoutMs, args.sharedLibraryPath),
|
|
321
|
+
]);
|
|
322
|
+
let value: unknown;
|
|
323
|
+
try {
|
|
324
|
+
value = JSON.parse(Buffer.from(raw).toString("utf8"));
|
|
325
|
+
} catch {
|
|
326
|
+
throw new Error("ffprobe returned invalid JSON");
|
|
327
|
+
}
|
|
328
|
+
return parseMediaInspection({ source: args.source, ffprobeVersion, value });
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function divisor(left: number, right: number): number {
|
|
332
|
+
let a = Math.abs(left);
|
|
333
|
+
let b = Math.abs(right);
|
|
334
|
+
while (b !== 0) [a, b] = [b, a % b];
|
|
335
|
+
return a;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
function animatedWebpInspection(source: BlobRef, animation: AnimatedWebp): MediaInspection {
|
|
339
|
+
const durationMs = animation.frames.reduce((sum, frame) => sum + frame.durationMs, 0);
|
|
340
|
+
const numerator = animation.frames.length * 1_000;
|
|
341
|
+
const factor = divisor(numerator, durationMs);
|
|
342
|
+
return sealMediaInspection({
|
|
343
|
+
container: { formatNames: ["webp", "webp-animation"] },
|
|
344
|
+
streams: [{
|
|
345
|
+
kind: "video",
|
|
346
|
+
index: 0,
|
|
347
|
+
codecType: "video",
|
|
348
|
+
codecName: "webp",
|
|
349
|
+
disposition: { default: true, attachedPicture: false },
|
|
350
|
+
timingStatus: "admissible",
|
|
351
|
+
timeBase: { numerator: 1, denominator: 1_000 },
|
|
352
|
+
startPts: { ticks: "0", timeBase: { numerator: 1, denominator: 1_000 } },
|
|
353
|
+
endPts: { ticks: String(durationMs), timeBase: { numerator: 1, denominator: 1_000 } },
|
|
354
|
+
decodedUnitCount: animation.frames.length,
|
|
355
|
+
role: "moving",
|
|
356
|
+
width: animation.width,
|
|
357
|
+
height: animation.height,
|
|
358
|
+
sampleAspectRatio: { numerator: 1, denominator: 1 },
|
|
359
|
+
rotationDegrees: 0,
|
|
360
|
+
averageFrameRate: { numerator: numerator / factor, denominator: durationMs / factor },
|
|
361
|
+
}],
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
async function animatedWebpConcat(
|
|
366
|
+
env: MediaExecutionEnvironment,
|
|
367
|
+
animation: AnimatedWebp,
|
|
368
|
+
work: string,
|
|
369
|
+
): Promise<string> {
|
|
370
|
+
assert(animation.frames.length <= 10_000, "Animated WebP has too many frames");
|
|
371
|
+
const canvasBytes = animation.width * animation.height * 4;
|
|
372
|
+
assert(Number.isSafeInteger(canvasBytes) && canvasBytes > 0 && canvasBytes <= 512 * 1024 * 1024,
|
|
373
|
+
"Animated WebP canvas is outside the supported memory bound");
|
|
374
|
+
const canvas = createAnimatedWebpCanvas(animation);
|
|
375
|
+
const paths: string[] = [];
|
|
376
|
+
for (const [index, frame] of animation.frames.entries()) {
|
|
377
|
+
const imagePath = join(work, `webp-source-${String(index).padStart(6, "0")}.webp`);
|
|
378
|
+
await writeFile(imagePath, frame.image);
|
|
379
|
+
const decoded = await runProcess({
|
|
380
|
+
executable: env.ffmpegPath,
|
|
381
|
+
argv: ["-v", "error", "-i", imagePath, "-frames:v", "1", "-f", "rawvideo", "-pix_fmt", "rgba", "-"],
|
|
382
|
+
timeoutMs: env.processTimeoutMs,
|
|
383
|
+
maxStdoutBytes: frame.width * frame.height * 4,
|
|
384
|
+
...(env.sharedLibraryPath === undefined ? {} : { sharedLibraryPath: env.sharedLibraryPath }),
|
|
385
|
+
});
|
|
386
|
+
const snapshot = compositeAnimatedWebpFrame(canvas, animation, frame, decoded);
|
|
387
|
+
const path = join(work, `webp-frame-${String(index).padStart(6, "0")}.pam`);
|
|
388
|
+
await writeFile(path, pamRgba(animation.width, animation.height, snapshot));
|
|
389
|
+
paths.push(path);
|
|
390
|
+
}
|
|
391
|
+
const lines = ["ffconcat version 1.0"];
|
|
392
|
+
for (const [index, path] of paths.entries()) {
|
|
393
|
+
lines.push(`file '${path}'`, `duration ${(animation.frames[index]!.durationMs / 1_000).toFixed(6)}`);
|
|
394
|
+
}
|
|
395
|
+
lines.push(`file '${paths.at(-1)!}'`);
|
|
396
|
+
const manifest = join(work, "animated-webp.ffconcat");
|
|
397
|
+
await writeFile(manifest, `${lines.join("\n")}\n`, "utf8");
|
|
398
|
+
return manifest;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
async function outputInspection(args: {
|
|
402
|
+
readonly path: string;
|
|
403
|
+
readonly mediaType: string;
|
|
404
|
+
readonly ffprobePath: string;
|
|
405
|
+
readonly timeoutMs: number;
|
|
406
|
+
readonly maxProbeOutputBytes: number;
|
|
407
|
+
readonly sharedLibraryPath?: string;
|
|
408
|
+
}): Promise<MediaInspection> {
|
|
409
|
+
const size = (await stat(args.path)).size;
|
|
410
|
+
const source: BlobRef = {
|
|
411
|
+
kind: "blob",
|
|
412
|
+
resource: `res_${randomUUID()}`,
|
|
413
|
+
size,
|
|
414
|
+
mediaType: args.mediaType,
|
|
415
|
+
};
|
|
416
|
+
return await inspectFile({ source, input: args.path, ffprobePath: args.ffprobePath,
|
|
417
|
+
timeoutMs: args.timeoutMs, maxProbeOutputBytes: args.maxProbeOutputBytes,
|
|
418
|
+
...(args.sharedLibraryPath === undefined ? {} : { sharedLibraryPath: args.sharedLibraryPath }) });
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
function inlineResult(value: CanonicalValue): MediaOperationResult {
|
|
422
|
+
return { value: { kind: "inline", value } };
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
function artifactResult(value: BlobRef): MediaOperationResult {
|
|
426
|
+
return { value };
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
function inspectNeed(value: CanonicalValue): InspectMediaNeed {
|
|
430
|
+
const item = object(value, "InspectMediaNeed") as unknown as InspectMediaNeed;
|
|
431
|
+
assert(item.source?.kind === "blob", "InspectMediaNeed is invalid");
|
|
432
|
+
return item;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
function normalizeNeed(value: CanonicalValue): NormalizeMediaNeed {
|
|
436
|
+
const item = object(value, "NormalizeMediaNeed") as unknown as NormalizeMediaNeed;
|
|
437
|
+
assert(item.source?.kind === "blob", "NormalizeMediaNeed is invalid");
|
|
438
|
+
verifyMediaInspection(item.inspection);
|
|
439
|
+
verifyMediaStreamSelection(item.selection);
|
|
440
|
+
assert(item.audio.sampleRate === 48_000 && item.audio.channels === 2
|
|
441
|
+
&& item.audio.codec === "pcm_s16le" && item.audio.loudness === "preserve",
|
|
442
|
+
"NormalizeMediaNeed audio profile is unsupported");
|
|
443
|
+
return item;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
function transformNeed(value: CanonicalValue): TransformMediaNeed {
|
|
447
|
+
const item = object(value, "TransformMediaNeed") as unknown as TransformMediaNeed;
|
|
448
|
+
verifySynchronizedMedia(item.media);
|
|
449
|
+
verifyMediaTransformProgram(item.program);
|
|
450
|
+
assert(item.media.visual !== undefined, "TransformMediaNeed requires synchronized visual media");
|
|
451
|
+
return item;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
function extractAudioNeed(value: CanonicalValue): ExtractAudioNeed {
|
|
455
|
+
const item = object(value, "ExtractAudioNeed") as unknown as ExtractAudioNeed;
|
|
456
|
+
assert(item.source?.kind === "blob", "ExtractAudioNeed is invalid");
|
|
457
|
+
assert(Number.isSafeInteger(item.streamIndex) && item.streamIndex >= 0,
|
|
458
|
+
"ExtractAudioNeed streamIndex is invalid");
|
|
459
|
+
verifyAudioExtractionRequest({
|
|
460
|
+
audio: { mode: "stream-index", streamIndex: item.streamIndex },
|
|
461
|
+
output: item.output,
|
|
462
|
+
});
|
|
463
|
+
return item;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
function extractFrameNeed(value: CanonicalValue): ExtractFrameNeed {
|
|
467
|
+
const item = object(value, "ExtractFrameNeed") as unknown as ExtractFrameNeed;
|
|
468
|
+
assert(item.source?.kind === "blob", "ExtractFrameNeed is invalid");
|
|
469
|
+
assert(Number.isSafeInteger(item.streamIndex) && item.streamIndex >= 0
|
|
470
|
+
&& Number.isSafeInteger(item.sourceFrameCount) && item.sourceFrameCount > 0,
|
|
471
|
+
"ExtractFrameNeed stream domain is invalid");
|
|
472
|
+
verifyFrameExtractionRequest({
|
|
473
|
+
video: { mode: "stream-index", streamIndex: item.streamIndex },
|
|
474
|
+
at: item.at,
|
|
475
|
+
output: item.output,
|
|
476
|
+
});
|
|
477
|
+
if (item.at.kind === "frame") {
|
|
478
|
+
assert(item.at.index < item.sourceFrameCount, "ExtractFrameNeed frame lies outside the source stream");
|
|
479
|
+
}
|
|
480
|
+
return item;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
function renderStillVideoNeed(value: CanonicalValue): RenderStillVideoNeed {
|
|
484
|
+
const item = object(value, "RenderStillVideoNeed") as unknown as RenderStillVideoNeed;
|
|
485
|
+
verifyStillVideoRequest(item.request);
|
|
486
|
+
assert(item.request.segments.every((segment) => segment.source?.kind === "blob" && segment.source.mediaType.startsWith("image/")),
|
|
487
|
+
"RenderStillVideoNeed needs an image Artifact for every segment");
|
|
488
|
+
return item;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
function evidenceAudioNeed(value: CanonicalValue): ProjectSpeechEvidenceAudioNeed {
|
|
492
|
+
const item = object(value, "ProjectSpeechEvidenceAudioNeed") as unknown as ProjectSpeechEvidenceAudioNeed;
|
|
493
|
+
assert(item.source?.kind === "blob", "ProjectSpeechEvidenceAudioNeed is invalid");
|
|
494
|
+
assert(Number.isSafeInteger(item.sourceSampleFrames) && item.sourceSampleFrames > 0
|
|
495
|
+
&& Number.isSafeInteger(item.evidenceSampleFrames) && item.evidenceSampleFrames > 0
|
|
496
|
+
&& item.evidenceSampleFrames === speechEvidenceSampleBoundary(item.sourceSampleFrames),
|
|
497
|
+
"Speech evidence audio sample projection is invalid");
|
|
498
|
+
return item;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
function renderAudioNeed(value: CanonicalValue): RenderAudioNeed {
|
|
502
|
+
const item = object(value, "RenderAudioNeed") as unknown as RenderAudioNeed;
|
|
503
|
+
verifyAudioProgramPlan(item.plan);
|
|
504
|
+
if (item.range !== undefined) verifyMediaFrameRange(item.range, item.plan.frameCount);
|
|
505
|
+
return item;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
function muxMediaNeed(value: CanonicalValue): MuxMediaNeed {
|
|
509
|
+
const item = object(value, "MuxMediaNeed") as unknown as MuxMediaNeed;
|
|
510
|
+
verifyRenderedVisual(item.visual);
|
|
511
|
+
verifyTimelineAudio(item.audio);
|
|
512
|
+
const expectedSamples = roundPositive(
|
|
513
|
+
BigInt(item.visual.frameCount) * 48_000n * BigInt(item.visual.frameRate.denominator),
|
|
514
|
+
BigInt(item.visual.frameRate.numerator),
|
|
515
|
+
);
|
|
516
|
+
assert(item.audio.sampleFrames === expectedSamples, "MuxMediaNeed audio differs from the visual frame domain");
|
|
517
|
+
return item;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
function atempo(rate: number): string[] {
|
|
521
|
+
if (Math.abs(rate - 1) < 1e-12) return [];
|
|
522
|
+
const filters: string[] = [];
|
|
523
|
+
let remaining = rate;
|
|
524
|
+
while (remaining > 2) {
|
|
525
|
+
filters.push("atempo=2");
|
|
526
|
+
remaining /= 2;
|
|
527
|
+
}
|
|
528
|
+
while (remaining < 0.5) {
|
|
529
|
+
filters.push("atempo=0.5");
|
|
530
|
+
remaining /= 0.5;
|
|
531
|
+
}
|
|
532
|
+
if (Math.abs(remaining - 1) >= 1e-12) filters.push(`atempo=${remaining.toPrecision(15)}`);
|
|
533
|
+
return filters;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
function audioClipFilter(clip: AudioProgramClip, inputIndex: number, outputIndex: number,
|
|
537
|
+
window: { readonly startSample: number; readonly endSampleExclusive: number }): string {
|
|
538
|
+
const length = clip.targetEndSampleExclusive - clip.targetStartSample;
|
|
539
|
+
const sourceLength = clip.sourceEndSampleExclusive - clip.sourceStartSample;
|
|
540
|
+
const left = Math.max(window.startSample, clip.targetStartSample);
|
|
541
|
+
const right = Math.min(window.endSampleExclusive, clip.targetEndSampleExclusive);
|
|
542
|
+
const filters = [
|
|
543
|
+
`atrim=start_sample=${clip.sourceStartSample}:end_sample=${clip.sourceEndSampleExclusive}`,
|
|
544
|
+
"asetpts=PTS-STARTPTS",
|
|
545
|
+
...(clip.sourceLoop ? [`aloop=loop=-1:size=${sourceLength}:start=0`] : []),
|
|
546
|
+
...(clip.sourcePhaseSample === 0 ? [] : [
|
|
547
|
+
`atrim=start_sample=${clip.sourcePhaseSample}`,
|
|
548
|
+
"asetpts=PTS-STARTPTS",
|
|
549
|
+
]),
|
|
550
|
+
...atempo(clip.playbackRate),
|
|
551
|
+
`atrim=start_sample=0:end_sample=${length}`,
|
|
552
|
+
`apad=whole_len=${length}`,
|
|
553
|
+
`atrim=start_sample=0:end_sample=${length}`,
|
|
554
|
+
`volume=${clip.gain.toPrecision(15)}`,
|
|
555
|
+
...(clip.fadeInSamples === 0 ? [] : [`afade=t=in:start_sample=0:nb_samples=${clip.fadeInSamples}`]),
|
|
556
|
+
...(clip.fadeOutSamples === 0 ? [] : [
|
|
557
|
+
`afade=t=out:start_sample=${length - clip.fadeOutSamples}:nb_samples=${clip.fadeOutSamples}`,
|
|
558
|
+
]),
|
|
559
|
+
// Crop after tempo, looping and envelopes, keeping their original phase.
|
|
560
|
+
`atrim=start_sample=${left - clip.targetStartSample}:end_sample=${right - clip.targetStartSample}`,
|
|
561
|
+
"asetpts=N/SR/TB",
|
|
562
|
+
`adelay=${left - window.startSample}S:all=1`,
|
|
563
|
+
];
|
|
564
|
+
return `[${inputIndex}:a:0]${filters.join(",")}[clip${outputIndex}]`;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
async function assertCanonicalWav(args: {
|
|
568
|
+
readonly path: string;
|
|
569
|
+
readonly source: BlobRef;
|
|
570
|
+
readonly ffprobePath: string;
|
|
571
|
+
readonly timeoutMs: number;
|
|
572
|
+
readonly maxProbeOutputBytes: number;
|
|
573
|
+
readonly sharedLibraryPath?: string;
|
|
574
|
+
}): Promise<MediaAudioStream> {
|
|
575
|
+
const inspection = await inspectFile({
|
|
576
|
+
source: args.source,
|
|
577
|
+
input: args.path,
|
|
578
|
+
ffprobePath: args.ffprobePath,
|
|
579
|
+
timeoutMs: args.timeoutMs,
|
|
580
|
+
maxProbeOutputBytes: args.maxProbeOutputBytes,
|
|
581
|
+
...(args.sharedLibraryPath === undefined ? {} : { sharedLibraryPath: args.sharedLibraryPath }),
|
|
582
|
+
});
|
|
583
|
+
const audio = inspection.streams.filter((item): item is MediaAudioStream => item.kind === "audio");
|
|
584
|
+
assert(audio.length === 1 && inspection.streams.length === 1, "Canonical audio must contain exactly one stream");
|
|
585
|
+
assert(audio[0]!.codecName === "pcm_s16le" && audio[0]!.sampleRate === 48_000 && audio[0]!.channels === 2,
|
|
586
|
+
"Canonical audio must be 48 kHz stereo PCM s16");
|
|
587
|
+
assert(audio[0]!.timingStatus === "admissible", "Canonical audio timing is not admissible");
|
|
588
|
+
return audio[0]!;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
export async function executeInspectMedia(
|
|
592
|
+
env: MediaExecutionEnvironment,
|
|
593
|
+
constraints: CanonicalValue,
|
|
594
|
+
): Promise<MediaOperationResult> {
|
|
595
|
+
const need = inspectNeed(constraints);
|
|
596
|
+
const work = await mkdtemp(join(tmpdir(), "hypit-media-inspect-"));
|
|
597
|
+
try {
|
|
598
|
+
const input = join(work, "source.bin");
|
|
599
|
+
const animationBytes = need.source.mediaType === "image/webp" ? await sourceBytes(env, need.source) : undefined;
|
|
600
|
+
if (animationBytes === undefined) await stageArtifact(env, need.source, input);
|
|
601
|
+
else await writeFile(input, animationBytes);
|
|
602
|
+
const animation = animationBytes === undefined ? undefined : parseAnimatedWebp(animationBytes);
|
|
603
|
+
const inspection = animation === undefined
|
|
604
|
+
? await inspectFile({
|
|
605
|
+
source: need.source,
|
|
606
|
+
input,
|
|
607
|
+
ffprobePath: env.ffprobePath,
|
|
608
|
+
timeoutMs: env.processTimeoutMs,
|
|
609
|
+
maxProbeOutputBytes: env.maxProbeOutputBytes,
|
|
610
|
+
...(env.sharedLibraryPath === undefined ? {} : { sharedLibraryPath: env.sharedLibraryPath }),
|
|
611
|
+
})
|
|
612
|
+
: animatedWebpInspection(need.source, animation);
|
|
613
|
+
return inlineResult(canonicalize(inspection));
|
|
614
|
+
} finally {
|
|
615
|
+
await rm(work, { recursive: true, force: true }).catch(() => {});
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
async function sourceVideoEncoding(env: MediaExecutionEnvironment, path: string, streamIndex: number) {
|
|
620
|
+
const bytes = await runProcess({
|
|
621
|
+
executable: env.ffprobePath,
|
|
622
|
+
argv: ["-v", "error", "-print_format", "json", "-show_streams", "-show_pixel_formats", path],
|
|
623
|
+
timeoutMs: env.processTimeoutMs, maxStdoutBytes: env.maxProbeOutputBytes,
|
|
624
|
+
...(env.sharedLibraryPath === undefined ? {} : { sharedLibraryPath: env.sharedLibraryPath }),
|
|
625
|
+
});
|
|
626
|
+
const probe = JSON.parse(Buffer.from(bytes).toString("utf8")) as {
|
|
627
|
+
streams: { index: number; codec_name?: string; pix_fmt?: string; tags?: Record<string, string> }[];
|
|
628
|
+
pixel_formats: { name: string; flags: { alpha: number } }[];
|
|
629
|
+
};
|
|
630
|
+
const stream = probe.streams.find((item) => item.index === streamIndex);
|
|
631
|
+
assert(stream !== undefined, `Media source has no video stream ${streamIndex}`);
|
|
632
|
+
const pixelFormat = probe.pixel_formats.find((item) => item.name === stream.pix_fmt);
|
|
633
|
+
assert(pixelFormat !== undefined, `Media source pixel format ${String(stream.pix_fmt)} is unknown`);
|
|
634
|
+
const alphaTag = Object.entries(stream.tags ?? {}).find(([key]) => key.toLowerCase() === "alpha_mode")?.[1];
|
|
635
|
+
const alpha = pixelFormat.flags.alpha === 1 || alphaTag === "1" || alphaTag === "straight";
|
|
636
|
+
// FFmpeg's native VP8/VP9 decoders discard the WebM alpha sidecar.
|
|
637
|
+
const decoder = !alpha ? undefined
|
|
638
|
+
: stream.codec_name === "vp9" ? "libvpx-vp9" : stream.codec_name === "vp8" ? "libvpx" : undefined;
|
|
639
|
+
return {
|
|
640
|
+
alpha,
|
|
641
|
+
inputArgs: decoder === undefined ? [] : [`-c:${streamIndex}`, decoder],
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
export async function executeNormalizeMedia(
|
|
646
|
+
env: MediaExecutionEnvironment,
|
|
647
|
+
constraints: CanonicalValue,
|
|
648
|
+
): Promise<MediaOperationResult> {
|
|
649
|
+
const need = normalizeNeed(constraints);
|
|
650
|
+
const plan = normalizationPlan(need.inspection, need.selection, need.frameRate);
|
|
651
|
+
const work = await mkdtemp(join(tmpdir(), "hypit-media-normalize-"));
|
|
652
|
+
try {
|
|
653
|
+
const input = join(work, "source.bin");
|
|
654
|
+
const animationBytes = need.source.mediaType === "image/webp" ? await sourceBytes(env, need.source) : undefined;
|
|
655
|
+
if (animationBytes === undefined) await stageArtifact(env, need.source, input);
|
|
656
|
+
else await writeFile(input, animationBytes);
|
|
657
|
+
const animation = animationBytes === undefined ? undefined : parseAnimatedWebp(animationBytes);
|
|
658
|
+
let visualArtifact: BlobRef | undefined;
|
|
659
|
+
let visualWidth: number | undefined;
|
|
660
|
+
let visualHeight: number | undefined;
|
|
661
|
+
if (plan.video !== undefined) {
|
|
662
|
+
const encoding = animation === undefined
|
|
663
|
+
? await sourceVideoEncoding(env, input, plan.video.index)
|
|
664
|
+
: { alpha: true, inputArgs: [] };
|
|
665
|
+
const outputType = encoding.alpha ? "video/webm" : "video/mp4";
|
|
666
|
+
const output = join(work, encoding.alpha ? "visual.webm" : "visual.mp4");
|
|
667
|
+
const fps = `${need.frameRate.numerator}/${need.frameRate.denominator}`;
|
|
668
|
+
const filter = [
|
|
669
|
+
"setpts=PTS-STARTPTS",
|
|
670
|
+
`fps=fps=${fps}:round=near:start_time=0:eof_action=round`,
|
|
671
|
+
`trim=start_frame=0:end_frame=${plan.frameCount}`,
|
|
672
|
+
`setpts=N*${need.frameRate.denominator}/(${need.frameRate.numerator}*TB)`,
|
|
673
|
+
// Autorotation runs before this filter. Expand, never shrink, the axis
|
|
674
|
+
// carrying non-square samples, then erase SAR so downstream Spatial
|
|
675
|
+
// receives only truthful square-pixel display dimensions.
|
|
676
|
+
"scale=trunc(iw*max(sar\\,1)/2)*2:trunc(ih*max(1/sar\\,1)/2)*2",
|
|
677
|
+
"setsar=1",
|
|
678
|
+
].join(",");
|
|
679
|
+
const visualInput = animation === undefined
|
|
680
|
+
? [...encoding.inputArgs, "-autorotate", "-i", input, "-map", `0:${plan.video.index}`]
|
|
681
|
+
: ["-f", "concat", "-safe", "0", "-i", await animatedWebpConcat(env, animation, work), "-map", "0:v:0"];
|
|
682
|
+
// The execution format must retain the source's alpha while materializing
|
|
683
|
+
// the program clock. Both encodings publish the same SynchronizedMedia type.
|
|
684
|
+
const encoderArgs = encoding.alpha
|
|
685
|
+
? ["-c:v", "libvpx-vp9", "-pix_fmt", "yuva420p", "-lossless", "1", "-auto-alt-ref", "0"]
|
|
686
|
+
: ["-c:v", "libx264", "-preset", "veryfast", "-pix_fmt", "yuv420p", "-movflags", "+faststart"];
|
|
687
|
+
await runProcess({
|
|
688
|
+
executable: env.ffmpegPath,
|
|
689
|
+
argv: ["-y", ...visualInput, "-an", "-vf", filter,
|
|
690
|
+
"-frames:v", String(plan.frameCount), "-fps_mode", "cfr", ...encoderArgs, output],
|
|
691
|
+
timeoutMs: env.processTimeoutMs,
|
|
692
|
+
maxStdoutBytes: 64 * 1024,
|
|
693
|
+
...(env.sharedLibraryPath === undefined ? {} : { sharedLibraryPath: env.sharedLibraryPath }),
|
|
694
|
+
});
|
|
695
|
+
const inspected = await outputInspection({
|
|
696
|
+
path: output,
|
|
697
|
+
mediaType: outputType,
|
|
698
|
+
ffprobePath: env.ffprobePath,
|
|
699
|
+
timeoutMs: env.processTimeoutMs,
|
|
700
|
+
maxProbeOutputBytes: env.maxProbeOutputBytes,
|
|
701
|
+
...(env.sharedLibraryPath === undefined ? {} : { sharedLibraryPath: env.sharedLibraryPath }),
|
|
702
|
+
});
|
|
703
|
+
const visual = inspected.streams.find((item): item is MediaVideoStream => item.kind === "video");
|
|
704
|
+
assert(visual?.decodedUnitCount === plan.frameCount && visual.role === "moving",
|
|
705
|
+
"Normalized visual frame shape differs from its plan");
|
|
706
|
+
assert(visual.sampleAspectRatio.numerator === 1 && visual.sampleAspectRatio.denominator === 1
|
|
707
|
+
&& visual.rotationDegrees === 0,
|
|
708
|
+
"Normalized visual retains non-square samples or display rotation");
|
|
709
|
+
visualArtifact = await env.artifacts.putFile(output, outputType);
|
|
710
|
+
visualWidth = visual.width;
|
|
711
|
+
visualHeight = visual.height;
|
|
712
|
+
}
|
|
713
|
+
let audioArtifact: BlobRef | undefined;
|
|
714
|
+
if (plan.audio !== undefined) {
|
|
715
|
+
const output = join(work, "audio.wav");
|
|
716
|
+
const filter = [
|
|
717
|
+
"asetpts=PTS-STARTPTS",
|
|
718
|
+
"aresample=48000:async=0:first_pts=0",
|
|
719
|
+
"aformat=sample_rates=48000:channel_layouts=stereo",
|
|
720
|
+
`atrim=start_sample=${plan.audioTrimStartSamples}:end_sample=${plan.audioTrimStartSamples + plan.audioContentSamples}`,
|
|
721
|
+
"asetpts=N/SR/TB",
|
|
722
|
+
`adelay=${plan.audioHeadSamples}S:all=1`,
|
|
723
|
+
`apad=whole_len=${plan.sampleFrames}`,
|
|
724
|
+
`atrim=start_sample=0:end_sample=${plan.sampleFrames}`,
|
|
725
|
+
"asetpts=N/SR/TB",
|
|
726
|
+
].join(",");
|
|
727
|
+
await runProcess({
|
|
728
|
+
executable: env.ffmpegPath,
|
|
729
|
+
argv: ["-y", "-i", input, "-map", `0:${plan.audio.index}`, "-vn", "-af", filter,
|
|
730
|
+
"-c:a", "pcm_s16le", "-ar", "48000", "-ac", "2", output],
|
|
731
|
+
timeoutMs: env.processTimeoutMs,
|
|
732
|
+
maxStdoutBytes: 64 * 1024,
|
|
733
|
+
...(env.sharedLibraryPath === undefined ? {} : { sharedLibraryPath: env.sharedLibraryPath }),
|
|
734
|
+
});
|
|
735
|
+
const inspected = await outputInspection({
|
|
736
|
+
path: output,
|
|
737
|
+
mediaType: "audio/wav",
|
|
738
|
+
ffprobePath: env.ffprobePath,
|
|
739
|
+
timeoutMs: env.processTimeoutMs,
|
|
740
|
+
maxProbeOutputBytes: env.maxProbeOutputBytes,
|
|
741
|
+
...(env.sharedLibraryPath === undefined ? {} : { sharedLibraryPath: env.sharedLibraryPath }),
|
|
742
|
+
});
|
|
743
|
+
const audio = inspected.streams.find((item): item is MediaAudioStream => item.kind === "audio");
|
|
744
|
+
assert(audio?.decodedSampleFrames === plan.sampleFrames && audio.sampleRate === 48_000 && audio.channels === 2,
|
|
745
|
+
"Normalized audio sample shape differs from its plan");
|
|
746
|
+
audioArtifact = await env.artifacts.putFile(output, "audio/wav");
|
|
747
|
+
}
|
|
748
|
+
const media = sealSynchronizedMedia({
|
|
749
|
+
timeline: {
|
|
750
|
+
frameRate: need.frameRate,
|
|
751
|
+
frameCount: plan.frameCount,
|
|
752
|
+
},
|
|
753
|
+
...(visualArtifact === undefined ? {} : {
|
|
754
|
+
visual: {
|
|
755
|
+
artifact: visualArtifact,
|
|
756
|
+
width: visualWidth!,
|
|
757
|
+
height: visualHeight!,
|
|
758
|
+
},
|
|
759
|
+
}),
|
|
760
|
+
...(audioArtifact === undefined ? {} : {
|
|
761
|
+
audio: {
|
|
762
|
+
artifact: audioArtifact,
|
|
763
|
+
},
|
|
764
|
+
}),
|
|
765
|
+
});
|
|
766
|
+
return inlineResult(canonicalize(media));
|
|
767
|
+
} finally {
|
|
768
|
+
await rm(work, { recursive: true, force: true }).catch(() => {});
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
/**
|
|
773
|
+
* Encode authored images into an exact video-only CFR clip. One picture is held for the whole frame
|
|
774
|
+
* count; several are each held for their planned segment, fitted into the first picture's frame
|
|
775
|
+
* and letterboxed on black, then concatenated in order.
|
|
776
|
+
*/
|
|
777
|
+
export async function executeRenderStillVideo(
|
|
778
|
+
env: MediaExecutionEnvironment,
|
|
779
|
+
constraints: CanonicalValue,
|
|
780
|
+
): Promise<MediaOperationResult> {
|
|
781
|
+
const need = renderStillVideoNeed(constraints);
|
|
782
|
+
const work = await mkdtemp(join(tmpdir(), "hypit-media-still-"));
|
|
783
|
+
try {
|
|
784
|
+
const output = join(work, "still.mp4");
|
|
785
|
+
const staged = new Map<string, string>();
|
|
786
|
+
const inputs: string[] = [];
|
|
787
|
+
for (const segment of need.request.segments) {
|
|
788
|
+
const source = segment.source!;
|
|
789
|
+
let path = staged.get(source.resource);
|
|
790
|
+
if (path === undefined) {
|
|
791
|
+
path = join(work, `picture-${staged.size}.image`);
|
|
792
|
+
await stageArtifact(env, source, path);
|
|
793
|
+
staged.set(source.resource, path);
|
|
794
|
+
}
|
|
795
|
+
inputs.push(path);
|
|
796
|
+
}
|
|
797
|
+
const { numerator, denominator } = need.request.frameRate;
|
|
798
|
+
const fps = `${numerator}/${denominator}`;
|
|
799
|
+
const hold = (frames: number): string => [
|
|
800
|
+
"select=eq(n\\,0)",
|
|
801
|
+
"loop=loop=-1:size=1:start=0",
|
|
802
|
+
`trim=start_frame=0:end_frame=${frames}`,
|
|
803
|
+
`setpts=N*${denominator}/(${numerator}*TB)`,
|
|
804
|
+
].join(",");
|
|
805
|
+
let argv: string[];
|
|
806
|
+
if (need.request.segments.length === 1) {
|
|
807
|
+
const filter = `${hold(need.request.frameCount)},pad=ceil(iw/2)*2:ceil(ih/2)*2:(ow-iw)/2:(oh-ih)/2:color=black,setsar=1`;
|
|
808
|
+
argv = ["-y", "-i", inputs[0]!, "-map", "0:v:0", "-an", "-vf", filter];
|
|
809
|
+
} else {
|
|
810
|
+
const first = await outputInspection({
|
|
811
|
+
path: inputs[0]!,
|
|
812
|
+
mediaType: need.request.segments[0]!.source!.mediaType,
|
|
813
|
+
ffprobePath: env.ffprobePath,
|
|
814
|
+
timeoutMs: env.processTimeoutMs,
|
|
815
|
+
maxProbeOutputBytes: env.maxProbeOutputBytes,
|
|
816
|
+
...(env.sharedLibraryPath === undefined ? {} : { sharedLibraryPath: env.sharedLibraryPath }),
|
|
817
|
+
});
|
|
818
|
+
const picture = first.streams.find((item): item is MediaVideoStream => item.kind === "video");
|
|
819
|
+
assert(picture !== undefined, "Still video first picture has no decodable image");
|
|
820
|
+
const width = Math.ceil(picture.width / 2) * 2;
|
|
821
|
+
const height = Math.ceil(picture.height / 2) * 2;
|
|
822
|
+
const chains = need.request.segments.map((segment, index) =>
|
|
823
|
+
`[${index}:v]${hold(segment.endFrameExclusive - segment.startFrame)},`
|
|
824
|
+
+ `scale=${width}:${height}:force_original_aspect_ratio=decrease,pad=${width}:${height}:(ow-iw)/2:(oh-ih)/2:color=black,setsar=1,format=yuv420p[s${index}]`);
|
|
825
|
+
const concat = `${need.request.segments.map((_, index) => `[s${index}]`).join("")}concat=n=${need.request.segments.length}:v=1:a=0[v]`;
|
|
826
|
+
argv = ["-y", ...inputs.flatMap((path) => ["-i", path]), "-filter_complex", `${chains.join(";")};${concat}`, "-map", "[v]", "-an"];
|
|
827
|
+
}
|
|
828
|
+
await runProcess({
|
|
829
|
+
executable: env.ffmpegPath,
|
|
830
|
+
argv: [
|
|
831
|
+
...argv,
|
|
832
|
+
"-frames:v", String(need.request.frameCount), "-r", fps, "-fps_mode", "cfr",
|
|
833
|
+
"-c:v", "libx264", "-preset", "veryfast", "-pix_fmt", "yuv420p",
|
|
834
|
+
"-movflags", "+faststart", output,
|
|
835
|
+
],
|
|
836
|
+
timeoutMs: env.processTimeoutMs,
|
|
837
|
+
maxStdoutBytes: 64 * 1024,
|
|
838
|
+
...(env.sharedLibraryPath === undefined ? {} : { sharedLibraryPath: env.sharedLibraryPath }),
|
|
839
|
+
});
|
|
840
|
+
const inspected = await outputInspection({
|
|
841
|
+
path: output,
|
|
842
|
+
mediaType: "video/mp4",
|
|
843
|
+
ffprobePath: env.ffprobePath,
|
|
844
|
+
timeoutMs: env.processTimeoutMs,
|
|
845
|
+
maxProbeOutputBytes: env.maxProbeOutputBytes,
|
|
846
|
+
...(env.sharedLibraryPath === undefined ? {} : { sharedLibraryPath: env.sharedLibraryPath }),
|
|
847
|
+
});
|
|
848
|
+
const videos = inspected.streams.filter((item): item is MediaVideoStream => item.kind === "video");
|
|
849
|
+
assert(videos.length === 1 && inspected.streams.length === 1
|
|
850
|
+
&& videos[0]!.decodedUnitCount === need.request.frameCount,
|
|
851
|
+
"Still media output differs from its requested frame domain");
|
|
852
|
+
const visual = videos[0]!;
|
|
853
|
+
assert(visual.sampleAspectRatio.numerator === 1 && visual.sampleAspectRatio.denominator === 1
|
|
854
|
+
&& visual.rotationDegrees === 0,
|
|
855
|
+
"Still media output retains non-square samples or display rotation");
|
|
856
|
+
assert(visual.averageFrameRate !== undefined
|
|
857
|
+
&& visual.averageFrameRate.numerator * need.request.frameRate.denominator
|
|
858
|
+
=== need.request.frameRate.numerator * visual.averageFrameRate.denominator,
|
|
859
|
+
"Still media output differs from its requested frame rate");
|
|
860
|
+
return artifactResult(await env.artifacts.putFile(output, "video/mp4"));
|
|
861
|
+
} finally {
|
|
862
|
+
await rm(work, { recursive: true, force: true }).catch(() => {});
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
type TransformPlan = {
|
|
867
|
+
readonly videoFilters: readonly string[];
|
|
868
|
+
readonly audioFilters: readonly string[];
|
|
869
|
+
readonly frameCount: number;
|
|
870
|
+
readonly sampleFrames: number;
|
|
871
|
+
readonly durationSec: number;
|
|
872
|
+
};
|
|
873
|
+
|
|
874
|
+
function decimal(value: number): string {
|
|
875
|
+
assert(Number.isFinite(value), "Media transform produced a non-finite time");
|
|
876
|
+
return value.toPrecision(15);
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
function compileTransformPlan(media: SynchronizedMedia, operations: readonly MediaTransformOperation[]): TransformPlan {
|
|
880
|
+
const rate = media.timeline.frameRate.numerator / media.timeline.frameRate.denominator;
|
|
881
|
+
let durationSec = media.timeline.frameCount / rate;
|
|
882
|
+
const videoFilters: string[] = ["setpts=PTS-STARTPTS"];
|
|
883
|
+
const audioFilters: string[] = ["asetpts=N/SR/TB"];
|
|
884
|
+
for (const [index, operation] of operations.entries()) {
|
|
885
|
+
if (operation.kind === "trim") {
|
|
886
|
+
const start = operation.startSec ?? 0;
|
|
887
|
+
const end = operation.endSec ?? (operation.tailSec === undefined
|
|
888
|
+
? durationSec
|
|
889
|
+
: durationSec - operation.tailSec);
|
|
890
|
+
assert(start >= 0 && start < durationSec,
|
|
891
|
+
`Media transform trim ${index} starts outside its current ${durationSec}s timeline`);
|
|
892
|
+
assert(end > start && end <= durationSec + 1e-9,
|
|
893
|
+
`Media transform trim ${index} ends outside its current ${durationSec}s timeline`);
|
|
894
|
+
const boundedEnd = Math.min(end, durationSec);
|
|
895
|
+
videoFilters.push(`trim=start=${decimal(start)}:end=${decimal(boundedEnd)}`, "setpts=PTS-STARTPTS");
|
|
896
|
+
audioFilters.push(`atrim=start=${decimal(start)}:end=${decimal(boundedEnd)}`, "asetpts=N/SR/TB");
|
|
897
|
+
durationSec = boundedEnd - start;
|
|
898
|
+
continue;
|
|
899
|
+
}
|
|
900
|
+
videoFilters.push(`setpts=PTS/${decimal(operation.rate)}`);
|
|
901
|
+
audioFilters.push(...atempo(operation.rate));
|
|
902
|
+
durationSec /= operation.rate;
|
|
903
|
+
}
|
|
904
|
+
const frameCount = Math.max(1, Math.round(durationSec * rate));
|
|
905
|
+
assert(Number.isSafeInteger(frameCount), "Media transform output frame count exceeds safe arithmetic");
|
|
906
|
+
const sampleFrames = roundPositive(
|
|
907
|
+
BigInt(frameCount) * 48_000n * BigInt(media.timeline.frameRate.denominator),
|
|
908
|
+
BigInt(media.timeline.frameRate.numerator),
|
|
909
|
+
);
|
|
910
|
+
const fps = `${media.timeline.frameRate.numerator}/${media.timeline.frameRate.denominator}`;
|
|
911
|
+
videoFilters.push(
|
|
912
|
+
`fps=fps=${fps}:round=near:start_time=0:eof_action=round`,
|
|
913
|
+
`trim=start_frame=0:end_frame=${frameCount}`,
|
|
914
|
+
`setpts=N*${media.timeline.frameRate.denominator}/(${media.timeline.frameRate.numerator}*TB)`,
|
|
915
|
+
);
|
|
916
|
+
audioFilters.push(
|
|
917
|
+
`apad=whole_len=${sampleFrames}`,
|
|
918
|
+
`atrim=start_sample=0:end_sample=${sampleFrames}`,
|
|
919
|
+
"asetpts=N/SR/TB",
|
|
920
|
+
);
|
|
921
|
+
return { videoFilters, audioFilters, frameCount, sampleFrames, durationSec };
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
/** Ordered trim/retime over one exact synchronized A/V value. */
|
|
925
|
+
export async function executeTransformMedia(
|
|
926
|
+
env: MediaExecutionEnvironment,
|
|
927
|
+
constraints: CanonicalValue,
|
|
928
|
+
): Promise<MediaOperationResult> {
|
|
929
|
+
const need = transformNeed(constraints);
|
|
930
|
+
const media = need.media;
|
|
931
|
+
const plan = compileTransformPlan(media, need.program.operations);
|
|
932
|
+
const work = await mkdtemp(join(tmpdir(), "hypit-media-transform-"));
|
|
933
|
+
try {
|
|
934
|
+
const visualPath = join(work, "visual.mp4");
|
|
935
|
+
const audioPath = join(work, "audio.wav");
|
|
936
|
+
const output = join(work, "transformed.mp4");
|
|
937
|
+
await stageArtifact(env, media.visual!.artifact, visualPath);
|
|
938
|
+
if (media.audio !== undefined) await stageArtifact(env, media.audio.artifact, audioPath);
|
|
939
|
+
const argv = ["-y", "-i", visualPath];
|
|
940
|
+
if (media.audio !== undefined) {
|
|
941
|
+
argv.push(
|
|
942
|
+
"-i", audioPath,
|
|
943
|
+
"-filter_complex",
|
|
944
|
+
`[0:v:0]${plan.videoFilters.join(",")}[video];[1:a:0]${plan.audioFilters.join(",")}[audio]`,
|
|
945
|
+
"-map", "[video]", "-map", "[audio]",
|
|
946
|
+
"-c:a", "aac", "-ar", "48000", "-ac", "2",
|
|
947
|
+
);
|
|
948
|
+
} else {
|
|
949
|
+
argv.push("-map", "0:v:0", "-an", "-vf", plan.videoFilters.join(","));
|
|
950
|
+
}
|
|
951
|
+
argv.push(
|
|
952
|
+
"-frames:v", String(plan.frameCount), "-fps_mode", "cfr",
|
|
953
|
+
"-c:v", "libx264", "-preset", "veryfast", "-pix_fmt", "yuv420p",
|
|
954
|
+
"-movflags", "+faststart", output,
|
|
955
|
+
);
|
|
956
|
+
await runProcess({
|
|
957
|
+
executable: env.ffmpegPath,
|
|
958
|
+
argv,
|
|
959
|
+
timeoutMs: env.processTimeoutMs,
|
|
960
|
+
maxStdoutBytes: 64 * 1024,
|
|
961
|
+
...(env.sharedLibraryPath === undefined ? {} : { sharedLibraryPath: env.sharedLibraryPath }),
|
|
962
|
+
});
|
|
963
|
+
const inspected = await outputInspection({
|
|
964
|
+
path: output,
|
|
965
|
+
mediaType: "video/mp4",
|
|
966
|
+
ffprobePath: env.ffprobePath,
|
|
967
|
+
timeoutMs: env.processTimeoutMs,
|
|
968
|
+
maxProbeOutputBytes: env.maxProbeOutputBytes,
|
|
969
|
+
...(env.sharedLibraryPath === undefined ? {} : { sharedLibraryPath: env.sharedLibraryPath }),
|
|
970
|
+
});
|
|
971
|
+
const videos = inspected.streams.filter((item): item is MediaVideoStream => item.kind === "video");
|
|
972
|
+
const audios = inspected.streams.filter((item): item is MediaAudioStream => item.kind === "audio");
|
|
973
|
+
assert(videos.length === 1 && videos[0]!.role === "moving"
|
|
974
|
+
&& videos[0]!.decodedUnitCount === plan.frameCount,
|
|
975
|
+
"Transformed media video differs from its compiled frame domain");
|
|
976
|
+
assert(audios.length === (media.audio === undefined ? 0 : 1),
|
|
977
|
+
"Transformed media audio presence differs from its synchronized input");
|
|
978
|
+
if (audios[0] !== undefined) {
|
|
979
|
+
assert(audios[0].sampleRate === 48_000 && audios[0].channels === 2,
|
|
980
|
+
"Transformed media audio is not 48 kHz stereo");
|
|
981
|
+
}
|
|
982
|
+
const artifact = await env.artifacts.putFile(output, "video/mp4");
|
|
983
|
+
return artifactResult(artifact);
|
|
984
|
+
} finally {
|
|
985
|
+
await rm(work, { recursive: true, force: true }).catch(() => {});
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
/** Generic model-reference audio: no Narrative, SemanticTake or alignment claim is introduced. */
|
|
990
|
+
export async function executeExtractAudio(
|
|
991
|
+
env: MediaExecutionEnvironment,
|
|
992
|
+
constraints: CanonicalValue,
|
|
993
|
+
): Promise<MediaOperationResult> {
|
|
994
|
+
const need = extractAudioNeed(constraints);
|
|
995
|
+
const work = await mkdtemp(join(tmpdir(), "hypit-media-extract-audio-"));
|
|
996
|
+
try {
|
|
997
|
+
const input = join(work, "source.bin");
|
|
998
|
+
const output = join(work, "audio.wav");
|
|
999
|
+
await stageArtifact(env, need.source, input);
|
|
1000
|
+
await runProcess({
|
|
1001
|
+
executable: env.ffmpegPath,
|
|
1002
|
+
argv: [
|
|
1003
|
+
"-y", "-i", input, "-map", `0:${need.streamIndex}`, "-vn",
|
|
1004
|
+
"-af", "asetpts=PTS-STARTPTS,aresample=48000:async=0:first_pts=0,aformat=sample_rates=48000:channel_layouts=stereo",
|
|
1005
|
+
"-c:a", "pcm_s16le", "-ar", "48000", "-ac", "2", output,
|
|
1006
|
+
],
|
|
1007
|
+
timeoutMs: env.processTimeoutMs,
|
|
1008
|
+
maxStdoutBytes: 64 * 1024,
|
|
1009
|
+
...(env.sharedLibraryPath === undefined ? {} : { sharedLibraryPath: env.sharedLibraryPath }),
|
|
1010
|
+
});
|
|
1011
|
+
const artifact = await env.artifacts.putFile(output, "audio/wav");
|
|
1012
|
+
await assertCanonicalWav({
|
|
1013
|
+
path: output,
|
|
1014
|
+
source: artifact,
|
|
1015
|
+
ffprobePath: env.ffprobePath,
|
|
1016
|
+
timeoutMs: env.processTimeoutMs,
|
|
1017
|
+
maxProbeOutputBytes: env.maxProbeOutputBytes,
|
|
1018
|
+
...(env.sharedLibraryPath === undefined ? {} : { sharedLibraryPath: env.sharedLibraryPath }),
|
|
1019
|
+
});
|
|
1020
|
+
return artifactResult(artifact);
|
|
1021
|
+
} finally {
|
|
1022
|
+
await rm(work, { recursive: true, force: true }).catch(() => {});
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
export async function executeExtractFrame(
|
|
1027
|
+
env: MediaExecutionEnvironment,
|
|
1028
|
+
constraints: CanonicalValue,
|
|
1029
|
+
): Promise<MediaOperationResult> {
|
|
1030
|
+
const need = extractFrameNeed(constraints);
|
|
1031
|
+
const work = await mkdtemp(join(tmpdir(), "hypit-media-extract-frame-"));
|
|
1032
|
+
try {
|
|
1033
|
+
const input = join(work, "source.bin");
|
|
1034
|
+
const output = join(work, "frame.png");
|
|
1035
|
+
await stageArtifact(env, need.source, input);
|
|
1036
|
+
const encoding = await sourceVideoEncoding(env, input, need.streamIndex);
|
|
1037
|
+
const selection = need.at.kind === "first"
|
|
1038
|
+
? "eq(n\\,0)"
|
|
1039
|
+
: need.at.kind === "last"
|
|
1040
|
+
? `eq(n\\,${need.sourceFrameCount - 1})`
|
|
1041
|
+
: need.at.kind === "frame"
|
|
1042
|
+
? `eq(n\\,${need.at.index})`
|
|
1043
|
+
: `gte(t\\,${decimal(need.at.seconds)})`;
|
|
1044
|
+
await runProcess({
|
|
1045
|
+
executable: env.ffmpegPath,
|
|
1046
|
+
argv: [
|
|
1047
|
+
"-y", ...encoding.inputArgs, "-i", input, "-map", `0:${need.streamIndex}`, "-an",
|
|
1048
|
+
"-vf", `setpts=PTS-STARTPTS,select=${selection}`,
|
|
1049
|
+
"-frames:v", "1", "-fps_mode", "vfr", "-c:v", "png", output,
|
|
1050
|
+
],
|
|
1051
|
+
timeoutMs: env.processTimeoutMs,
|
|
1052
|
+
maxStdoutBytes: 64 * 1024,
|
|
1053
|
+
...(env.sharedLibraryPath === undefined ? {} : { sharedLibraryPath: env.sharedLibraryPath }),
|
|
1054
|
+
});
|
|
1055
|
+
const bytes = await readFile(output);
|
|
1056
|
+
assert(bytes.byteLength > 0, "Frame extraction produced no image");
|
|
1057
|
+
const inspected = await outputInspection({
|
|
1058
|
+
path: output,
|
|
1059
|
+
mediaType: "image/png",
|
|
1060
|
+
ffprobePath: env.ffprobePath,
|
|
1061
|
+
timeoutMs: env.processTimeoutMs,
|
|
1062
|
+
maxProbeOutputBytes: env.maxProbeOutputBytes,
|
|
1063
|
+
...(env.sharedLibraryPath === undefined ? {} : { sharedLibraryPath: env.sharedLibraryPath }),
|
|
1064
|
+
});
|
|
1065
|
+
const images = inspected.streams.filter((item): item is MediaVideoStream => item.kind === "video");
|
|
1066
|
+
assert(images.length === 1 && images[0]!.decodedUnitCount === 1,
|
|
1067
|
+
"Frame extraction output must decode to exactly one image");
|
|
1068
|
+
const artifact = await env.artifacts.put(bytes, "image/png");
|
|
1069
|
+
return artifactResult(artifact);
|
|
1070
|
+
} finally {
|
|
1071
|
+
await rm(work, { recursive: true, force: true }).catch(() => {});
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
export async function executeProjectSpeechEvidenceAudio(
|
|
1076
|
+
env: MediaExecutionEnvironment,
|
|
1077
|
+
constraints: CanonicalValue,
|
|
1078
|
+
): Promise<MediaOperationResult> {
|
|
1079
|
+
const need = evidenceAudioNeed(constraints);
|
|
1080
|
+
const work = await mkdtemp(join(tmpdir(), "hypit-media-speech-evidence-"));
|
|
1081
|
+
try {
|
|
1082
|
+
const input = join(work, "speech-master.wav");
|
|
1083
|
+
const output = join(work, "alignment-evidence.wav");
|
|
1084
|
+
await stageArtifact(env, need.source, input);
|
|
1085
|
+
const source = await assertCanonicalWav({
|
|
1086
|
+
path: input,
|
|
1087
|
+
source: need.source,
|
|
1088
|
+
ffprobePath: env.ffprobePath,
|
|
1089
|
+
timeoutMs: env.processTimeoutMs,
|
|
1090
|
+
maxProbeOutputBytes: env.maxProbeOutputBytes,
|
|
1091
|
+
...(env.sharedLibraryPath === undefined ? {} : { sharedLibraryPath: env.sharedLibraryPath }),
|
|
1092
|
+
});
|
|
1093
|
+
assert(source.decodedSampleFrames === need.sourceSampleFrames,
|
|
1094
|
+
"Speech master sample count differs from its ProgramSpace");
|
|
1095
|
+
const filter = [
|
|
1096
|
+
"asetpts=N/SR/TB",
|
|
1097
|
+
"aresample=16000:async=0:first_pts=0",
|
|
1098
|
+
"aformat=sample_rates=16000:channel_layouts=mono",
|
|
1099
|
+
`apad=whole_len=${need.evidenceSampleFrames}`,
|
|
1100
|
+
`atrim=start_sample=0:end_sample=${need.evidenceSampleFrames}`,
|
|
1101
|
+
"asetpts=N/SR/TB",
|
|
1102
|
+
].join(",");
|
|
1103
|
+
await runProcess({
|
|
1104
|
+
executable: env.ffmpegPath,
|
|
1105
|
+
argv: ["-y", "-i", input, "-vn", "-af", filter,
|
|
1106
|
+
"-c:a", "pcm_s16le", "-ar", "16000", "-ac", "1", output],
|
|
1107
|
+
timeoutMs: env.processTimeoutMs,
|
|
1108
|
+
maxStdoutBytes: 64 * 1024,
|
|
1109
|
+
...(env.sharedLibraryPath === undefined ? {} : { sharedLibraryPath: env.sharedLibraryPath }),
|
|
1110
|
+
});
|
|
1111
|
+
const inspected = await outputInspection({
|
|
1112
|
+
path: output,
|
|
1113
|
+
mediaType: "audio/wav",
|
|
1114
|
+
ffprobePath: env.ffprobePath,
|
|
1115
|
+
timeoutMs: env.processTimeoutMs,
|
|
1116
|
+
maxProbeOutputBytes: env.maxProbeOutputBytes,
|
|
1117
|
+
...(env.sharedLibraryPath === undefined ? {} : { sharedLibraryPath: env.sharedLibraryPath }),
|
|
1118
|
+
});
|
|
1119
|
+
const streams = inspected.streams.filter((item): item is MediaAudioStream => item.kind === "audio");
|
|
1120
|
+
assert(streams.length === 1 && inspected.streams.length === 1
|
|
1121
|
+
&& streams[0]!.codecName === "pcm_s16le"
|
|
1122
|
+
&& streams[0]!.sampleRate === 16_000
|
|
1123
|
+
&& streams[0]!.channels === 1
|
|
1124
|
+
&& streams[0]!.decodedSampleFrames === need.evidenceSampleFrames,
|
|
1125
|
+
"Alignment evidence must be exact 16 kHz mono PCM s16");
|
|
1126
|
+
const artifact = await env.artifacts.putFile(output, "audio/wav");
|
|
1127
|
+
const evidence: SpeechEvidenceAudio = sealSpeechEvidenceAudio({
|
|
1128
|
+
artifact,
|
|
1129
|
+
sampleFrames: need.evidenceSampleFrames,
|
|
1130
|
+
});
|
|
1131
|
+
assertSpeechEvidenceAudioIdentity(evidence);
|
|
1132
|
+
return inlineResult(canonicalize(evidence));
|
|
1133
|
+
} finally {
|
|
1134
|
+
await rm(work, { recursive: true, force: true }).catch(() => {});
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
export async function executeRenderTimelineAudio(
|
|
1139
|
+
env: MediaExecutionEnvironment,
|
|
1140
|
+
constraints: CanonicalValue,
|
|
1141
|
+
): Promise<MediaOperationResult> {
|
|
1142
|
+
const need = renderAudioNeed(constraints);
|
|
1143
|
+
const plan: AudioProgramPlan = need.plan;
|
|
1144
|
+
const window = need.range === undefined
|
|
1145
|
+
? { startSample: 0, endSampleExclusive: plan.sampleFrames, sampleFrames: plan.sampleFrames }
|
|
1146
|
+
: mediaFrameRangeSamples(need.range, plan.frameRate);
|
|
1147
|
+
const clips = plan.clips.filter((clip) => clip.targetStartSample < window.endSampleExclusive
|
|
1148
|
+
&& clip.targetEndSampleExclusive > window.startSample);
|
|
1149
|
+
const work = await mkdtemp(join(tmpdir(), "hypit-media-audio-"));
|
|
1150
|
+
try {
|
|
1151
|
+
const artifacts = new Map<string, { source: BlobRef; path: string; inputIndex: number; sampleFrames: number }>();
|
|
1152
|
+
for (const clip of clips) {
|
|
1153
|
+
const existing = artifacts.get(clip.artifact.resource);
|
|
1154
|
+
if (existing !== undefined) {
|
|
1155
|
+
assert(existing.sampleFrames === clip.sourceSampleFrames,
|
|
1156
|
+
`Audio input ${clip.artifact.resource} has conflicting sample counts in one plan`);
|
|
1157
|
+
continue;
|
|
1158
|
+
}
|
|
1159
|
+
const inputIndex = artifacts.size;
|
|
1160
|
+
const path = join(work, `input-${inputIndex}.wav`);
|
|
1161
|
+
await stageArtifact(env, clip.artifact, path);
|
|
1162
|
+
const audio = await assertCanonicalWav({
|
|
1163
|
+
path,
|
|
1164
|
+
source: clip.artifact,
|
|
1165
|
+
ffprobePath: env.ffprobePath,
|
|
1166
|
+
timeoutMs: env.processTimeoutMs,
|
|
1167
|
+
maxProbeOutputBytes: env.maxProbeOutputBytes,
|
|
1168
|
+
...(env.sharedLibraryPath === undefined ? {} : { sharedLibraryPath: env.sharedLibraryPath }),
|
|
1169
|
+
});
|
|
1170
|
+
assert(audio.decodedSampleFrames === clip.sourceSampleFrames,
|
|
1171
|
+
`Audio input ${clip.artifact.resource} sample count differs from its plan`);
|
|
1172
|
+
artifacts.set(clip.artifact.resource, {
|
|
1173
|
+
source: clip.artifact,
|
|
1174
|
+
path,
|
|
1175
|
+
inputIndex,
|
|
1176
|
+
sampleFrames: audio.decodedSampleFrames,
|
|
1177
|
+
});
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
const output = join(work, "program.wav");
|
|
1181
|
+
const argv = ["-y"];
|
|
1182
|
+
for (const item of artifacts.values()) argv.push("-i", item.path);
|
|
1183
|
+
if (clips.length === 0) {
|
|
1184
|
+
argv.push(
|
|
1185
|
+
"-f", "lavfi", "-i", "anullsrc=r=48000:cl=stereo",
|
|
1186
|
+
"-af", `atrim=start_sample=0:end_sample=${window.sampleFrames},asetpts=N/SR/TB`,
|
|
1187
|
+
);
|
|
1188
|
+
} else {
|
|
1189
|
+
const chains = clips.map((clip, index) => {
|
|
1190
|
+
const inputIndex = artifacts.get(clip.artifact.resource)!.inputIndex;
|
|
1191
|
+
return audioClipFilter(clip, inputIndex, index, window);
|
|
1192
|
+
});
|
|
1193
|
+
const labels = clips.map((_clip, index) => `[clip${index}]`).join("");
|
|
1194
|
+
chains.push(
|
|
1195
|
+
`${labels}amix=inputs=${clips.length}:duration=longest:dropout_transition=0:normalize=0,`
|
|
1196
|
+
+ `apad=whole_len=${window.sampleFrames},atrim=start_sample=0:end_sample=${window.sampleFrames},`
|
|
1197
|
+
+ "asetpts=N/SR/TB[out]",
|
|
1198
|
+
);
|
|
1199
|
+
argv.push("-filter_complex", chains.join(";"), "-map", "[out]");
|
|
1200
|
+
}
|
|
1201
|
+
argv.push("-c:a", "pcm_s16le", "-ar", "48000", "-ac", "2", output);
|
|
1202
|
+
await runProcess({
|
|
1203
|
+
executable: env.ffmpegPath,
|
|
1204
|
+
argv,
|
|
1205
|
+
timeoutMs: env.processTimeoutMs,
|
|
1206
|
+
maxStdoutBytes: 64 * 1024,
|
|
1207
|
+
...(env.sharedLibraryPath === undefined ? {} : { sharedLibraryPath: env.sharedLibraryPath }),
|
|
1208
|
+
});
|
|
1209
|
+
const artifact = await env.artifacts.putFile(output, "audio/wav");
|
|
1210
|
+
const audio = await assertCanonicalWav({
|
|
1211
|
+
path: output,
|
|
1212
|
+
source: artifact,
|
|
1213
|
+
ffprobePath: env.ffprobePath,
|
|
1214
|
+
timeoutMs: env.processTimeoutMs,
|
|
1215
|
+
maxProbeOutputBytes: env.maxProbeOutputBytes,
|
|
1216
|
+
...(env.sharedLibraryPath === undefined ? {} : { sharedLibraryPath: env.sharedLibraryPath }),
|
|
1217
|
+
});
|
|
1218
|
+
assert(audio.decodedSampleFrames === window.sampleFrames,
|
|
1219
|
+
"Rendered TimelineAudio sample count differs from its plan");
|
|
1220
|
+
const value: TimelineAudio = sealTimelineAudio({
|
|
1221
|
+
artifact,
|
|
1222
|
+
sampleFrames: window.sampleFrames,
|
|
1223
|
+
});
|
|
1224
|
+
return inlineResult(canonicalize(value));
|
|
1225
|
+
} finally {
|
|
1226
|
+
await rm(work, { recursive: true, force: true }).catch(() => {});
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
export async function executeMuxProgramMedia(
|
|
1231
|
+
env: MediaExecutionEnvironment,
|
|
1232
|
+
constraints: CanonicalValue,
|
|
1233
|
+
): Promise<MediaOperationResult> {
|
|
1234
|
+
const need = muxMediaNeed(constraints);
|
|
1235
|
+
const work = await mkdtemp(join(tmpdir(), "hypit-media-mux-"));
|
|
1236
|
+
try {
|
|
1237
|
+
const visualPath = join(work, "visual.mp4");
|
|
1238
|
+
const audioPath = join(work, "audio.wav");
|
|
1239
|
+
const output = join(work, "final.mp4");
|
|
1240
|
+
await Promise.all([
|
|
1241
|
+
stageArtifact(env, need.visual.artifact, visualPath),
|
|
1242
|
+
stageArtifact(env, need.audio.artifact, audioPath),
|
|
1243
|
+
]);
|
|
1244
|
+
const [visualInspection, audio] = await Promise.all([
|
|
1245
|
+
inspectFile({
|
|
1246
|
+
source: need.visual.artifact,
|
|
1247
|
+
input: visualPath,
|
|
1248
|
+
ffprobePath: env.ffprobePath,
|
|
1249
|
+
timeoutMs: env.processTimeoutMs,
|
|
1250
|
+
maxProbeOutputBytes: env.maxProbeOutputBytes,
|
|
1251
|
+
...(env.sharedLibraryPath === undefined ? {} : { sharedLibraryPath: env.sharedLibraryPath }),
|
|
1252
|
+
}),
|
|
1253
|
+
assertCanonicalWav({
|
|
1254
|
+
path: audioPath,
|
|
1255
|
+
source: need.audio.artifact,
|
|
1256
|
+
ffprobePath: env.ffprobePath,
|
|
1257
|
+
timeoutMs: env.processTimeoutMs,
|
|
1258
|
+
maxProbeOutputBytes: env.maxProbeOutputBytes,
|
|
1259
|
+
...(env.sharedLibraryPath === undefined ? {} : { sharedLibraryPath: env.sharedLibraryPath }),
|
|
1260
|
+
}),
|
|
1261
|
+
]);
|
|
1262
|
+
const visualStreams = visualInspection.streams.filter((item): item is MediaVideoStream => item.kind === "video");
|
|
1263
|
+
assert(visualStreams.length === 1 && visualInspection.streams.length === 1,
|
|
1264
|
+
"RenderedVisual Artifact must contain exactly one silent video stream");
|
|
1265
|
+
const visual = visualStreams[0]!;
|
|
1266
|
+
assert(visual.role === "moving" && visual.timingStatus === "admissible"
|
|
1267
|
+
&& visual.decodedUnitCount === need.visual.frameCount
|
|
1268
|
+
&& visual.width === need.visual.canvas.width && visual.height === need.visual.canvas.height,
|
|
1269
|
+
"RenderedVisual bytes differ from their declared frame domain");
|
|
1270
|
+
assert(audio.decodedSampleFrames === need.audio.sampleFrames,
|
|
1271
|
+
"TimelineAudio bytes differ from their declared sample domain");
|
|
1272
|
+
await runProcess({
|
|
1273
|
+
executable: env.ffmpegPath,
|
|
1274
|
+
argv: [
|
|
1275
|
+
"-y", "-i", visualPath, "-i", audioPath,
|
|
1276
|
+
"-map", `0:${visual.index}`, "-map", "1:0",
|
|
1277
|
+
"-c:v", "copy", "-c:a", "aac", "-ar", "48000", "-ac", "2",
|
|
1278
|
+
"-frames:v", String(need.visual.frameCount),
|
|
1279
|
+
"-movflags", "+faststart", output,
|
|
1280
|
+
],
|
|
1281
|
+
timeoutMs: env.processTimeoutMs,
|
|
1282
|
+
maxStdoutBytes: 64 * 1024,
|
|
1283
|
+
...(env.sharedLibraryPath === undefined ? {} : { sharedLibraryPath: env.sharedLibraryPath }),
|
|
1284
|
+
});
|
|
1285
|
+
const finalInspection = await outputInspection({
|
|
1286
|
+
path: output,
|
|
1287
|
+
mediaType: "video/mp4",
|
|
1288
|
+
ffprobePath: env.ffprobePath,
|
|
1289
|
+
timeoutMs: env.processTimeoutMs,
|
|
1290
|
+
maxProbeOutputBytes: env.maxProbeOutputBytes,
|
|
1291
|
+
...(env.sharedLibraryPath === undefined ? {} : { sharedLibraryPath: env.sharedLibraryPath }),
|
|
1292
|
+
});
|
|
1293
|
+
const finalVideo = finalInspection.streams.filter((item): item is MediaVideoStream => item.kind === "video");
|
|
1294
|
+
const finalAudio = finalInspection.streams.filter((item): item is MediaAudioStream => item.kind === "audio");
|
|
1295
|
+
assert(finalVideo.length === 1 && finalAudio.length === 1 && finalInspection.streams.length === 2,
|
|
1296
|
+
"Final mux must contain exactly one video and one audio stream");
|
|
1297
|
+
assert(finalVideo[0]!.timingStatus === "admissible"
|
|
1298
|
+
&& finalVideo[0]!.decodedUnitCount === need.visual.frameCount,
|
|
1299
|
+
"Final mux video frame count differs from RenderedVisual");
|
|
1300
|
+
assert(finalAudio[0]!.timingStatus === "admissible"
|
|
1301
|
+
&& finalAudio[0]!.sampleRate === 48_000 && finalAudio[0]!.channels === 2,
|
|
1302
|
+
"Final mux audio shape differs from TimelineAudio");
|
|
1303
|
+
assert(finalVideo[0]!.startPts !== undefined && finalAudio[0]!.startPts !== undefined
|
|
1304
|
+
&& compareTimestamp(finalVideo[0]!.startPts, finalAudio[0]!.startPts) === 0,
|
|
1305
|
+
"Final mux audio and video do not share one presentation origin");
|
|
1306
|
+
const finalAudioSpan = duration(finalAudio[0]!);
|
|
1307
|
+
assert(roundPositive(finalAudioSpan.numerator * 48_000n, finalAudioSpan.denominator)
|
|
1308
|
+
=== need.audio.sampleFrames,
|
|
1309
|
+
"Final mux audio presentation span differs from TimelineAudio");
|
|
1310
|
+
const artifact = await env.artifacts.putFile(output, "video/mp4");
|
|
1311
|
+
const value: MuxedMedia = sealMuxedMedia({
|
|
1312
|
+
frameRate: need.visual.frameRate,
|
|
1313
|
+
frameCount: need.visual.frameCount,
|
|
1314
|
+
canvas: need.visual.canvas,
|
|
1315
|
+
presentationSampleFrames: need.audio.sampleFrames,
|
|
1316
|
+
artifact,
|
|
1317
|
+
});
|
|
1318
|
+
return inlineResult(canonicalize(value));
|
|
1319
|
+
} finally {
|
|
1320
|
+
await rm(work, { recursive: true, force: true }).catch(() => {});
|
|
1321
|
+
}
|
|
1322
|
+
}
|