fundus 0.0.2 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +73 -0
- package/build/client/_app/immutable/assets/0.DLhIsYA_.css.br +0 -0
- package/build/client/_app/immutable/assets/0.DLhIsYA_.css.gz +0 -0
- package/build/client/_app/immutable/assets/2.ohi-TClG.css +1 -0
- package/build/client/_app/immutable/assets/2.ohi-TClG.css.br +0 -0
- package/build/client/_app/immutable/assets/2.ohi-TClG.css.gz +0 -0
- package/build/client/_app/immutable/assets/3.JwgtcTEd.css +1 -0
- package/build/client/_app/immutable/assets/3.JwgtcTEd.css.br +0 -0
- package/build/client/_app/immutable/assets/3.JwgtcTEd.css.gz +0 -0
- package/build/client/_app/immutable/assets/4.p004hmEN.css +1 -0
- package/build/client/_app/immutable/assets/4.p004hmEN.css.br +0 -0
- package/build/client/_app/immutable/assets/4.p004hmEN.css.gz +0 -0
- package/build/client/_app/immutable/assets/ConfirmDialog.BdKlitwi.css +1 -0
- package/build/client/_app/immutable/assets/ConfirmDialog.BdKlitwi.css.br +0 -0
- package/build/client/_app/immutable/assets/ConfirmDialog.BdKlitwi.css.gz +0 -0
- package/build/client/_app/immutable/assets/LibraryContextMenu.CMefGNE2.css +1 -0
- package/build/client/_app/immutable/assets/LibraryContextMenu.CMefGNE2.css.br +0 -0
- package/build/client/_app/immutable/assets/LibraryContextMenu.CMefGNE2.css.gz +0 -0
- package/build/client/_app/immutable/chunks/Bjy-W4x2.js.br +0 -0
- package/build/client/_app/immutable/chunks/Bjy-W4x2.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CNRz3sSw.js.br +0 -0
- package/build/client/_app/immutable/chunks/CNRz3sSw.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CO4RBJfa.js.br +0 -0
- package/build/client/_app/immutable/chunks/CO4RBJfa.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DOpBpjxS.js +108 -0
- package/build/client/_app/immutable/chunks/DOpBpjxS.js.br +0 -0
- package/build/client/_app/immutable/chunks/DOpBpjxS.js.gz +0 -0
- package/{dist/editor/_app/immutable/chunks/BfPE5wdt.js → build/client/_app/immutable/chunks/DVEQFclp.js} +1 -1
- package/build/client/_app/immutable/chunks/DVEQFclp.js.br +1 -0
- package/build/client/_app/immutable/chunks/DVEQFclp.js.gz +0 -0
- package/{dist/editor/_app/immutable/chunks/DjvOiDmq.js → build/client/_app/immutable/chunks/DiFoWL2c.js} +1 -1
- package/build/client/_app/immutable/chunks/DiFoWL2c.js.br +0 -0
- package/build/client/_app/immutable/chunks/DiFoWL2c.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DkADHpZF.js +1 -0
- package/build/client/_app/immutable/chunks/DkADHpZF.js.br +0 -0
- package/build/client/_app/immutable/chunks/DkADHpZF.js.gz +0 -0
- package/build/client/_app/immutable/chunks/xihTtKlq.js.br +1 -0
- package/build/client/_app/immutable/chunks/xihTtKlq.js.gz +0 -0
- package/{dist/editor/_app/immutable/entry/app.BkqbfI8n.js → build/client/_app/immutable/entry/app.CKgtYXQb.js} +2 -2
- package/build/client/_app/immutable/entry/app.CKgtYXQb.js.br +0 -0
- package/build/client/_app/immutable/entry/app.CKgtYXQb.js.gz +0 -0
- package/build/client/_app/immutable/entry/start.CYZu5qE1.js +1 -0
- package/build/client/_app/immutable/entry/start.CYZu5qE1.js.br +0 -0
- package/build/client/_app/immutable/entry/start.CYZu5qE1.js.gz +0 -0
- package/{dist/editor/_app/immutable/nodes/0.xdMI5GEU.js → build/client/_app/immutable/nodes/0.I_1SApYQ.js} +1 -1
- package/build/client/_app/immutable/nodes/0.I_1SApYQ.js.br +0 -0
- package/build/client/_app/immutable/nodes/0.I_1SApYQ.js.gz +0 -0
- package/{dist/editor/_app/immutable/nodes/1.D9p2tfoJ.js → build/client/_app/immutable/nodes/1.v6eUTnWq.js} +1 -1
- package/build/client/_app/immutable/nodes/1.v6eUTnWq.js.br +2 -0
- package/build/client/_app/immutable/nodes/1.v6eUTnWq.js.gz +0 -0
- package/build/client/_app/immutable/nodes/2.DoA7Dfl-.js +3 -0
- package/build/client/_app/immutable/nodes/2.DoA7Dfl-.js.br +0 -0
- package/build/client/_app/immutable/nodes/2.DoA7Dfl-.js.gz +0 -0
- package/build/client/_app/immutable/nodes/3.C-DO70Bs.js +1 -0
- package/build/client/_app/immutable/nodes/3.C-DO70Bs.js.br +0 -0
- package/build/client/_app/immutable/nodes/3.C-DO70Bs.js.gz +0 -0
- package/build/client/_app/immutable/nodes/4.CALOuSyz.js +2 -0
- package/build/client/_app/immutable/nodes/4.CALOuSyz.js.br +0 -0
- package/build/client/_app/immutable/nodes/4.CALOuSyz.js.gz +0 -0
- package/build/client/_app/version.json +1 -0
- package/build/client/_app/version.json.br +0 -0
- package/build/client/_app/version.json.gz +0 -0
- package/build/env.js +100 -0
- package/build/env.js.map +1 -0
- package/build/handler.js +22 -0
- package/build/handler.js.map +1 -0
- package/build/index.js +145 -0
- package/build/index.js.map +1 -0
- package/build/server/chunks/chunks/context.js-BUZqFfWG.js +429 -0
- package/build/server/chunks/chunks/context.js-BUZqFfWG.js.map +1 -0
- package/build/server/chunks/chunks/core.js-xfrkaQpS.js +458 -0
- package/build/server/chunks/chunks/core.js-xfrkaQpS.js.map +1 -0
- package/build/server/chunks/chunks/env.js-l0sNRNKZ.js +2 -0
- package/build/server/chunks/chunks/env.js-l0sNRNKZ.js.map +1 -0
- package/build/server/chunks/chunks/exports.js-C0rwMMtk.js +152 -0
- package/build/server/chunks/chunks/exports.js-C0rwMMtk.js.map +1 -0
- package/build/server/chunks/chunks/folder-path.js-ytx9ucgg.js +64 -0
- package/build/server/chunks/chunks/folder-path.js-ytx9ucgg.js.map +1 -0
- package/build/server/chunks/chunks/http.js-DvopzwKh.js +100 -0
- package/build/server/chunks/chunks/http.js-DvopzwKh.js.map +1 -0
- package/build/server/chunks/chunks/internal.js-DGo2jPcg.js +5753 -0
- package/build/server/chunks/chunks/internal.js-DGo2jPcg.js.map +1 -0
- package/build/server/chunks/chunks/internal2.js-BlXt2rfE.js +22 -0
- package/build/server/chunks/chunks/internal2.js-BlXt2rfE.js.map +1 -0
- package/{dist/operations-DYM-5KVl.js → build/server/chunks/chunks/library-store.js-ZLudCJlT.js} +463 -492
- package/build/server/chunks/chunks/library-store.js-ZLudCJlT.js.map +1 -0
- package/build/server/chunks/chunks/manifest-appearance.js-DTk355LQ.js +146 -0
- package/build/server/chunks/chunks/manifest-appearance.js-DTk355LQ.js.map +1 -0
- package/build/server/chunks/chunks/routing.js-y-tryuip.js +213 -0
- package/build/server/chunks/chunks/routing.js-y-tryuip.js.map +1 -0
- package/build/server/chunks/chunks/shared.js-B1gC9MiJ.js +255 -0
- package/build/server/chunks/chunks/shared.js-B1gC9MiJ.js.map +1 -0
- package/build/server/chunks/chunks/utils.js-DnEJ3UjO.js +1048 -0
- package/build/server/chunks/chunks/utils.js-DnEJ3UjO.js.map +1 -0
- package/build/server/chunks/entries/endpoints/api/_...path_/_server.ts.js-Cq5gXVQT.js +27 -0
- package/build/server/chunks/entries/endpoints/api/_...path_/_server.ts.js-Cq5gXVQT.js.map +1 -0
- package/build/server/chunks/entries/endpoints/api/assets/_id_/_server.ts.js-RXbedi8q.js +24 -0
- package/build/server/chunks/entries/endpoints/api/assets/_id_/_server.ts.js-RXbedi8q.js.map +1 -0
- package/build/server/chunks/entries/endpoints/api/assets/_server.ts.js-D7Qae9vU.js +45 -0
- package/build/server/chunks/entries/endpoints/api/assets/_server.ts.js-D7Qae9vU.js.map +1 -0
- package/build/server/chunks/entries/endpoints/api/assets/delete/_server.ts.js-DtO20kgB.js +31 -0
- package/build/server/chunks/entries/endpoints/api/assets/delete/_server.ts.js-DtO20kgB.js.map +1 -0
- package/build/server/chunks/entries/endpoints/api/assets/move/_server.ts.js-BUebPbd5.js +31 -0
- package/build/server/chunks/entries/endpoints/api/assets/move/_server.ts.js-BUebPbd5.js.map +1 -0
- package/build/server/chunks/entries/endpoints/api/folders/_path_/_server.ts.js-BjT2qPMT.js +38 -0
- package/build/server/chunks/entries/endpoints/api/folders/_path_/_server.ts.js-BjT2qPMT.js.map +1 -0
- package/build/server/chunks/entries/endpoints/api/folders/_server.ts.js-DYRLsoT7.js +31 -0
- package/build/server/chunks/entries/endpoints/api/folders/_server.ts.js-DYRLsoT7.js.map +1 -0
- package/build/server/chunks/entries/endpoints/api/manifests/_name_/_server.ts.js-BbN2fGNN.js +24 -0
- package/build/server/chunks/entries/endpoints/api/manifests/_name_/_server.ts.js-BbN2fGNN.js.map +1 -0
- package/build/server/chunks/entries/endpoints/api/manifests/_server.ts.js-b_MKZ41b.js +31 -0
- package/build/server/chunks/entries/endpoints/api/manifests/_server.ts.js-b_MKZ41b.js.map +1 -0
- package/build/server/chunks/entries/endpoints/api/state/_server.ts.js-evEfMpvn.js +24 -0
- package/build/server/chunks/entries/endpoints/api/state/_server.ts.js-evEfMpvn.js.map +1 -0
- package/build/server/chunks/entries/endpoints/files/_...path_/_server.ts.js-C04XE_1w.js +27 -0
- package/build/server/chunks/entries/endpoints/files/_...path_/_server.ts.js-C04XE_1w.js.map +1 -0
- package/build/server/chunks/entries/endpoints/files/proxy/_...path_/_server.ts.js-DB8TdoEv.js +24 -0
- package/build/server/chunks/entries/endpoints/files/proxy/_...path_/_server.ts.js-DB8TdoEv.js.map +1 -0
- package/build/server/chunks/entries/endpoints/files/raw/_...path_/_server.ts.js-BrpaijZO.js +24 -0
- package/build/server/chunks/entries/endpoints/files/raw/_...path_/_server.ts.js-BrpaijZO.js.map +1 -0
- package/build/server/chunks/entries/hooks.server.js-BJJwZW_I.js +23 -0
- package/build/server/chunks/entries/hooks.server.js-BJJwZW_I.js.map +1 -0
- package/build/server/chunks/handler-DnbS2t1B.js +1475 -0
- package/build/server/chunks/handler-DnbS2t1B.js.map +1 -0
- package/build/server/chunks/index.js-g77q3Boj.js +3789 -0
- package/build/server/chunks/index.js-g77q3Boj.js.map +1 -0
- package/build/server/chunks/manifest.js-qmEXbstS.js +149 -0
- package/build/server/chunks/manifest.js-qmEXbstS.js.map +1 -0
- package/build/server/chunks/nodes/0.js-Bk6IZ-vD.js +11 -0
- package/build/server/chunks/nodes/0.js-Bk6IZ-vD.js.map +1 -0
- package/build/server/chunks/nodes/1.js-H-qwo4GJ.js +7 -0
- package/build/server/chunks/nodes/1.js-H-qwo4GJ.js.map +1 -0
- package/build/server/chunks/nodes/2.js-DRpXVLwp.js +7 -0
- package/build/server/chunks/nodes/2.js-DRpXVLwp.js.map +1 -0
- package/build/server/chunks/nodes/3.js-CDEzNFeU.js +7 -0
- package/build/server/chunks/nodes/3.js-CDEzNFeU.js.map +1 -0
- package/build/server/chunks/nodes/4.js-BKL7LSSm.js +7 -0
- package/build/server/chunks/nodes/4.js-BKL7LSSm.js.map +1 -0
- package/build/shims.js +33 -0
- package/build/shims.js.map +1 -0
- package/dist/cli.d.ts +2 -1
- package/dist/cli.js +116 -1336
- package/dist/config.d.ts +3 -2
- package/dist/config.js +2 -2
- package/dist/core/api-types.d.ts +141 -0
- package/dist/core/api-types.js +1 -0
- package/dist/core/asset-id.d.ts +28 -0
- package/dist/core/asset-id.js +59 -0
- package/dist/core/asset-reference.d.ts +23 -0
- package/dist/core/asset-reference.js +15 -0
- package/dist/core/canonicalize.d.ts +9 -0
- package/dist/core/canonicalize.js +36 -0
- package/dist/core/chroma-key.d.ts +30 -0
- package/dist/core/chroma-key.js +55 -0
- package/dist/core/config.d.ts +35 -0
- package/dist/core/config.js +4 -0
- package/dist/core/folder-path.d.ts +19 -0
- package/dist/core/folder-path.js +86 -0
- package/dist/core/index.d.ts +33 -499
- package/dist/core/index.js +17 -962
- package/dist/core/ingest.d.ts +24 -0
- package/dist/core/ingest.js +42 -0
- package/dist/core/library.d.ts +54 -0
- package/dist/core/library.js +138 -0
- package/dist/core/manifest-appearance.d.ts +35 -0
- package/dist/core/manifest-appearance.js +123 -0
- package/dist/core/manifest-entry.d.ts +104 -0
- package/dist/core/manifest-entry.js +1 -0
- package/dist/core/manifest-membership.d.ts +56 -0
- package/dist/core/manifest-membership.js +54 -0
- package/dist/core/plugins/audio.d.ts +26 -0
- package/dist/core/plugins/audio.js +44 -0
- package/dist/core/plugins/chroma-key-video.d.ts +29 -0
- package/dist/core/plugins/chroma-key-video.js +69 -0
- package/dist/core/plugins/contract.d.ts +35 -0
- package/dist/core/plugins/contract.js +1 -0
- package/dist/core/plugins/image.d.ts +30 -0
- package/dist/core/plugins/image.js +45 -0
- package/dist/core/plugins/registry.d.ts +16 -0
- package/dist/core/plugins/registry.js +52 -0
- package/dist/core/plugins/slice.d.ts +22 -0
- package/dist/core/plugins/slice.js +60 -0
- package/dist/core/plugins/video.d.ts +45 -0
- package/dist/core/plugins/video.js +77 -0
- package/dist/core/reference-graph.d.ts +41 -0
- package/dist/core/reference-graph.js +138 -0
- package/dist/core/slice-data.d.ts +61 -0
- package/dist/core/slice-data.js +49 -0
- package/dist/runtime/chroma-key-gl.d.ts +40 -22
- package/dist/runtime/chroma-key-gl.js +189 -183
- package/dist/runtime/components/ChromaKeyVideo.svelte +3 -3
- package/dist/runtime/components/ChromaKeyVideo.svelte.d.ts +21 -0
- package/dist/runtime/components/Image.svelte +1 -1
- package/dist/runtime/components/Image.svelte.d.ts +9 -0
- package/dist/runtime/components/Slice.svelte +1 -1
- package/dist/runtime/components/Slice.svelte.d.ts +19 -0
- package/dist/runtime/components/SliceCanvas.svelte +3 -3
- package/dist/runtime/components/SliceCanvas.svelte.d.ts +10 -0
- package/dist/runtime/components/SliceDom.svelte +3 -3
- package/dist/runtime/components/SliceDom.svelte.d.ts +10 -0
- package/dist/runtime/components/Video.svelte +2 -2
- package/dist/runtime/components/Video.svelte.d.ts +18 -0
- package/dist/runtime/geometry.d.ts +55 -35
- package/dist/runtime/geometry.js +186 -191
- package/dist/runtime/index.d.ts +9 -9
- package/dist/runtime/index.js +9 -9
- package/dist/runtime/kind-handlers.d.ts +33 -30
- package/dist/runtime/kind-handlers.js +85 -67
- package/dist/runtime/manifest.d.ts +53 -47
- package/dist/runtime/manifest.js +133 -113
- package/dist/runtime/observe-border-box-size.d.ts +1 -4
- package/dist/runtime/observe-border-box-size.js +9 -10
- package/dist/runtime/preload-registry.d.ts +22 -8
- package/dist/runtime/preload-registry.js +65 -62
- package/dist/server/build.d.ts +8 -0
- package/dist/server/build.js +25 -0
- package/dist/server/check.d.ts +24 -0
- package/dist/server/check.js +199 -0
- package/dist/server/cli-commands.d.ts +25 -0
- package/dist/server/cli-commands.js +1178 -0
- package/dist/server/cli-init.d.ts +3 -0
- package/dist/server/cli-init.js +53 -0
- package/dist/server/cli-io.d.ts +46 -0
- package/dist/server/cli-io.js +105 -0
- package/dist/server/codegen.d.ts +58 -0
- package/dist/server/codegen.js +185 -0
- package/dist/server/config-loader.d.ts +35 -0
- package/dist/server/config-loader.js +79 -0
- package/dist/server/context.d.ts +8 -0
- package/dist/server/context.js +19 -0
- package/dist/server/folder-filesystem.d.ts +7 -0
- package/dist/server/folder-filesystem.js +39 -0
- package/dist/server/http.d.ts +12 -0
- package/dist/server/http.js +100 -0
- package/dist/server/index.d.ts +17 -0
- package/dist/server/index.js +9 -0
- package/dist/server/job-hash.d.ts +10 -0
- package/dist/server/job-hash.js +23 -0
- package/dist/server/library-store.d.ts +82 -0
- package/dist/server/library-store.js +763 -0
- package/dist/server/operations.d.ts +87 -0
- package/dist/server/operations.js +427 -0
- package/dist/server/plugins/audio.d.ts +8 -0
- package/dist/server/plugins/audio.js +41 -0
- package/dist/server/plugins/chroma-key-video.d.ts +9 -0
- package/dist/server/plugins/chroma-key-video.js +40 -0
- package/dist/server/plugins/config.d.ts +13 -0
- package/dist/server/plugins/config.js +32 -0
- package/dist/server/plugins/contract.d.ts +114 -0
- package/dist/server/plugins/contract.js +16 -0
- package/dist/server/plugins/image.d.ts +9 -0
- package/dist/server/plugins/image.js +43 -0
- package/dist/server/plugins/slice.d.ts +15 -0
- package/dist/server/plugins/slice.js +76 -0
- package/dist/server/plugins/video.d.ts +7 -0
- package/dist/server/plugins/video.js +54 -0
- package/dist/server/plugins.d.ts +23 -0
- package/dist/server/plugins.js +35 -0
- package/dist/server/processing/audio-processing.d.ts +17 -0
- package/dist/server/processing/audio-processing.js +46 -0
- package/dist/server/processing/ffmpeg.d.ts +9 -0
- package/dist/server/processing/ffmpeg.js +50 -0
- package/dist/server/processing/image-processing.d.ts +16 -0
- package/dist/server/processing/image-processing.js +26 -0
- package/dist/server/processing/media-probe.d.ts +15 -0
- package/dist/server/processing/media-probe.js +27 -0
- package/dist/server/processing/processor.d.ts +50 -0
- package/dist/server/processing/processor.js +158 -0
- package/dist/server/processing/video-processing.d.ts +35 -0
- package/dist/server/processing/video-processing.js +87 -0
- package/dist/server/server.d.ts +13 -0
- package/dist/server/server.js +57 -0
- package/dist/server/start.d.ts +13 -0
- package/dist/server/start.js +80 -0
- package/package.json +31 -16
- package/dist/build-CZYFLLvl.js +0 -191
- package/dist/core/image-Dqmz8FXS.js +0 -54
- package/dist/core/reference-graph-CjuHbLDw.d.ts +0 -280
- package/dist/core/testing.d.ts +0 -27
- package/dist/core/testing.js +0 -80
- package/dist/editor/200.html +0 -37
- package/dist/editor/_app/immutable/assets/2.DIbbW7U1.css +0 -1
- package/dist/editor/_app/immutable/assets/3.C-9vFQ9A.css +0 -1
- package/dist/editor/_app/immutable/assets/4.BS8ElpOV.css +0 -1
- package/dist/editor/_app/immutable/assets/ConfirmDialog.CHEISE-L.css +0 -1
- package/dist/editor/_app/immutable/assets/LibraryContextMenu.CDjCmdV-.css +0 -1
- package/dist/editor/_app/immutable/chunks/Cx6qtxJg.js +0 -1
- package/dist/editor/_app/immutable/chunks/DjdrdRzT.js +0 -1
- package/dist/editor/_app/immutable/chunks/n7mvYc42.js +0 -5
- package/dist/editor/_app/immutable/entry/start.C8HHOLly.js +0 -1
- package/dist/editor/_app/immutable/nodes/2.BrA0Hyzu.js +0 -3
- package/dist/editor/_app/immutable/nodes/3.WO7tZH_d.js +0 -104
- package/dist/editor/_app/immutable/nodes/4.DQtx17aW.js +0 -2
- package/dist/editor/_app/version.json +0 -1
- package/dist/index.d.ts +0 -370
- package/dist/index.js +0 -4
- package/dist/start-Bhnqt9Dc.js +0 -279
- package/dist/start-D2IXOrni.js +0 -2
- /package/{dist/editor → build/client}/_app/immutable/assets/0.DLhIsYA_.css +0 -0
- /package/{dist/editor → build/client}/_app/immutable/chunks/Bjy-W4x2.js +0 -0
- /package/{dist/editor → build/client}/_app/immutable/chunks/CNRz3sSw.js +0 -0
- /package/{dist/editor → build/client}/_app/immutable/chunks/CO4RBJfa.js +0 -0
- /package/{dist/editor → build/client}/_app/immutable/chunks/xihTtKlq.js +0 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/** Built-in presets, always available; host config merges over them. */
|
|
2
|
+
export const AUDIO_BUILT_IN_PRESETS = {
|
|
3
|
+
default: { codec: 'opus', bitrateKbps: 96, container: 'webm' }
|
|
4
|
+
};
|
|
5
|
+
/** Each codec's muxable container — a mismatch is a config error, not an ffmpeg one. */
|
|
6
|
+
const CONTAINER_BY_CODEC = {
|
|
7
|
+
opus: 'webm',
|
|
8
|
+
aac: 'm4a'
|
|
9
|
+
};
|
|
10
|
+
export function validateAudioPreset(name, value) {
|
|
11
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
12
|
+
throw new Error(`Audio preset "${name}" must be an object.`);
|
|
13
|
+
}
|
|
14
|
+
const record = value;
|
|
15
|
+
if (record.codec !== 'opus' && record.codec !== 'aac') {
|
|
16
|
+
throw new Error(`Audio preset "${name}" needs a "codec" of 'opus' | 'aac'.`);
|
|
17
|
+
}
|
|
18
|
+
if (typeof record.bitrateKbps !== 'number' ||
|
|
19
|
+
!Number.isInteger(record.bitrateKbps) ||
|
|
20
|
+
record.bitrateKbps <= 0) {
|
|
21
|
+
throw new Error(`Audio preset "${name}" needs a positive integer "bitrateKbps".`);
|
|
22
|
+
}
|
|
23
|
+
const expectedContainer = CONTAINER_BY_CODEC[record.codec];
|
|
24
|
+
if (record.container !== expectedContainer) {
|
|
25
|
+
throw new Error(`Audio preset "${name}": codec '${record.codec}' requires container '${expectedContainer}'.`);
|
|
26
|
+
}
|
|
27
|
+
return { codec: record.codec, bitrateKbps: record.bitrateKbps, container: expectedContainer };
|
|
28
|
+
}
|
|
29
|
+
export const AUDIO_EXTENSIONS = ['mp3', 'wav', 'm4a', 'ogg', 'flac'];
|
|
30
|
+
export const audioPlugin = {
|
|
31
|
+
type: 'audio',
|
|
32
|
+
accepts: [...AUDIO_EXTENSIONS],
|
|
33
|
+
defaultParameters: () => ({ preset: 'default' }),
|
|
34
|
+
validateParameters(value) {
|
|
35
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
36
|
+
throw new Error('Audio parameters must be an object.');
|
|
37
|
+
}
|
|
38
|
+
const record = value;
|
|
39
|
+
if (typeof record.preset !== 'string' || record.preset.length === 0) {
|
|
40
|
+
throw new Error('Audio parameters need a non-empty string "preset".');
|
|
41
|
+
}
|
|
42
|
+
return { preset: record.preset };
|
|
43
|
+
}
|
|
44
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chroma-key-video plugin (core part): a greenscreen video delivered through
|
|
3
|
+
* the video plugin's transcode pipeline, plus the keying data the runtime's
|
|
4
|
+
* WebGL renderer needs — chroma-key parameters and two optional image
|
|
5
|
+
* references: a grayscale shape `mask` multiplied onto the keyed
|
|
6
|
+
* alpha, and a static `fallback` image shown when WebGL2 is unavailable.
|
|
7
|
+
*
|
|
8
|
+
* The keying values and references are entry-only data: they travel into the
|
|
9
|
+
* generated manifest entry but never touch the ffmpeg job, so
|
|
10
|
+
* `processingParameters` narrows the job-hash input to the transcode inputs
|
|
11
|
+
* (preset + maxWidth) — retuning a slider must not re-transcode the proxy.
|
|
12
|
+
*/
|
|
13
|
+
import { type AssetReference } from '../asset-reference.js';
|
|
14
|
+
import type { ChromaKeyParameters } from '../chroma-key.js';
|
|
15
|
+
import type { AssetTypePluginDescriptor } from './contract.js';
|
|
16
|
+
import type { VideoParameters, VideoPluginConfig } from './video.js';
|
|
17
|
+
export interface ChromaKeyVideoParameters extends VideoParameters {
|
|
18
|
+
chromaKey: ChromaKeyParameters;
|
|
19
|
+
/** Grayscale shape mask multiplied onto the keyed alpha (an `image` asset). */
|
|
20
|
+
mask?: AssetReference;
|
|
21
|
+
/** Static image rendered when WebGL2 is unavailable (an `image` asset). */
|
|
22
|
+
fallback?: AssetReference;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Shares the video plugin's preset namespace (chroma-key-video builds
|
|
26
|
+
* on video's processing) — it has no config namespace of its own in M3.
|
|
27
|
+
*/
|
|
28
|
+
export type ChromaKeyVideoPluginConfig = VideoPluginConfig;
|
|
29
|
+
export declare const chromaKeyVideoPlugin: AssetTypePluginDescriptor<ChromaKeyVideoParameters, ChromaKeyVideoPluginConfig>;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chroma-key-video plugin (core part): a greenscreen video delivered through
|
|
3
|
+
* the video plugin's transcode pipeline, plus the keying data the runtime's
|
|
4
|
+
* WebGL renderer needs — chroma-key parameters and two optional image
|
|
5
|
+
* references: a grayscale shape `mask` multiplied onto the keyed
|
|
6
|
+
* alpha, and a static `fallback` image shown when WebGL2 is unavailable.
|
|
7
|
+
*
|
|
8
|
+
* The keying values and references are entry-only data: they travel into the
|
|
9
|
+
* generated manifest entry but never touch the ffmpeg job, so
|
|
10
|
+
* `processingParameters` narrows the job-hash input to the transcode inputs
|
|
11
|
+
* (preset + maxWidth) — retuning a slider must not re-transcode the proxy.
|
|
12
|
+
*/
|
|
13
|
+
import { isAssetReference } from '../asset-reference.js';
|
|
14
|
+
import { validateChromaKeyParameters, DEFAULT_CHROMA_KEY_PARAMETERS } from '../chroma-key.js';
|
|
15
|
+
import { validateVideoParameters, VIDEO_EXTENSIONS } from './video.js';
|
|
16
|
+
function validateOptionalReference(label, value) {
|
|
17
|
+
if (value === undefined)
|
|
18
|
+
return undefined;
|
|
19
|
+
if (!isAssetReference(value)) {
|
|
20
|
+
throw new Error(`"${label}" must be an asset reference ({ "$asset": "<id>" }).`);
|
|
21
|
+
}
|
|
22
|
+
return { $asset: value.$asset };
|
|
23
|
+
}
|
|
24
|
+
export const chromaKeyVideoPlugin = {
|
|
25
|
+
type: 'chroma-key-video',
|
|
26
|
+
accepts: [...VIDEO_EXTENSIONS],
|
|
27
|
+
defaultParameters: () => ({
|
|
28
|
+
preset: 'default',
|
|
29
|
+
chromaKey: {
|
|
30
|
+
...DEFAULT_CHROMA_KEY_PARAMETERS,
|
|
31
|
+
keyColor: [...DEFAULT_CHROMA_KEY_PARAMETERS.keyColor]
|
|
32
|
+
}
|
|
33
|
+
}),
|
|
34
|
+
validateParameters(value) {
|
|
35
|
+
const videoParameters = validateVideoParameters('Chroma-key-video', value);
|
|
36
|
+
const record = value;
|
|
37
|
+
const chromaKey = validateChromaKeyParameters(record.chromaKey);
|
|
38
|
+
const mask = validateOptionalReference('mask', record.mask);
|
|
39
|
+
const fallback = validateOptionalReference('fallback', record.fallback);
|
|
40
|
+
return {
|
|
41
|
+
...videoParameters,
|
|
42
|
+
chromaKey,
|
|
43
|
+
...(mask !== undefined ? { mask } : {}),
|
|
44
|
+
...(fallback !== undefined ? { fallback } : {})
|
|
45
|
+
};
|
|
46
|
+
},
|
|
47
|
+
collectReferences(parameters) {
|
|
48
|
+
const references = [];
|
|
49
|
+
if (parameters.mask) {
|
|
50
|
+
references.push({ field: 'mask', assetId: parameters.mask.$asset, expectedTypes: ['image'] });
|
|
51
|
+
}
|
|
52
|
+
if (parameters.fallback) {
|
|
53
|
+
references.push({
|
|
54
|
+
field: 'fallback',
|
|
55
|
+
assetId: parameters.fallback.$asset,
|
|
56
|
+
expectedTypes: ['image']
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
return references;
|
|
60
|
+
},
|
|
61
|
+
processingParameters(parameters) {
|
|
62
|
+
// Only what process() consumes: keying values and references are
|
|
63
|
+
// entry-only and must not force a re-transcode.
|
|
64
|
+
return {
|
|
65
|
+
preset: parameters.preset,
|
|
66
|
+
...(parameters.maxWidth !== undefined ? { maxWidth: parameters.maxWidth } : {})
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The core part of the type-plugin contract. A type plugin is a
|
|
3
|
+
* first-party module compiled into Fundus that fully defines one asset type;
|
|
4
|
+
* this descriptor is its environment-free part — ingestion claims, parameter
|
|
5
|
+
* defaults, and parameter validation. Server processing, editor panels, and
|
|
6
|
+
* runtime kind handlers complete the four-part contract.
|
|
7
|
+
*/
|
|
8
|
+
import type { CollectedReference } from '../asset-reference.js';
|
|
9
|
+
export interface AssetTypePluginDescriptor<TParameters, TPluginConfig = unknown> {
|
|
10
|
+
/** Asset type key, e.g. 'image' | 'slice'. */
|
|
11
|
+
type: string;
|
|
12
|
+
/** Ingestion: lowercase file extensions (without dot) claimed by this type. */
|
|
13
|
+
accepts: string[];
|
|
14
|
+
/** Parameters a freshly ingested asset starts with. */
|
|
15
|
+
defaultParameters(pluginConfig: TPluginConfig): TParameters;
|
|
16
|
+
/** Validate an unknown parameter blob; returns the typed value or throws. */
|
|
17
|
+
validateParameters(value: unknown): TParameters;
|
|
18
|
+
/**
|
|
19
|
+
* Enumerate the asset references in a validated parameter value.
|
|
20
|
+
* A plugin that declares reference fields must list every one of them here —
|
|
21
|
+
* core and server never introspect parameter blobs to find references.
|
|
22
|
+
* Omitted = the type declares no references (image, slice).
|
|
23
|
+
*/
|
|
24
|
+
collectReferences?(parameters: TParameters): CollectedReference[];
|
|
25
|
+
/**
|
|
26
|
+
* The subset of a validated parameter value the plugin's server-side
|
|
27
|
+
* `process()` actually consumes — the canonicalized form of this value feeds
|
|
28
|
+
* the job hash, so only these fields can force a reprocess.
|
|
29
|
+
* Entry-only parameters (chroma-key tuning, asset references) must be left
|
|
30
|
+
* out: they change the generated manifest entry, not the proxy bytes.
|
|
31
|
+
* Omitted = every parameter is processing-relevant (image, slice, video,
|
|
32
|
+
* audio).
|
|
33
|
+
*/
|
|
34
|
+
processingParameters?(parameters: TParameters): unknown;
|
|
35
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Image plugin (core part): a raw raster image delivered as one optimized
|
|
3
|
+
* proxy. Parameters pick a named format preset (resolved against the plugin's
|
|
4
|
+
* config) and optionally a target width; omitted `targetWidth` keeps the
|
|
5
|
+
* source size (exactly one delivered variant per asset).
|
|
6
|
+
*/
|
|
7
|
+
import type { AssetTypePluginDescriptor } from './contract.js';
|
|
8
|
+
declare const IMAGE_FORMATS: readonly ["webp", "png", "avif", "jpeg"];
|
|
9
|
+
export type ImageFormat = (typeof IMAGE_FORMATS)[number];
|
|
10
|
+
export interface ImagePreset {
|
|
11
|
+
format: ImageFormat;
|
|
12
|
+
/** Encoder quality (format-dependent semantics); omitted = encoder default. */
|
|
13
|
+
quality?: number;
|
|
14
|
+
}
|
|
15
|
+
export interface ImagePluginConfig {
|
|
16
|
+
/** Named presets; merged over the built-in defaults. */
|
|
17
|
+
presets?: Record<string, ImagePreset>;
|
|
18
|
+
}
|
|
19
|
+
export interface ImageParameters {
|
|
20
|
+
/** Preset name, resolved against the plugin config's presets. */
|
|
21
|
+
preset: string;
|
|
22
|
+
/** Resize to this width (keeping aspect ratio); omitted = keep source size. */
|
|
23
|
+
targetWidth?: number;
|
|
24
|
+
}
|
|
25
|
+
/** Built-in presets, always available; host config merges over them. */
|
|
26
|
+
export declare const IMAGE_BUILT_IN_PRESETS: Record<string, ImagePreset>;
|
|
27
|
+
export declare function validateImagePreset(name: string, value: unknown): ImagePreset;
|
|
28
|
+
export declare const RASTER_IMAGE_EXTENSIONS: readonly string[];
|
|
29
|
+
export declare const imagePlugin: AssetTypePluginDescriptor<ImageParameters, ImagePluginConfig>;
|
|
30
|
+
export {};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const IMAGE_FORMATS = ['webp', 'png', 'avif', 'jpeg'];
|
|
2
|
+
/** Built-in presets, always available; host config merges over them. */
|
|
3
|
+
export const IMAGE_BUILT_IN_PRESETS = {
|
|
4
|
+
default: { format: 'webp', quality: 82 }
|
|
5
|
+
};
|
|
6
|
+
export function validateImagePreset(name, value) {
|
|
7
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
8
|
+
throw new Error(`Image preset "${name}" must be an object.`);
|
|
9
|
+
}
|
|
10
|
+
const record = value;
|
|
11
|
+
if (typeof record.format !== 'string' ||
|
|
12
|
+
!IMAGE_FORMATS.includes(record.format)) {
|
|
13
|
+
throw new Error(`Image preset "${name}" needs a "format" of ${IMAGE_FORMATS.map((format) => `'${format}'`).join(' | ')}.`);
|
|
14
|
+
}
|
|
15
|
+
if (record.quality !== undefined && typeof record.quality !== 'number') {
|
|
16
|
+
throw new Error(`Image preset "${name}" has a non-number "quality".`);
|
|
17
|
+
}
|
|
18
|
+
return { format: record.format, quality: record.quality };
|
|
19
|
+
}
|
|
20
|
+
export const RASTER_IMAGE_EXTENSIONS = ['png', 'jpg', 'jpeg', 'webp', 'avif'];
|
|
21
|
+
export const imagePlugin = {
|
|
22
|
+
type: 'image',
|
|
23
|
+
accepts: [...RASTER_IMAGE_EXTENSIONS],
|
|
24
|
+
defaultParameters: () => ({ preset: 'default' }),
|
|
25
|
+
validateParameters(value) {
|
|
26
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
27
|
+
throw new Error('Image parameters must be an object.');
|
|
28
|
+
}
|
|
29
|
+
const record = value;
|
|
30
|
+
if (typeof record.preset !== 'string' || record.preset.length === 0) {
|
|
31
|
+
throw new Error('Image parameters need a non-empty string "preset".');
|
|
32
|
+
}
|
|
33
|
+
if (record.targetWidth !== undefined) {
|
|
34
|
+
if (typeof record.targetWidth !== 'number' ||
|
|
35
|
+
!Number.isInteger(record.targetWidth) ||
|
|
36
|
+
record.targetWidth <= 0) {
|
|
37
|
+
throw new Error('Image "targetWidth" must be a positive integer when set.');
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
preset: record.preset,
|
|
42
|
+
...(record.targetWidth !== undefined ? { targetWidth: record.targetWidth } : {})
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { AssetTypePluginDescriptor } from './contract.js';
|
|
2
|
+
export type RegisteredPlugin = AssetTypePluginDescriptor<unknown, unknown>;
|
|
3
|
+
export declare class PluginRegistry {
|
|
4
|
+
#private;
|
|
5
|
+
register(plugin: RegisteredPlugin): void;
|
|
6
|
+
has(type: string): boolean;
|
|
7
|
+
values(): RegisteredPlugin[];
|
|
8
|
+
types(): string[];
|
|
9
|
+
/**
|
|
10
|
+
* Select the environment-specific implementation for every registered
|
|
11
|
+
* plugin. Unregistered catalogue entries remain dormant; a registered
|
|
12
|
+
* plugin without the requested facet fails immediately during startup.
|
|
13
|
+
*/
|
|
14
|
+
project<T>(catalogue: Readonly<Record<string, T>>, facet: string): Record<string, T>;
|
|
15
|
+
}
|
|
16
|
+
export declare const plugins: PluginRegistry;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The composition root for Fundus asset-type plugins.
|
|
3
|
+
*
|
|
4
|
+
* A plugin is registered exactly once here. Environment-specific surfaces
|
|
5
|
+
* (server processing, editor panels, runtime handlers) keep their imports on
|
|
6
|
+
* the correct side of SvelteKit's browser/server boundary and project this
|
|
7
|
+
* registered set onto their local implementation catalogues.
|
|
8
|
+
*/
|
|
9
|
+
import { audioPlugin } from './audio.js';
|
|
10
|
+
import { chromaKeyVideoPlugin } from './chroma-key-video.js';
|
|
11
|
+
import { imagePlugin } from './image.js';
|
|
12
|
+
import { slicePlugin } from './slice.js';
|
|
13
|
+
import { videoPlugin } from './video.js';
|
|
14
|
+
export class PluginRegistry {
|
|
15
|
+
#byType = new Map();
|
|
16
|
+
register(plugin) {
|
|
17
|
+
if (this.#byType.has(plugin.type)) {
|
|
18
|
+
throw new Error(`Plugin type "${plugin.type}" is already registered.`);
|
|
19
|
+
}
|
|
20
|
+
this.#byType.set(plugin.type, plugin);
|
|
21
|
+
}
|
|
22
|
+
has(type) {
|
|
23
|
+
return this.#byType.has(type);
|
|
24
|
+
}
|
|
25
|
+
values() {
|
|
26
|
+
return [...this.#byType.values()];
|
|
27
|
+
}
|
|
28
|
+
types() {
|
|
29
|
+
return [...this.#byType.keys()];
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Select the environment-specific implementation for every registered
|
|
33
|
+
* plugin. Unregistered catalogue entries remain dormant; a registered
|
|
34
|
+
* plugin without the requested facet fails immediately during startup.
|
|
35
|
+
*/
|
|
36
|
+
project(catalogue, facet) {
|
|
37
|
+
const projected = Object.create(null);
|
|
38
|
+
for (const type of this.#byType.keys()) {
|
|
39
|
+
if (!Object.hasOwn(catalogue, type)) {
|
|
40
|
+
throw new Error(`Registered plugin "${type}" has no ${facet}.`);
|
|
41
|
+
}
|
|
42
|
+
projected[type] = catalogue[type];
|
|
43
|
+
}
|
|
44
|
+
return projected;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
export const plugins = new PluginRegistry();
|
|
48
|
+
plugins.register(imagePlugin);
|
|
49
|
+
plugins.register(slicePlugin);
|
|
50
|
+
plugins.register(videoPlugin);
|
|
51
|
+
plugins.register(chromaKeyVideoPlugin);
|
|
52
|
+
plugins.register(audioPlugin);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slice plugin (core part): an image plus 9-/3-/1-slice data. Parameters carry the full slicing
|
|
3
|
+
* model (mode, insets, overdraw, pixelRatio) plus the same image output
|
|
4
|
+
* options (a format preset). No `targetWidth`: insets are authored in source
|
|
5
|
+
* pixels, so the delivered proxy keeps the source dimensions.
|
|
6
|
+
*/
|
|
7
|
+
import type { AssetTypePluginDescriptor } from './contract.js';
|
|
8
|
+
import type { SliceInsets, SliceMode } from '../slice-data.js';
|
|
9
|
+
import type { ImagePluginConfig, ImagePreset } from './image.js';
|
|
10
|
+
export interface SliceParameters {
|
|
11
|
+
mode: SliceMode;
|
|
12
|
+
insets: SliceInsets;
|
|
13
|
+
pixelRatio: number;
|
|
14
|
+
overdraw?: SliceInsets;
|
|
15
|
+
/** Output format preset name, resolved against the slice plugin config's presets. */
|
|
16
|
+
preset: string;
|
|
17
|
+
}
|
|
18
|
+
/** Same shape as the image plugin's config: named output-format presets. */
|
|
19
|
+
export type SlicePluginConfig = ImagePluginConfig;
|
|
20
|
+
/** Built-in presets, always available; host config merges over them. */
|
|
21
|
+
export declare const SLICE_BUILT_IN_PRESETS: Record<string, ImagePreset>;
|
|
22
|
+
export declare const slicePlugin: AssetTypePluginDescriptor<SliceParameters, SlicePluginConfig>;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { createZeroInsets, SLICE_MODES } from '../slice-data.js';
|
|
2
|
+
import { RASTER_IMAGE_EXTENSIONS } from './image.js';
|
|
3
|
+
/** Built-in presets, always available; host config merges over them. */
|
|
4
|
+
// Deliberately separate from image presets: each namespace feeds its own job hash.
|
|
5
|
+
export const SLICE_BUILT_IN_PRESETS = {
|
|
6
|
+
default: { format: 'webp', quality: 82 }
|
|
7
|
+
};
|
|
8
|
+
function validateInsets(label, value) {
|
|
9
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
10
|
+
throw new Error(`Slice ${label} must be an object with top/right/bottom/left.`);
|
|
11
|
+
}
|
|
12
|
+
const record = value;
|
|
13
|
+
const sides = ['top', 'right', 'bottom', 'left'];
|
|
14
|
+
const result = createZeroInsets();
|
|
15
|
+
for (const side of sides) {
|
|
16
|
+
const sideValue = record[side];
|
|
17
|
+
if (typeof sideValue !== 'number' || !Number.isInteger(sideValue) || sideValue < 0) {
|
|
18
|
+
throw new Error(`Slice ${label}.${side} must be a non-negative integer.`);
|
|
19
|
+
}
|
|
20
|
+
result[side] = sideValue;
|
|
21
|
+
}
|
|
22
|
+
return result;
|
|
23
|
+
}
|
|
24
|
+
export const slicePlugin = {
|
|
25
|
+
type: 'slice',
|
|
26
|
+
accepts: [...RASTER_IMAGE_EXTENSIONS],
|
|
27
|
+
defaultParameters: () => ({
|
|
28
|
+
mode: 'nine',
|
|
29
|
+
insets: createZeroInsets(),
|
|
30
|
+
pixelRatio: 1,
|
|
31
|
+
preset: 'default'
|
|
32
|
+
}),
|
|
33
|
+
validateParameters(value) {
|
|
34
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
35
|
+
throw new Error('Slice parameters must be an object.');
|
|
36
|
+
}
|
|
37
|
+
const record = value;
|
|
38
|
+
if (typeof record.mode !== 'string' ||
|
|
39
|
+
!SLICE_MODES.includes(record.mode)) {
|
|
40
|
+
throw new Error(`Slice "mode" must be one of ${SLICE_MODES.map((mode) => `'${mode}'`).join(' | ')}.`);
|
|
41
|
+
}
|
|
42
|
+
const insets = validateInsets('insets', record.insets);
|
|
43
|
+
if (typeof record.pixelRatio !== 'number' ||
|
|
44
|
+
!Number.isInteger(record.pixelRatio) ||
|
|
45
|
+
record.pixelRatio < 1) {
|
|
46
|
+
throw new Error('Slice "pixelRatio" must be an integer ≥ 1.');
|
|
47
|
+
}
|
|
48
|
+
const overdraw = record.overdraw === undefined ? undefined : validateInsets('overdraw', record.overdraw);
|
|
49
|
+
if (typeof record.preset !== 'string' || record.preset.length === 0) {
|
|
50
|
+
throw new Error('Slice parameters need a non-empty string "preset".');
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
mode: record.mode,
|
|
54
|
+
insets,
|
|
55
|
+
pixelRatio: record.pixelRatio,
|
|
56
|
+
...(overdraw !== undefined ? { overdraw } : {}),
|
|
57
|
+
preset: record.preset
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Video plugin (core part): a raw video delivered as one universally playable
|
|
3
|
+
* H.264 MP4 proxy (one variant per asset, with no per-platform splits in
|
|
4
|
+
* v1). Parameters pick a named encoding preset (resolved against the plugin's
|
|
5
|
+
* config) and optionally a downscale-only width cap; omitted `maxWidth` keeps
|
|
6
|
+
* the source resolution.
|
|
7
|
+
*/
|
|
8
|
+
import type { AssetTypePluginDescriptor } from './contract.js';
|
|
9
|
+
export interface VideoPreset {
|
|
10
|
+
codec: 'h264';
|
|
11
|
+
/** x264 constant rate factor (quality; lower = better/larger). */
|
|
12
|
+
crf: number;
|
|
13
|
+
/** x264 speed/efficiency preset (`-preset`), e.g. 'slow'. */
|
|
14
|
+
encoderPreset: string;
|
|
15
|
+
/** x264 content tuning (`-tune`), e.g. 'animation'; omitted = none. */
|
|
16
|
+
tune?: string;
|
|
17
|
+
/** Output pixel format (`-pix_fmt`), e.g. 'yuv420p'. */
|
|
18
|
+
pixelFormat: string;
|
|
19
|
+
container: 'mp4';
|
|
20
|
+
}
|
|
21
|
+
export interface VideoPluginConfig {
|
|
22
|
+
/** Named presets; merged over the built-in defaults. */
|
|
23
|
+
presets?: Record<string, VideoPreset>;
|
|
24
|
+
/** Absolute path of a system ffmpeg binary; omitted = the bundled `ffmpeg-static`. */
|
|
25
|
+
ffmpegPath?: string;
|
|
26
|
+
/** Absolute path of a system ffprobe binary; omitted = the bundled `ffprobe-static`. */
|
|
27
|
+
ffprobePath?: string;
|
|
28
|
+
}
|
|
29
|
+
export interface VideoParameters {
|
|
30
|
+
/** Preset name, resolved against the video plugin config's presets. */
|
|
31
|
+
preset: string;
|
|
32
|
+
/** Downscale-only width cap (keeping aspect ratio); omitted = source resolution. */
|
|
33
|
+
maxWidth?: number;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Built-in presets, always available; host config merges over them. The
|
|
37
|
+
* default is the proven committed-video formula: H.264, slow preset, animation
|
|
38
|
+
* tune, CRF 28, yuv420p, faststart MP4.
|
|
39
|
+
*/
|
|
40
|
+
export declare const VIDEO_BUILT_IN_PRESETS: Record<string, VideoPreset>;
|
|
41
|
+
export declare function validateVideoPreset(name: string, value: unknown): VideoPreset;
|
|
42
|
+
export declare const VIDEO_EXTENSIONS: readonly string[];
|
|
43
|
+
/** Shared by the video and chroma-key-video plugins (identical transcode inputs). */
|
|
44
|
+
export declare function validateVideoParameters(label: string, value: unknown): VideoParameters;
|
|
45
|
+
export declare const videoPlugin: AssetTypePluginDescriptor<VideoParameters, VideoPluginConfig>;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in presets, always available; host config merges over them. The
|
|
3
|
+
* default is the proven committed-video formula: H.264, slow preset, animation
|
|
4
|
+
* tune, CRF 28, yuv420p, faststart MP4.
|
|
5
|
+
*/
|
|
6
|
+
export const VIDEO_BUILT_IN_PRESETS = {
|
|
7
|
+
default: {
|
|
8
|
+
codec: 'h264',
|
|
9
|
+
crf: 28,
|
|
10
|
+
encoderPreset: 'slow',
|
|
11
|
+
tune: 'animation',
|
|
12
|
+
pixelFormat: 'yuv420p',
|
|
13
|
+
container: 'mp4'
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
export function validateVideoPreset(name, value) {
|
|
17
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
18
|
+
throw new Error(`Video preset "${name}" must be an object.`);
|
|
19
|
+
}
|
|
20
|
+
const record = value;
|
|
21
|
+
if (record.codec !== 'h264') {
|
|
22
|
+
throw new Error(`Video preset "${name}" needs a "codec" of 'h264'.`);
|
|
23
|
+
}
|
|
24
|
+
if (typeof record.crf !== 'number' || !Number.isInteger(record.crf) || record.crf < 0) {
|
|
25
|
+
throw new Error(`Video preset "${name}" needs an integer "crf" ≥ 0.`);
|
|
26
|
+
}
|
|
27
|
+
if (typeof record.encoderPreset !== 'string' || record.encoderPreset.length === 0) {
|
|
28
|
+
throw new Error(`Video preset "${name}" needs a non-empty string "encoderPreset".`);
|
|
29
|
+
}
|
|
30
|
+
if (record.tune !== undefined && (typeof record.tune !== 'string' || record.tune.length === 0)) {
|
|
31
|
+
throw new Error(`Video preset "${name}" has a non-string "tune".`);
|
|
32
|
+
}
|
|
33
|
+
if (typeof record.pixelFormat !== 'string' || record.pixelFormat.length === 0) {
|
|
34
|
+
throw new Error(`Video preset "${name}" needs a non-empty string "pixelFormat".`);
|
|
35
|
+
}
|
|
36
|
+
if (record.container !== 'mp4') {
|
|
37
|
+
throw new Error(`Video preset "${name}" needs a "container" of 'mp4'.`);
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
codec: 'h264',
|
|
41
|
+
crf: record.crf,
|
|
42
|
+
encoderPreset: record.encoderPreset,
|
|
43
|
+
...(record.tune !== undefined ? { tune: record.tune } : {}),
|
|
44
|
+
pixelFormat: record.pixelFormat,
|
|
45
|
+
container: 'mp4'
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
export const VIDEO_EXTENSIONS = ['mp4', 'mov', 'webm', 'm4v'];
|
|
49
|
+
/** Shared by the video and chroma-key-video plugins (identical transcode inputs). */
|
|
50
|
+
export function validateVideoParameters(label, value) {
|
|
51
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
52
|
+
throw new Error(`${label} parameters must be an object.`);
|
|
53
|
+
}
|
|
54
|
+
const record = value;
|
|
55
|
+
if (typeof record.preset !== 'string' || record.preset.length === 0) {
|
|
56
|
+
throw new Error(`${label} parameters need a non-empty string "preset".`);
|
|
57
|
+
}
|
|
58
|
+
if (record.maxWidth !== undefined) {
|
|
59
|
+
if (typeof record.maxWidth !== 'number' ||
|
|
60
|
+
!Number.isInteger(record.maxWidth) ||
|
|
61
|
+
record.maxWidth <= 0) {
|
|
62
|
+
throw new Error(`${label} "maxWidth" must be a positive integer when set.`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
preset: record.preset,
|
|
67
|
+
...(record.maxWidth !== undefined ? { maxWidth: record.maxWidth } : {})
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
export const videoPlugin = {
|
|
71
|
+
type: 'video',
|
|
72
|
+
accepts: [...VIDEO_EXTENSIONS],
|
|
73
|
+
defaultParameters: () => ({ preset: 'default' }),
|
|
74
|
+
validateParameters(value) {
|
|
75
|
+
return validateVideoParameters('Video', value);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure functions over the reference graph: references form a
|
|
3
|
+
* directed acyclic graph — cycles, dangling targets, and type mismatches are
|
|
4
|
+
* validation *errors*; a closure that leaks out of a manifest is simply
|
|
5
|
+
* passive membership (`manifest-membership.ts`), not a problem. Everything
|
|
6
|
+
* here operates on a `Library` plus the per-asset references the plugins
|
|
7
|
+
* enumerated (`collectAllReferences`) — no fs, no plugin introspection.
|
|
8
|
+
*/
|
|
9
|
+
import type { CollectedReference } from './asset-reference.js';
|
|
10
|
+
import type { AssetId, Library } from './library.js';
|
|
11
|
+
import type { ValidationIssueDto } from './api-types.js';
|
|
12
|
+
import type { AssetTypePluginDescriptor } from './plugins/contract.js';
|
|
13
|
+
/** The plugin registry shape shared by server and tests: type → descriptor. */
|
|
14
|
+
export type PluginDescriptorRegistry = Record<string, AssetTypePluginDescriptor<unknown, unknown>>;
|
|
15
|
+
/** Per-asset outgoing references, keyed by asset id (every asset gets a key). */
|
|
16
|
+
export type ReferencesByAsset = Record<AssetId, CollectedReference[]>;
|
|
17
|
+
/**
|
|
18
|
+
* Ask each asset's owning plugin for the references in its parameters. Assets
|
|
19
|
+
* whose type is unknown or whose parameters do not validate contribute no
|
|
20
|
+
* references — those problems are reported by the regular validation pass.
|
|
21
|
+
*/
|
|
22
|
+
export declare function collectAllReferences(library: Library, plugins: PluginDescriptorRegistry): ReferencesByAsset;
|
|
23
|
+
/**
|
|
24
|
+
* Reference *errors* (they block build/codegen): a reference to an unknown
|
|
25
|
+
* asset, a target whose type is not among the field's expected types, and
|
|
26
|
+
* cycles (composition is acyclic by nature).
|
|
27
|
+
*/
|
|
28
|
+
export declare function referenceErrors(library: Library, referencesByAsset: ReferencesByAsset): ValidationIssueDto[];
|
|
29
|
+
/**
|
|
30
|
+
* The transitive reference closure of a set of assets, including the starting
|
|
31
|
+
* set — the assets that must be delivered so the starting set renders as
|
|
32
|
+
* authored. Tolerates cycles and ignores dangling targets (both are reported
|
|
33
|
+
* as errors by `referenceErrors`).
|
|
34
|
+
*/
|
|
35
|
+
export declare function referenceClosure(assetIds: Iterable<AssetId>, referencesByAsset: ReferencesByAsset): Set<AssetId>;
|
|
36
|
+
/**
|
|
37
|
+
* A dependencies-first order over `assetIds` and everything they transitively
|
|
38
|
+
* reference — the build order that lets each entry inline its references from
|
|
39
|
+
* already-built entries. Throws on a cycle (with the cycle path).
|
|
40
|
+
*/
|
|
41
|
+
export declare function topologicalReferenceOrder(assetIds: Iterable<AssetId>, referencesByAsset: ReferencesByAsset): AssetId[];
|