fundus 0.0.2 → 0.0.3
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 +58 -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.wavcNhPC.css +1 -0
- package/build/client/_app/immutable/assets/2.wavcNhPC.css.br +0 -0
- package/build/client/_app/immutable/assets/2.wavcNhPC.css.gz +0 -0
- package/build/client/_app/immutable/assets/3.Cjh_fXPY.css +1 -0
- package/build/client/_app/immutable/assets/3.Cjh_fXPY.css.br +0 -0
- package/build/client/_app/immutable/assets/3.Cjh_fXPY.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.zyZtpKzC.css +1 -0
- package/build/client/_app/immutable/assets/LibraryContextMenu.zyZtpKzC.css.br +0 -0
- package/build/client/_app/immutable/assets/LibraryContextMenu.zyZtpKzC.css.gz +0 -0
- package/build/client/_app/immutable/chunks/BiIto6ft.js +1 -0
- package/build/client/_app/immutable/chunks/BiIto6ft.js.br +0 -0
- package/build/client/_app/immutable/chunks/BiIto6ft.js.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/{dist/editor/_app/immutable/chunks/DjvOiDmq.js → build/client/_app/immutable/chunks/CvY6YsFE.js} +1 -1
- package/build/client/_app/immutable/chunks/CvY6YsFE.js.br +0 -0
- package/build/client/_app/immutable/chunks/CvY6YsFE.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DL0_39Ys.js +5 -0
- package/build/client/_app/immutable/chunks/DL0_39Ys.js.br +0 -0
- package/build/client/_app/immutable/chunks/DL0_39Ys.js.gz +0 -0
- package/{dist/editor/_app/immutable/chunks/BfPE5wdt.js → build/client/_app/immutable/chunks/DQ8SXFaX.js} +1 -1
- package/build/client/_app/immutable/chunks/DQ8SXFaX.js.br +0 -0
- package/build/client/_app/immutable/chunks/DQ8SXFaX.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DjdrdRzT.js.br +0 -0
- package/build/client/_app/immutable/chunks/DjdrdRzT.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.ubN0v9bN.js} +2 -2
- package/build/client/_app/immutable/entry/app.ubN0v9bN.js.br +0 -0
- package/build/client/_app/immutable/entry/app.ubN0v9bN.js.gz +0 -0
- package/build/client/_app/immutable/entry/start.BW8dBDh6.js +1 -0
- package/build/client/_app/immutable/entry/start.BW8dBDh6.js.br +0 -0
- package/build/client/_app/immutable/entry/start.BW8dBDh6.js.gz +0 -0
- package/{dist/editor/_app/immutable/nodes/0.xdMI5GEU.js → build/client/_app/immutable/nodes/0.Dr5ijCIl.js} +1 -1
- package/build/client/_app/immutable/nodes/0.Dr5ijCIl.js.br +0 -0
- package/build/client/_app/immutable/nodes/0.Dr5ijCIl.js.gz +0 -0
- package/{dist/editor/_app/immutable/nodes/1.D9p2tfoJ.js → build/client/_app/immutable/nodes/1.DiIGIRAb.js} +1 -1
- package/build/client/_app/immutable/nodes/1.DiIGIRAb.js.br +3 -0
- package/build/client/_app/immutable/nodes/1.DiIGIRAb.js.gz +0 -0
- package/{dist/editor/_app/immutable/nodes/2.BrA0Hyzu.js → build/client/_app/immutable/nodes/2.B8KYmQcV.js} +3 -3
- package/build/client/_app/immutable/nodes/2.B8KYmQcV.js.br +0 -0
- package/build/client/_app/immutable/nodes/2.B8KYmQcV.js.gz +0 -0
- package/build/client/_app/immutable/nodes/3.B1jc7arT.js +104 -0
- package/build/client/_app/immutable/nodes/3.B1jc7arT.js.br +0 -0
- package/build/client/_app/immutable/nodes/3.B1jc7arT.js.gz +0 -0
- package/build/client/_app/immutable/nodes/4.BPK9n1mH.js +2 -0
- package/build/client/_app/immutable/nodes/4.BPK9n1mH.js.br +0 -0
- package/build/client/_app/immutable/nodes/4.BPK9n1mH.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-Bw4WWLhG.js +443 -0
- package/build/server/chunks/chunks/context.js-Bw4WWLhG.js.map +1 -0
- package/build/server/chunks/chunks/core.js-DDntpb72.js +409 -0
- package/build/server/chunks/chunks/core.js-DDntpb72.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-CLTzaOxn.js +152 -0
- package/build/server/chunks/chunks/exports.js-CLTzaOxn.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-Dxoa-jtP.js +100 -0
- package/build/server/chunks/chunks/http.js-Dxoa-jtP.js.map +1 -0
- package/build/server/chunks/chunks/internal.js-BDBSzJId.js +5753 -0
- package/build/server/chunks/chunks/internal.js-BDBSzJId.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-BP8cL-qD.js} +344 -444
- package/build/server/chunks/chunks/library-store.js-BP8cL-qD.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-BWXgl1Xf.js +255 -0
- package/build/server/chunks/chunks/shared.js-BWXgl1Xf.js.map +1 -0
- package/build/server/chunks/chunks/utils.js-9oI5Oz0w.js +1048 -0
- package/build/server/chunks/chunks/utils.js-9oI5Oz0w.js.map +1 -0
- package/build/server/chunks/entries/endpoints/api/_...path_/_server.ts.js-WknZVW_S.js +27 -0
- package/build/server/chunks/entries/endpoints/api/_...path_/_server.ts.js-WknZVW_S.js.map +1 -0
- package/build/server/chunks/entries/endpoints/api/assets/_id_/_server.ts.js-BrZ__-HZ.js +24 -0
- package/build/server/chunks/entries/endpoints/api/assets/_id_/_server.ts.js-BrZ__-HZ.js.map +1 -0
- package/build/server/chunks/entries/endpoints/api/assets/_server.ts.js-PqADGPXc.js +45 -0
- package/build/server/chunks/entries/endpoints/api/assets/_server.ts.js-PqADGPXc.js.map +1 -0
- package/build/server/chunks/entries/endpoints/api/assets/delete/_server.ts.js-OqoE5PN-.js +31 -0
- package/build/server/chunks/entries/endpoints/api/assets/delete/_server.ts.js-OqoE5PN-.js.map +1 -0
- package/build/server/chunks/entries/endpoints/api/assets/move/_server.ts.js-CJ08e1ho.js +31 -0
- package/build/server/chunks/entries/endpoints/api/assets/move/_server.ts.js-CJ08e1ho.js.map +1 -0
- package/build/server/chunks/entries/endpoints/api/folders/_path_/_server.ts.js-DdF3eK2I.js +38 -0
- package/build/server/chunks/entries/endpoints/api/folders/_path_/_server.ts.js-DdF3eK2I.js.map +1 -0
- package/build/server/chunks/entries/endpoints/api/folders/_server.ts.js-Cxt6jsJ5.js +31 -0
- package/build/server/chunks/entries/endpoints/api/folders/_server.ts.js-Cxt6jsJ5.js.map +1 -0
- package/build/server/chunks/entries/endpoints/api/manifests/_name_/_server.ts.js-BWVM2Ntl.js +24 -0
- package/build/server/chunks/entries/endpoints/api/manifests/_name_/_server.ts.js-BWVM2Ntl.js.map +1 -0
- package/build/server/chunks/entries/endpoints/api/manifests/_server.ts.js-Z2bM-qHM.js +31 -0
- package/build/server/chunks/entries/endpoints/api/manifests/_server.ts.js-Z2bM-qHM.js.map +1 -0
- package/build/server/chunks/entries/endpoints/api/state/_server.ts.js-BX9H2Z34.js +24 -0
- package/build/server/chunks/entries/endpoints/api/state/_server.ts.js-BX9H2Z34.js.map +1 -0
- package/build/server/chunks/entries/endpoints/files/_...path_/_server.ts.js-BWHDZ8mz.js +27 -0
- package/build/server/chunks/entries/endpoints/files/_...path_/_server.ts.js-BWHDZ8mz.js.map +1 -0
- package/build/server/chunks/entries/endpoints/files/proxy/_...path_/_server.ts.js-CR_7mndO.js +24 -0
- package/build/server/chunks/entries/endpoints/files/proxy/_...path_/_server.ts.js-CR_7mndO.js.map +1 -0
- package/build/server/chunks/entries/endpoints/files/raw/_...path_/_server.ts.js-Cw1PDBvb.js +24 -0
- package/build/server/chunks/entries/endpoints/files/raw/_...path_/_server.ts.js-Cw1PDBvb.js.map +1 -0
- package/build/server/chunks/entries/hooks.server.js-BHoE0yBi.js +23 -0
- package/build/server/chunks/entries/hooks.server.js-BHoE0yBi.js.map +1 -0
- package/build/server/chunks/handler-10cx4zHn.js +1475 -0
- package/build/server/chunks/handler-10cx4zHn.js.map +1 -0
- package/build/server/chunks/index.js-DvPfiC00.js +3789 -0
- package/build/server/chunks/index.js-DvPfiC00.js.map +1 -0
- package/build/server/chunks/manifest.js-DoPjUnzD.js +149 -0
- package/build/server/chunks/manifest.js-DoPjUnzD.js.map +1 -0
- package/build/server/chunks/nodes/0.js-Di3qgAtW.js +11 -0
- package/build/server/chunks/nodes/0.js-Di3qgAtW.js.map +1 -0
- package/build/server/chunks/nodes/1.js-BlWSFIA8.js +7 -0
- package/build/server/chunks/nodes/1.js-BlWSFIA8.js.map +1 -0
- package/build/server/chunks/nodes/2.js-CO9nSPUN.js +7 -0
- package/build/server/chunks/nodes/2.js-CO9nSPUN.js.map +1 -0
- package/build/server/chunks/nodes/3.js-C2D6xpEO.js +7 -0
- package/build/server/chunks/nodes/3.js-C2D6xpEO.js.map +1 -0
- package/build/server/chunks/nodes/4.js-B3IuagJW.js +7 -0
- package/build/server/chunks/nodes/4.js-B3IuagJW.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 +109 -1332
- 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 +18 -0
- package/dist/core/ingest.js +58 -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/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 -24
- package/dist/runtime/kind-handlers.js +74 -65
- 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 +34 -0
- package/dist/server/config-loader.js +103 -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 +16 -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 +7 -0
- package/dist/server/plugins/audio.js +27 -0
- package/dist/server/plugins/chroma-key-video.d.ts +9 -0
- package/dist/server/plugins/chroma-key-video.js +38 -0
- package/dist/server/plugins/contract.d.ts +100 -0
- package/dist/server/plugins/contract.js +16 -0
- package/dist/server/plugins/image.d.ts +8 -0
- package/dist/server/plugins/image.js +30 -0
- package/dist/server/plugins/slice.d.ts +14 -0
- package/dist/server/plugins/slice.js +63 -0
- package/dist/server/plugins/video.d.ts +6 -0
- package/dist/server/plugins/video.js +29 -0
- package/dist/server/plugins.d.ts +23 -0
- package/dist/server/plugins.js +33 -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 +1 -0
- package/dist/server/start.js +33 -0
- package/package.json +29 -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/n7mvYc42.js +0 -5
- package/dist/editor/_app/immutable/entry/start.C8HHOLly.js +0 -1
- 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/DjdrdRzT.js +0 -0
- /package/{dist/editor → build/client}/_app/immutable/chunks/xihTtKlq.js +0 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The server part of the type-plugin contract: everything the
|
|
3
|
+
* pipeline needs per asset type beyond the core descriptor — the plugin's
|
|
4
|
+
* config slice, preset resolution, the processing step (sharp/ffmpeg), proxy
|
|
5
|
+
* probing, and the manifest-entry builder for codegen. The orchestrators
|
|
6
|
+
* (processor, codegen, check, store, operations) are type-agnostic and consult
|
|
7
|
+
* only the registry (plugins.ts), so a new asset type is: a core descriptor +
|
|
8
|
+
* one `ServerAssetTypePlugin` + one registry entry — nothing else server-side.
|
|
9
|
+
*/
|
|
10
|
+
import type { AssetTypePluginDescriptor, ManifestEntryData } from '../../core/index.js';
|
|
11
|
+
import type { ResolvedFundusConfig } from '../config-loader.js';
|
|
12
|
+
/** Output metadata of one processed proxy file (absent fields don't apply). */
|
|
13
|
+
export interface ProxyOutputInfo {
|
|
14
|
+
/** Intrinsic pixel dimensions; absent for audio proxies. */
|
|
15
|
+
width?: number;
|
|
16
|
+
height?: number;
|
|
17
|
+
/** Media duration; present for video and audio proxies. */
|
|
18
|
+
durationMs?: number;
|
|
19
|
+
}
|
|
20
|
+
/** One delivered asset's current proxy: file identity plus output metadata. */
|
|
21
|
+
export interface AssetProxyInfo extends ProxyOutputInfo {
|
|
22
|
+
assetId: string;
|
|
23
|
+
/** Basename, stable across folder moves. */
|
|
24
|
+
fileName: string;
|
|
25
|
+
/** Path relative to proxyDir, mirroring the raw asset's folder. */
|
|
26
|
+
relativePath: string;
|
|
27
|
+
fileSize: number;
|
|
28
|
+
}
|
|
29
|
+
/** The inputs of one processing run: raw file → proxy file. */
|
|
30
|
+
export interface ServerProcessingJob<TParameters, TPreset> {
|
|
31
|
+
/** Absolute path of the raw source file. */
|
|
32
|
+
sourcePath: string;
|
|
33
|
+
/** Absolute path the proxy file must be written to. */
|
|
34
|
+
targetPath: string;
|
|
35
|
+
/** The asset's validated parameters. */
|
|
36
|
+
parameters: TParameters;
|
|
37
|
+
/** The resolved preset the parameters name. */
|
|
38
|
+
preset: TPreset;
|
|
39
|
+
config: ResolvedFundusConfig;
|
|
40
|
+
}
|
|
41
|
+
/** The inputs of one manifest-entry build. */
|
|
42
|
+
export interface ManifestEntryInput<TParameters> {
|
|
43
|
+
assetId: string;
|
|
44
|
+
/** The asset's validated parameters. */
|
|
45
|
+
parameters: TParameters;
|
|
46
|
+
/** The delivered proxy URL (`proxyBasePath` + proxy relative path). */
|
|
47
|
+
src: string;
|
|
48
|
+
proxy: AssetProxyInfo;
|
|
49
|
+
}
|
|
50
|
+
export interface ServerAssetTypePlugin<TParameters = unknown, TPreset = unknown> {
|
|
51
|
+
/** The environment-free half of the plugin (type key, ingestion, validation). */
|
|
52
|
+
descriptor: AssetTypePluginDescriptor<TParameters, unknown>;
|
|
53
|
+
/**
|
|
54
|
+
* The plugin's `plugins.<namespace>` config slice — handed to the
|
|
55
|
+
* descriptor's `defaultParameters` on ingest. Types may share a namespace
|
|
56
|
+
* (chroma-key-video uses the video slice).
|
|
57
|
+
*/
|
|
58
|
+
pluginConfig(config: ResolvedFundusConfig): unknown;
|
|
59
|
+
/** The merged (built-in + host) named presets available to this type. */
|
|
60
|
+
presets(config: ResolvedFundusConfig): Record<string, TPreset>;
|
|
61
|
+
/**
|
|
62
|
+
* The resolved settings of the preset the validated parameters name —
|
|
63
|
+
* these feed the job hash (a preset edit under an unchanged name
|
|
64
|
+
* must rename the proxy). Throws (listing the known names) on a miss.
|
|
65
|
+
*/
|
|
66
|
+
resolvePreset(parameters: TParameters, config: ResolvedFundusConfig): TPreset;
|
|
67
|
+
/** The proxy file extension a resolved preset's output carries. */
|
|
68
|
+
proxyExtension(preset: TPreset): string;
|
|
69
|
+
/**
|
|
70
|
+
* Optional ingest seeding, run right after the raw file is stored: derive
|
|
71
|
+
* better starting parameters than the schema defaults from the actual raw
|
|
72
|
+
* file — the slice plugin seeds insets from the image dimensions and a
|
|
73
|
+
* pixel ratio from a `@2x`-style file-name suffix. Returns the replacement
|
|
74
|
+
* parameters, or undefined to keep the defaults. Omitted = defaults stand.
|
|
75
|
+
*/
|
|
76
|
+
seedIngestParameters?(context: {
|
|
77
|
+
/** Absolute path of the stored raw file. */
|
|
78
|
+
rawPath: string;
|
|
79
|
+
/** The stored raw file name (sanitized, unique). */
|
|
80
|
+
rawFileName: string;
|
|
81
|
+
/** The validated default parameters the record starts with. */
|
|
82
|
+
parameters: TParameters;
|
|
83
|
+
config: ResolvedFundusConfig;
|
|
84
|
+
}): Promise<TParameters | undefined>;
|
|
85
|
+
/** Run the processing job (sharp/ffmpeg): write the proxy, report its metadata. */
|
|
86
|
+
process(job: ServerProcessingJob<TParameters, TPreset>): Promise<ProxyOutputInfo>;
|
|
87
|
+
/** Probe an existing proxy file's output metadata (the up-to-date path). */
|
|
88
|
+
readProxyInfo(proxyPath: string, config: ResolvedFundusConfig): Promise<ProxyOutputInfo>;
|
|
89
|
+
/**
|
|
90
|
+
* Build the generated manifest entry. Reference fields stay
|
|
91
|
+
* `{ $asset }` markers — codegen's generic inline walk resolves them.
|
|
92
|
+
*/
|
|
93
|
+
buildManifestEntry(input: ManifestEntryInput<TParameters>): ManifestEntryData;
|
|
94
|
+
}
|
|
95
|
+
/** The one server-side touch point for a new asset type: type key → plugin. */
|
|
96
|
+
export type ServerPluginRegistry = Record<string, ServerAssetTypePlugin>;
|
|
97
|
+
/** Resolve a named preset out of a plugin's merged preset map, or throw listing the known names. */
|
|
98
|
+
export declare function resolveNamedPreset<TPreset>(label: string, presetName: string, presets: Record<string, TPreset>): TPreset;
|
|
99
|
+
/** A proxy-info field a given entry kind cannot do without. */
|
|
100
|
+
export declare function requireProxyField(proxy: AssetProxyInfo, field: 'width' | 'height' | 'durationMs'): number;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** Resolve a named preset out of a plugin's merged preset map, or throw listing the known names. */
|
|
2
|
+
export function resolveNamedPreset(label, presetName, presets) {
|
|
3
|
+
const preset = presets[presetName];
|
|
4
|
+
if (!preset) {
|
|
5
|
+
throw new Error(`Unknown ${label} preset "${presetName}" (known: ${Object.keys(presets).sort().join(', ')}).`);
|
|
6
|
+
}
|
|
7
|
+
return preset;
|
|
8
|
+
}
|
|
9
|
+
/** A proxy-info field a given entry kind cannot do without. */
|
|
10
|
+
export function requireProxyField(proxy, field) {
|
|
11
|
+
const value = proxy[field];
|
|
12
|
+
if (value === undefined) {
|
|
13
|
+
throw new Error(`Proxy info of "${proxy.assetId}" is missing "${field}".`);
|
|
14
|
+
}
|
|
15
|
+
return value;
|
|
16
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Image plugin (server part): sharp scale + convert per preset.
|
|
3
|
+
*/
|
|
4
|
+
import { type ImageFormat, type ImageParameters, type ImagePreset } from '../../core/index.js';
|
|
5
|
+
import { type ServerAssetTypePlugin } from './contract.js';
|
|
6
|
+
/** The delivered file extension of an image-preset format. */
|
|
7
|
+
export declare function extensionForImageFormat(format: ImageFormat): string;
|
|
8
|
+
export declare const imageServerPlugin: ServerAssetTypePlugin<ImageParameters, ImagePreset>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Image plugin (server part): sharp scale + convert per preset.
|
|
3
|
+
*/
|
|
4
|
+
import { imagePlugin } from '../../core/index.js';
|
|
5
|
+
import { processImageFile, readImageDimensions } from '../processing/image-processing.js';
|
|
6
|
+
import { requireProxyField, resolveNamedPreset } from './contract.js';
|
|
7
|
+
/** The delivered file extension of an image-preset format. */
|
|
8
|
+
export function extensionForImageFormat(format) {
|
|
9
|
+
return format === 'jpeg' ? 'jpg' : format;
|
|
10
|
+
}
|
|
11
|
+
export const imageServerPlugin = {
|
|
12
|
+
descriptor: imagePlugin,
|
|
13
|
+
pluginConfig: (config) => config.plugins.image,
|
|
14
|
+
presets: (config) => config.plugins.image.presets,
|
|
15
|
+
resolvePreset: (parameters, config) => resolveNamedPreset('image', parameters.preset, config.plugins.image.presets),
|
|
16
|
+
proxyExtension: (preset) => extensionForImageFormat(preset.format),
|
|
17
|
+
process: (job) => processImageFile({
|
|
18
|
+
sourcePath: job.sourcePath,
|
|
19
|
+
targetPath: job.targetPath,
|
|
20
|
+
preset: job.preset,
|
|
21
|
+
targetWidth: job.parameters.targetWidth
|
|
22
|
+
}),
|
|
23
|
+
readProxyInfo: (proxyPath) => readImageDimensions(proxyPath),
|
|
24
|
+
buildManifestEntry: ({ src, proxy }) => ({
|
|
25
|
+
kind: 'image',
|
|
26
|
+
src,
|
|
27
|
+
width: requireProxyField(proxy, 'width'),
|
|
28
|
+
height: requireProxyField(proxy, 'height')
|
|
29
|
+
})
|
|
30
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slice plugin (server part): the same sharp conversion as the image plugin,
|
|
3
|
+
* never resized — insets are authored in source pixels, so the delivered proxy
|
|
4
|
+
* keeps the source dimensions. The slicing data is entry-only.
|
|
5
|
+
*/
|
|
6
|
+
import { type ImagePreset, type SliceParameters } from '../../core/index.js';
|
|
7
|
+
import { type ServerAssetTypePlugin } from './contract.js';
|
|
8
|
+
/**
|
|
9
|
+
* Best-effort guess of the asset's export scale from a `@2x` / `@3x` suffix in
|
|
10
|
+
* the file name (also tolerates `-`/`_` separators, e.g. `dialog-3x.png`).
|
|
11
|
+
* Falls back to 1.
|
|
12
|
+
*/
|
|
13
|
+
export declare function detectPixelRatio(fileName: string): number;
|
|
14
|
+
export declare const sliceServerPlugin: ServerAssetTypePlugin<SliceParameters, ImagePreset>;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slice plugin (server part): the same sharp conversion as the image plugin,
|
|
3
|
+
* never resized — insets are authored in source pixels, so the delivered proxy
|
|
4
|
+
* keeps the source dimensions. The slicing data is entry-only.
|
|
5
|
+
*/
|
|
6
|
+
import { defaultInsetsForSize, slicePlugin } from '../../core/index.js';
|
|
7
|
+
import { processImageFile, readImageDimensions } from '../processing/image-processing.js';
|
|
8
|
+
import { extensionForImageFormat } from './image.js';
|
|
9
|
+
import { requireProxyField, resolveNamedPreset } from './contract.js';
|
|
10
|
+
function hasAnySide(insets) {
|
|
11
|
+
if (!insets)
|
|
12
|
+
return false;
|
|
13
|
+
return insets.top > 0 || insets.right > 0 || insets.bottom > 0 || insets.left > 0;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Best-effort guess of the asset's export scale from a `@2x` / `@3x` suffix in
|
|
17
|
+
* the file name (also tolerates `-`/`_` separators, e.g. `dialog-3x.png`).
|
|
18
|
+
* Falls back to 1.
|
|
19
|
+
*/
|
|
20
|
+
export function detectPixelRatio(fileName) {
|
|
21
|
+
const match = fileName.match(/(?:@|[-_])(\d)x(?=\.[^.]*$|$)/i);
|
|
22
|
+
const ratio = match ? Number(match[1]) : 1;
|
|
23
|
+
return ratio >= 1 ? ratio : 1;
|
|
24
|
+
}
|
|
25
|
+
export const sliceServerPlugin = {
|
|
26
|
+
descriptor: slicePlugin,
|
|
27
|
+
pluginConfig: (config) => config.plugins.slice,
|
|
28
|
+
presets: (config) => config.plugins.slice.presets,
|
|
29
|
+
resolvePreset: (parameters, config) => resolveNamedPreset('slice', parameters.preset, config.plugins.slice.presets),
|
|
30
|
+
proxyExtension: (preset) => extensionForImageFormat(preset.format),
|
|
31
|
+
// Seed sensible insets from the actual image dimensions and a pixel ratio
|
|
32
|
+
// from the file name — the schema default can't know either.
|
|
33
|
+
seedIngestParameters: async ({ rawPath, rawFileName, parameters }) => {
|
|
34
|
+
const dimensions = await readImageDimensions(rawPath);
|
|
35
|
+
return {
|
|
36
|
+
...parameters,
|
|
37
|
+
insets: defaultInsetsForSize(dimensions.width, dimensions.height),
|
|
38
|
+
pixelRatio: detectPixelRatio(rawFileName)
|
|
39
|
+
};
|
|
40
|
+
},
|
|
41
|
+
process: (job) => processImageFile({
|
|
42
|
+
sourcePath: job.sourcePath,
|
|
43
|
+
targetPath: job.targetPath,
|
|
44
|
+
preset: job.preset
|
|
45
|
+
}),
|
|
46
|
+
readProxyInfo: (proxyPath) => readImageDimensions(proxyPath),
|
|
47
|
+
buildManifestEntry: ({ parameters, src, proxy }) => {
|
|
48
|
+
const slicing = {
|
|
49
|
+
mode: parameters.mode,
|
|
50
|
+
insets: parameters.insets,
|
|
51
|
+
pixelRatio: parameters.pixelRatio,
|
|
52
|
+
// An all-zero overdraw is semantically "none" — keep the entry minimal.
|
|
53
|
+
...(hasAnySide(parameters.overdraw) ? { overdraw: parameters.overdraw } : {})
|
|
54
|
+
};
|
|
55
|
+
return {
|
|
56
|
+
kind: 'slice',
|
|
57
|
+
src,
|
|
58
|
+
slicing,
|
|
59
|
+
sourceWidth: requireProxyField(proxy, 'width'),
|
|
60
|
+
sourceHeight: requireProxyField(proxy, 'height')
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Video plugin (server part): one ffmpeg transcode per preset.
|
|
3
|
+
*/
|
|
4
|
+
import { type VideoParameters, type VideoPreset } from '../../core/index.js';
|
|
5
|
+
import { type ServerAssetTypePlugin } from './contract.js';
|
|
6
|
+
export declare const videoServerPlugin: ServerAssetTypePlugin<VideoParameters, VideoPreset>;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Video plugin (server part): one ffmpeg transcode per preset.
|
|
3
|
+
*/
|
|
4
|
+
import { videoPlugin } from '../../core/index.js';
|
|
5
|
+
import { probeMedia } from '../processing/media-probe.js';
|
|
6
|
+
import { processVideoFile } from '../processing/video-processing.js';
|
|
7
|
+
import { requireProxyField, resolveNamedPreset } from './contract.js';
|
|
8
|
+
export const videoServerPlugin = {
|
|
9
|
+
descriptor: videoPlugin,
|
|
10
|
+
pluginConfig: (config) => config.plugins.video,
|
|
11
|
+
presets: (config) => config.plugins.video.presets,
|
|
12
|
+
resolvePreset: (parameters, config) => resolveNamedPreset('video', parameters.preset, config.plugins.video.presets),
|
|
13
|
+
proxyExtension: (preset) => preset.container,
|
|
14
|
+
process: (job) => processVideoFile({
|
|
15
|
+
sourcePath: job.sourcePath,
|
|
16
|
+
targetPath: job.targetPath,
|
|
17
|
+
preset: job.preset,
|
|
18
|
+
maxWidth: job.parameters.maxWidth,
|
|
19
|
+
config: job.config
|
|
20
|
+
}),
|
|
21
|
+
readProxyInfo: (proxyPath, config) => probeMedia(proxyPath, config),
|
|
22
|
+
buildManifestEntry: ({ src, proxy }) => ({
|
|
23
|
+
kind: 'video',
|
|
24
|
+
src,
|
|
25
|
+
width: requireProxyField(proxy, 'width'),
|
|
26
|
+
height: requireProxyField(proxy, 'height'),
|
|
27
|
+
durationMs: requireProxyField(proxy, 'durationMs')
|
|
28
|
+
})
|
|
29
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The server-side plugin registry: the ONE map that binds an asset
|
|
3
|
+
* type to its server plugin (core descriptor + config slice + presets +
|
|
4
|
+
* processing + entry building — plugins/contract.ts). The pipeline
|
|
5
|
+
* orchestrators (processor, codegen, check, store, operations) are
|
|
6
|
+
* type-agnostic and take the registry as a parameter defaulting to this map,
|
|
7
|
+
* so a new asset type is a descriptor + one `ServerAssetTypePlugin` + one
|
|
8
|
+
* entry here — and tests can extend the registry without touching them.
|
|
9
|
+
*/
|
|
10
|
+
import type { AssetTypePluginDescriptor } from '../core/index.js';
|
|
11
|
+
import type { ServerAssetTypePlugin, ServerPluginRegistry } from './plugins/contract.js';
|
|
12
|
+
export declare const serverPluginsByType: ServerPluginRegistry;
|
|
13
|
+
export declare function serverPluginForType(type: string, registry?: ServerPluginRegistry): ServerAssetTypePlugin;
|
|
14
|
+
/** The core-descriptor view of a registry — what core's reference/graph helpers consume. */
|
|
15
|
+
export declare function descriptorsByType(registry: ServerPluginRegistry): Record<string, AssetTypePluginDescriptor<unknown, unknown>>;
|
|
16
|
+
/** The built-in registry's descriptor map (the common case; registry-aware code derives its own). */
|
|
17
|
+
export declare const pluginsByType: Record<string, AssetTypePluginDescriptor<unknown, unknown>>;
|
|
18
|
+
/**
|
|
19
|
+
* The job-hash parameter input for an asset: the plugin-declared
|
|
20
|
+
* processing-relevant subset when the plugin distinguishes one (chroma-key-
|
|
21
|
+
* video: keying + references are entry-only), else the full parameter value.
|
|
22
|
+
*/
|
|
23
|
+
export declare function processingParametersOf(plugin: AssetTypePluginDescriptor<unknown, unknown>, parameters: unknown): unknown;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { audioServerPlugin } from './plugins/audio.js';
|
|
2
|
+
import { chromaKeyVideoServerPlugin } from './plugins/chroma-key-video.js';
|
|
3
|
+
import { imageServerPlugin } from './plugins/image.js';
|
|
4
|
+
import { sliceServerPlugin } from './plugins/slice.js';
|
|
5
|
+
import { videoServerPlugin } from './plugins/video.js';
|
|
6
|
+
export const serverPluginsByType = {
|
|
7
|
+
[imageServerPlugin.descriptor.type]: imageServerPlugin,
|
|
8
|
+
[sliceServerPlugin.descriptor.type]: sliceServerPlugin,
|
|
9
|
+
[videoServerPlugin.descriptor.type]: videoServerPlugin,
|
|
10
|
+
[chromaKeyVideoServerPlugin.descriptor.type]: chromaKeyVideoServerPlugin,
|
|
11
|
+
[audioServerPlugin.descriptor.type]: audioServerPlugin
|
|
12
|
+
};
|
|
13
|
+
export function serverPluginForType(type, registry = serverPluginsByType) {
|
|
14
|
+
const plugin = registry[type];
|
|
15
|
+
if (!plugin) {
|
|
16
|
+
throw new Error(`Unknown asset type "${type}" (known: ${Object.keys(registry).sort().join(', ')}).`);
|
|
17
|
+
}
|
|
18
|
+
return plugin;
|
|
19
|
+
}
|
|
20
|
+
/** The core-descriptor view of a registry — what core's reference/graph helpers consume. */
|
|
21
|
+
export function descriptorsByType(registry) {
|
|
22
|
+
return Object.fromEntries(Object.entries(registry).map(([type, plugin]) => [type, plugin.descriptor]));
|
|
23
|
+
}
|
|
24
|
+
/** The built-in registry's descriptor map (the common case; registry-aware code derives its own). */
|
|
25
|
+
export const pluginsByType = descriptorsByType(serverPluginsByType);
|
|
26
|
+
/**
|
|
27
|
+
* The job-hash parameter input for an asset: the plugin-declared
|
|
28
|
+
* processing-relevant subset when the plugin distinguishes one (chroma-key-
|
|
29
|
+
* video: keying + references are entry-only), else the full parameter value.
|
|
30
|
+
*/
|
|
31
|
+
export function processingParametersOf(plugin, parameters) {
|
|
32
|
+
return plugin.processingParameters ? plugin.processingParameters(parameters) : parameters;
|
|
33
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { AudioPreset } from '../../core/index.js';
|
|
2
|
+
import type { ResolvedFundusConfig } from '../config-loader.js';
|
|
3
|
+
/** The full ffmpeg argument list for one audio transcode (pure, testable). */
|
|
4
|
+
export declare function buildAudioTranscodeArguments(options: {
|
|
5
|
+
sourcePath: string;
|
|
6
|
+
targetPath: string;
|
|
7
|
+
preset: AudioPreset;
|
|
8
|
+
}): string[];
|
|
9
|
+
export interface AudioProcessingOutput {
|
|
10
|
+
durationMs: number;
|
|
11
|
+
}
|
|
12
|
+
export declare function processAudioFile(options: {
|
|
13
|
+
sourcePath: string;
|
|
14
|
+
targetPath: string;
|
|
15
|
+
preset: AudioPreset;
|
|
16
|
+
config: ResolvedFundusConfig;
|
|
17
|
+
}): Promise<AudioProcessingOutput>;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The ffmpeg transcode step of the audio plugin: one audio-only proxy per
|
|
3
|
+
* preset (opus/webm or aac/m4a). Deterministic like the video step:
|
|
4
|
+
* metadata stripped, bitexact muxing.
|
|
5
|
+
*/
|
|
6
|
+
import { mkdir } from 'node:fs/promises';
|
|
7
|
+
import { dirname } from 'node:path';
|
|
8
|
+
import { ffmpegBinaryPath, runToolchainBinary } from './ffmpeg.js';
|
|
9
|
+
import { probeMedia } from './media-probe.js';
|
|
10
|
+
const ENCODER_BY_CODEC = {
|
|
11
|
+
opus: 'libopus',
|
|
12
|
+
aac: 'aac'
|
|
13
|
+
};
|
|
14
|
+
/** The full ffmpeg argument list for one audio transcode (pure, testable). */
|
|
15
|
+
export function buildAudioTranscodeArguments(options) {
|
|
16
|
+
return [
|
|
17
|
+
'-hide_banner',
|
|
18
|
+
'-loglevel',
|
|
19
|
+
'error',
|
|
20
|
+
'-y',
|
|
21
|
+
'-i',
|
|
22
|
+
options.sourcePath,
|
|
23
|
+
'-vn',
|
|
24
|
+
'-c:a',
|
|
25
|
+
ENCODER_BY_CODEC[options.preset.codec],
|
|
26
|
+
'-b:a',
|
|
27
|
+
`${options.preset.bitrateKbps}k`,
|
|
28
|
+
'-map_metadata',
|
|
29
|
+
'-1',
|
|
30
|
+
'-fflags',
|
|
31
|
+
'+bitexact',
|
|
32
|
+
'-flags:a',
|
|
33
|
+
'+bitexact',
|
|
34
|
+
options.targetPath
|
|
35
|
+
];
|
|
36
|
+
}
|
|
37
|
+
export async function processAudioFile(options) {
|
|
38
|
+
await mkdir(dirname(options.targetPath), { recursive: true });
|
|
39
|
+
await runToolchainBinary(ffmpegBinaryPath(options.config), buildAudioTranscodeArguments({
|
|
40
|
+
sourcePath: options.sourcePath,
|
|
41
|
+
targetPath: options.targetPath,
|
|
42
|
+
preset: options.preset
|
|
43
|
+
}));
|
|
44
|
+
const output = await probeMedia(options.targetPath, options.config);
|
|
45
|
+
return { durationMs: output.durationMs };
|
|
46
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ResolvedFundusConfig } from '../config-loader.js';
|
|
2
|
+
export declare function ffmpegBinaryPath(config: ResolvedFundusConfig): string;
|
|
3
|
+
export declare function ffprobeBinaryPath(config: ResolvedFundusConfig): string;
|
|
4
|
+
/**
|
|
5
|
+
* Run a toolchain binary with an argument array (never a shell string) and
|
|
6
|
+
* return its stdout. A non-zero exit throws with the stderr tail — ffmpeg
|
|
7
|
+
* writes its diagnostics there.
|
|
8
|
+
*/
|
|
9
|
+
export declare function runToolchainBinary(binaryPath: string, commandArguments: string[]): Promise<string>;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The ffmpeg/ffprobe toolchain layer: binary resolution (bundled `-static`
|
|
3
|
+
* packages by default, with host-config overrides) and a spawn helper.
|
|
4
|
+
* Since proxies are committed, this toolchain is only needed where
|
|
5
|
+
* assets are authored/processed — host builds and CI never invoke it.
|
|
6
|
+
*/
|
|
7
|
+
import ffmpegStaticPath from 'ffmpeg-static';
|
|
8
|
+
import { path as ffprobeStaticPath } from 'ffprobe-static';
|
|
9
|
+
import { basename } from 'node:path';
|
|
10
|
+
import { execFile } from 'node:child_process';
|
|
11
|
+
import { promisify } from 'node:util';
|
|
12
|
+
export function ffmpegBinaryPath(config) {
|
|
13
|
+
if (config.plugins.video.ffmpegPath)
|
|
14
|
+
return config.plugins.video.ffmpegPath;
|
|
15
|
+
if (!ffmpegStaticPath) {
|
|
16
|
+
throw new Error('ffmpeg-static provides no binary for this platform — set "plugins.video.ffmpegPath" in fundus.config.ts.');
|
|
17
|
+
}
|
|
18
|
+
return ffmpegStaticPath;
|
|
19
|
+
}
|
|
20
|
+
export function ffprobeBinaryPath(config) {
|
|
21
|
+
if (config.plugins.video.ffprobePath)
|
|
22
|
+
return config.plugins.video.ffprobePath;
|
|
23
|
+
if (!ffprobeStaticPath) {
|
|
24
|
+
throw new Error('ffprobe-static provides no binary for this platform — set "plugins.video.ffprobePath" in fundus.config.ts.');
|
|
25
|
+
}
|
|
26
|
+
return ffprobeStaticPath;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Run a toolchain binary with an argument array (never a shell string) and
|
|
30
|
+
* return its stdout. A non-zero exit throws with the stderr tail — ffmpeg
|
|
31
|
+
* writes its diagnostics there.
|
|
32
|
+
*/
|
|
33
|
+
export async function runToolchainBinary(binaryPath, commandArguments) {
|
|
34
|
+
try {
|
|
35
|
+
const { stdout } = await promisify(execFile)(binaryPath, commandArguments, {
|
|
36
|
+
encoding: 'utf8',
|
|
37
|
+
maxBuffer: 16 * 1024 * 1024
|
|
38
|
+
});
|
|
39
|
+
return stdout;
|
|
40
|
+
}
|
|
41
|
+
catch (cause) {
|
|
42
|
+
const failure = cause;
|
|
43
|
+
const diagnostics = (failure.stderr ?? failure.message)
|
|
44
|
+
.trim()
|
|
45
|
+
.split('\n')
|
|
46
|
+
.slice(-12)
|
|
47
|
+
.join('\n');
|
|
48
|
+
throw new Error(`${basename(binaryPath)} exited with code ${failure.code ?? 'unknown'}:\n${diagnostics}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ImagePreset } from '../../core/index.js';
|
|
2
|
+
export interface ImageProcessingOutput {
|
|
3
|
+
width: number;
|
|
4
|
+
height: number;
|
|
5
|
+
}
|
|
6
|
+
export declare function processImageFile(options: {
|
|
7
|
+
sourcePath: string;
|
|
8
|
+
targetPath: string;
|
|
9
|
+
preset: ImagePreset;
|
|
10
|
+
targetWidth?: number;
|
|
11
|
+
}): Promise<ImageProcessingOutput>;
|
|
12
|
+
/** Pixel dimensions of an existing image file (for already-fresh proxies). */
|
|
13
|
+
export declare function readImageDimensions(filePath: string): Promise<{
|
|
14
|
+
width: number;
|
|
15
|
+
height: number;
|
|
16
|
+
}>;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The sharp step shared by the image and slice plugins: optional resize to a
|
|
3
|
+
* target width, format conversion per preset, write the proxy file. Returns
|
|
4
|
+
* the delivered (post-resize) dimensions.
|
|
5
|
+
*/
|
|
6
|
+
import { mkdir } from 'node:fs/promises';
|
|
7
|
+
import { dirname } from 'node:path';
|
|
8
|
+
import sharp from 'sharp';
|
|
9
|
+
export async function processImageFile(options) {
|
|
10
|
+
await mkdir(dirname(options.targetPath), { recursive: true });
|
|
11
|
+
let pipeline = sharp(options.sourcePath);
|
|
12
|
+
if (options.targetWidth !== undefined) {
|
|
13
|
+
pipeline = pipeline.resize({ width: options.targetWidth });
|
|
14
|
+
}
|
|
15
|
+
pipeline = pipeline.toFormat(options.preset.format, options.preset.quality !== undefined ? { quality: options.preset.quality } : {});
|
|
16
|
+
const info = await pipeline.toFile(options.targetPath);
|
|
17
|
+
return { width: info.width, height: info.height };
|
|
18
|
+
}
|
|
19
|
+
/** Pixel dimensions of an existing image file (for already-fresh proxies). */
|
|
20
|
+
export async function readImageDimensions(filePath) {
|
|
21
|
+
const metadata = await sharp(filePath).metadata();
|
|
22
|
+
if (typeof metadata.width !== 'number' || typeof metadata.height !== 'number') {
|
|
23
|
+
throw new Error(`Could not read image dimensions of ${filePath}.`);
|
|
24
|
+
}
|
|
25
|
+
return { width: metadata.width, height: metadata.height };
|
|
26
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ffprobe wrapper: intrinsic dimensions (video streams) and duration of a
|
|
3
|
+
* media file — the metadata the manifest entries and the editor's output info
|
|
4
|
+
* carry (`VideoManifestEntry.width/height/durationMs`, `AudioManifestEntry.durationMs`).
|
|
5
|
+
*/
|
|
6
|
+
import type { ResolvedFundusConfig } from '../config-loader.js';
|
|
7
|
+
export interface MediaProbeResult {
|
|
8
|
+
/** Present when the file has a video stream. */
|
|
9
|
+
width?: number;
|
|
10
|
+
height?: number;
|
|
11
|
+
durationMs: number;
|
|
12
|
+
}
|
|
13
|
+
/** Parse ffprobe's JSON output (pure, testable without a binary). */
|
|
14
|
+
export declare function parseProbeOutput(json: string, filePath: string): MediaProbeResult;
|
|
15
|
+
export declare function probeMedia(filePath: string, config: ResolvedFundusConfig): Promise<MediaProbeResult>;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { ffprobeBinaryPath, runToolchainBinary } from './ffmpeg.js';
|
|
2
|
+
/** Parse ffprobe's JSON output (pure, testable without a binary). */
|
|
3
|
+
export function parseProbeOutput(json, filePath) {
|
|
4
|
+
const output = JSON.parse(json);
|
|
5
|
+
const videoStream = output.streams?.find((stream) => stream.codec_type === 'video' && stream.width && stream.height);
|
|
6
|
+
// Container duration first; some formats only report it per stream.
|
|
7
|
+
const durationSeconds = Number(output.format?.duration ?? output.streams?.find((stream) => stream.duration)?.duration);
|
|
8
|
+
if (!Number.isFinite(durationSeconds)) {
|
|
9
|
+
throw new Error(`Could not read the media duration of ${filePath}.`);
|
|
10
|
+
}
|
|
11
|
+
return {
|
|
12
|
+
...(videoStream ? { width: videoStream.width, height: videoStream.height } : {}),
|
|
13
|
+
durationMs: Math.round(durationSeconds * 1000)
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export async function probeMedia(filePath, config) {
|
|
17
|
+
const json = await runToolchainBinary(ffprobeBinaryPath(config), [
|
|
18
|
+
'-v',
|
|
19
|
+
'error',
|
|
20
|
+
'-show_entries',
|
|
21
|
+
'stream=codec_type,width,height,duration:format=duration',
|
|
22
|
+
'-of',
|
|
23
|
+
'json',
|
|
24
|
+
filePath
|
|
25
|
+
]);
|
|
26
|
+
return parseProbeOutput(json, filePath);
|
|
27
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { type AssetId, type AssetRecord, type Library } from '../../core/index.js';
|
|
2
|
+
import type { ResolvedFundusConfig } from '../config-loader.js';
|
|
3
|
+
import type { AssetProxyInfo, ProxyOutputInfo, ServerPluginRegistry } from '../plugins/contract.js';
|
|
4
|
+
export type { AssetProxyInfo, ProxyOutputInfo } from '../plugins/contract.js';
|
|
5
|
+
/** Whether a file name in the proxy directory is (shaped like) a fundus proxy. */
|
|
6
|
+
export declare function isProxyFileName(fileName: string): boolean;
|
|
7
|
+
/** Whether a shaped proxy file belongs to one specific asset id. */
|
|
8
|
+
export declare function isProxyFileNameFor(assetId: string, fileName: string): boolean;
|
|
9
|
+
export interface ProcessingReport {
|
|
10
|
+
/** Asset ids whose proxy was (re)generated in this run. */
|
|
11
|
+
processed: string[];
|
|
12
|
+
/** Asset ids whose proxy was already current (name check hit). */
|
|
13
|
+
reused: string[];
|
|
14
|
+
/** Orphaned proxy file names removed from the proxy directory. */
|
|
15
|
+
pruned: string[];
|
|
16
|
+
/** Per delivered asset: the current proxy and its output metadata. */
|
|
17
|
+
proxies: Record<string, AssetProxyInfo>;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* The proxy file name an asset's current raw content + parameters demand.
|
|
21
|
+
* Throws when the parameters are invalid or reference an unknown preset.
|
|
22
|
+
*
|
|
23
|
+
* The hash input is the *effective* processing input: the asset's
|
|
24
|
+
* processing-relevant parameters (the plugin-declared subset — entry-only data
|
|
25
|
+
* like chroma-key tuning and references must not rename the proxy)
|
|
26
|
+
* plus the preset's **resolved settings** — parameters only carry the preset
|
|
27
|
+
* *name*, so hashing them alone would keep serving stale proxy bytes after a
|
|
28
|
+
* preset's quality/format is edited in the host config.
|
|
29
|
+
*/
|
|
30
|
+
export declare function expectedProxyFileName(assetId: string, record: AssetRecord, config: ResolvedFundusConfig, registry?: ServerPluginRegistry): string;
|
|
31
|
+
/** Expected proxy path relative to proxyDir, mirroring the raw asset folder. */
|
|
32
|
+
export declare function expectedProxyRelativePath(assetId: string, record: AssetRecord, config: ResolvedFundusConfig, registry?: ServerPluginRegistry): string;
|
|
33
|
+
/**
|
|
34
|
+
* Output metadata of an existing proxy file, probed per the owning plugin's
|
|
35
|
+
* strategy: image-backed proxies via sharp, media proxies via ffprobe.
|
|
36
|
+
*/
|
|
37
|
+
export declare function readProxyOutputInfo(type: string, proxyPath: string, config: ResolvedFundusConfig, registry?: ServerPluginRegistry): Promise<ProxyOutputInfo>;
|
|
38
|
+
/** Read one asset's current proxy projection, or null when its expected file is absent. */
|
|
39
|
+
export declare function readCurrentProxy(assetId: string, record: AssetRecord, config: ResolvedFundusConfig, registry?: ServerPluginRegistry): Promise<AssetProxyInfo | null>;
|
|
40
|
+
/**
|
|
41
|
+
* The delivered set: every explicit or passive member of ≥ 1 manifest —
|
|
42
|
+
* derived by the shared membership computation. Assets outside
|
|
43
|
+
* it are inert.
|
|
44
|
+
*/
|
|
45
|
+
export declare function deliveredAssetIds(library: Library, registry?: ServerPluginRegistry): Set<AssetId>;
|
|
46
|
+
/**
|
|
47
|
+
* Ensure every delivered asset's proxy exists (processing stale/missing ones)
|
|
48
|
+
* and prune orphans. Assets outside the delivered set are skipped entirely.
|
|
49
|
+
*/
|
|
50
|
+
export declare function processAssets(config: ResolvedFundusConfig, library: Library, registry?: ServerPluginRegistry): Promise<ProcessingReport>;
|