davinci-resolve-mcp 2.57.4 → 2.58.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +20 -0
- package/CHANGELOG.md +197 -2
- package/LICENSE +1 -1
- package/README.md +73 -1
- package/bin/davinci-resolve-advanced-mcp.mjs +20 -0
- package/docs/README.md +19 -0
- package/docs/SKILL.md +62 -1
- package/docs/contributing.md +1 -1
- package/docs/guides/control-panel.md +9 -0
- package/docs/kernels/README.md +21 -0
- package/docs/kernels/audio-fairlight-kernel.md +22 -0
- package/docs/kernels/color-grade-kernel.md +51 -0
- package/docs/kernels/fusion-composition-kernel.md +17 -0
- package/docs/kernels/media-pool-ingest-kernel.md +20 -0
- package/docs/kernels/render-deliver-kernel.md +32 -0
- package/docs/kernels/timeline-conform-interchange-kernel.md +39 -0
- package/docs/kernels/timeline-edit-kernel.md +17 -0
- package/docs/process/release-process.md +29 -7
- package/docs/reference/api-coverage.md +1 -1
- package/docs/reference/api-limitations.md +38 -1
- package/install.py +27 -4
- package/package.json +23 -2
- package/resolve-advanced/README.md +194 -0
- package/resolve-advanced/package.json +50 -0
- package/resolve-advanced/server/black-balance.mjs +92 -0
- package/resolve-advanced/server/capabilities.mjs +86 -0
- package/resolve-advanced/server/cdl-io.mjs +76 -0
- package/resolve-advanced/server/contrast-normalize.mjs +180 -0
- package/resolve-advanced/server/db-patch.mjs +91 -0
- package/resolve-advanced/server/deliverable-entities.mjs +86 -0
- package/resolve-advanced/server/deliverable-qc.mjs +270 -0
- package/resolve-advanced/server/editorial.mjs +370 -0
- package/resolve-advanced/server/exposure-level.mjs +129 -0
- package/resolve-advanced/server/extract-frames.mjs +196 -0
- package/resolve-advanced/server/ffprobe-media.mjs +88 -0
- package/resolve-advanced/server/gamut-legal.mjs +112 -0
- package/resolve-advanced/server/grade-body-patch.mjs +246 -0
- package/resolve-advanced/server/grade-transfer.mjs +66 -0
- package/resolve-advanced/server/group-grade-read.mjs +137 -0
- package/resolve-advanced/server/index.mjs +105 -0
- package/resolve-advanced/server/lib.mjs +84 -0
- package/resolve-advanced/server/libs.mjs +65 -0
- package/resolve-advanced/server/lineage-db.mjs +479 -0
- package/resolve-advanced/server/lut-apply.mjs +61 -0
- package/resolve-advanced/server/match-to-reference.mjs +173 -0
- package/resolve-advanced/server/media-inventory.mjs +169 -0
- package/resolve-advanced/server/media-ops.mjs +243 -0
- package/resolve-advanced/server/node-meta-db.mjs +62 -0
- package/resolve-advanced/server/node-provenance.mjs +102 -0
- package/resolve-advanced/server/offline-ref-db.mjs +420 -0
- package/resolve-advanced/server/offline-ref.mjs +179 -0
- package/resolve-advanced/server/project-db.mjs +233 -0
- package/resolve-advanced/server/provenance-audit.mjs +210 -0
- package/resolve-advanced/server/qc-frame.mjs +224 -0
- package/resolve-advanced/server/qc-sampler.mjs +67 -0
- package/resolve-advanced/server/readback.mjs +77 -0
- package/resolve-advanced/server/render-manifest.mjs +93 -0
- package/resolve-advanced/server/reverse-clip-db.mjs +270 -0
- package/resolve-advanced/server/runner-apply-contract.mjs +64 -0
- package/resolve-advanced/server/runner.mjs +244 -0
- package/resolve-advanced/server/saturation-match.mjs +108 -0
- package/resolve-advanced/server/scope-read.mjs +257 -0
- package/resolve-advanced/server/season-look.mjs +96 -0
- package/resolve-advanced/server/shot-intent.mjs +82 -0
- package/resolve-advanced/server/shot-match.mjs +147 -0
- package/resolve-advanced/server/skin-match.mjs +205 -0
- package/resolve-advanced/server/spec-compile.mjs +217 -0
- package/resolve-advanced/server/tone-curve-transfer.mjs +184 -0
- package/resolve-advanced/server/tool-catalog.mjs +265 -0
- package/resolve-advanced/server/tools/audio.mjs +54 -0
- package/resolve-advanced/server/tools/audio_plan.mjs +58 -0
- package/resolve-advanced/server/tools/capabilities.mjs +16 -0
- package/resolve-advanced/server/tools/color_trace.mjs +165 -0
- package/resolve-advanced/server/tools/conform.mjs +291 -0
- package/resolve-advanced/server/tools/deliverable.mjs +105 -0
- package/resolve-advanced/server/tools/drp.mjs +232 -0
- package/resolve-advanced/server/tools/drt.mjs +200 -0
- package/resolve-advanced/server/tools/drx.mjs +943 -0
- package/resolve-advanced/server/tools/editorial.mjs +69 -0
- package/resolve-advanced/server/tools/fairlight.mjs +108 -0
- package/resolve-advanced/server/tools/fusion.mjs +64 -0
- package/resolve-advanced/server/tools/media.mjs +102 -0
- package/resolve-advanced/server/tools/offline_ref.mjs +123 -0
- package/resolve-advanced/server/tools/pipeline.mjs +137 -0
- package/resolve-advanced/server/tools/project_db.mjs +200 -0
- package/resolve-advanced/server/tools/project_read.mjs +212 -0
- package/resolve-advanced/server/tools/provenance.mjs +73 -0
- package/resolve-advanced/server/verify-grade.mjs +113 -0
- package/resolve-advanced/server/white-balance-match.mjs +122 -0
- package/resolve-advanced/vendor/audio/format-converter.js +330 -0
- package/resolve-advanced/vendor/audio/split.js +173 -0
- package/resolve-advanced/vendor/audio/trim.js +188 -0
- package/resolve-advanced/vendor/audio-fairlight/index.js +391 -0
- package/resolve-advanced/vendor/conform-qc/adapters/frame-sampler.js +56 -0
- package/resolve-advanced/vendor/conform-qc/adapters/local-ffmpeg-sampler.js +65 -0
- package/resolve-advanced/vendor/conform-qc/adapters/rendernode-ffmpeg-sampler.js +50 -0
- package/resolve-advanced/vendor/conform-qc/adapters/resolve/driver.py +176 -0
- package/resolve-advanced/vendor/conform-qc/adapters/resolve-driver.js +61 -0
- package/resolve-advanced/vendor/conform-qc/adapters/resolve-headless-driver.js +86 -0
- package/resolve-advanced/vendor/conform-qc/adapters/vision-validator.js +75 -0
- package/resolve-advanced/vendor/conform-qc/cli.js +63 -0
- package/resolve-advanced/vendor/conform-qc/compare/advisory.js +106 -0
- package/resolve-advanced/vendor/conform-qc/compare/decode.js +36 -0
- package/resolve-advanced/vendor/conform-qc/compare/index.js +61 -0
- package/resolve-advanced/vendor/conform-qc/compare/locate.js +70 -0
- package/resolve-advanced/vendor/conform-qc/compare/metrics.js +409 -0
- package/resolve-advanced/vendor/conform-qc/docs/runbook.md +72 -0
- package/resolve-advanced/vendor/conform-qc/index.js +49 -0
- package/resolve-advanced/vendor/conform-qc/knowledge/index.js +110 -0
- package/resolve-advanced/vendor/conform-qc/ops/conform-core.js +137 -0
- package/resolve-advanced/vendor/conform-qc/ops/conform-diff.js +109 -0
- package/resolve-advanced/vendor/conform-qc/ops/insert.js +108 -0
- package/resolve-advanced/vendor/conform-qc/ops/local-conform.js +61 -0
- package/resolve-advanced/vendor/conform-qc/ops/patch.js +80 -0
- package/resolve-advanced/vendor/conform-qc/ops/trigger.js +32 -0
- package/resolve-advanced/vendor/conform-qc/ops/verify.js +105 -0
- package/resolve-advanced/vendor/conform-qc/ops/workflow.js +30 -0
- package/resolve-advanced/vendor/conform-qc/oracle/emulate.js +155 -0
- package/resolve-advanced/vendor/conform-qc/oracle/index.js +38 -0
- package/resolve-advanced/vendor/conform-qc/oracle/resolve.js +196 -0
- package/resolve-advanced/vendor/conform-qc/package.json +16 -0
- package/resolve-advanced/vendor/conform-qc/packaging/emit-aaf.js +30 -0
- package/resolve-advanced/vendor/conform-qc/packaging/emit-fcp7.js +64 -0
- package/resolve-advanced/vendor/conform-qc/packaging/index.js +169 -0
- package/resolve-advanced/vendor/conform-qc/packaging/media-ops.js +56 -0
- package/resolve-advanced/vendor/conform-qc/packaging/otio.js +50 -0
- package/resolve-advanced/vendor/conform-qc/packaging/relink.js +50 -0
- package/resolve-advanced/vendor/conform-qc/packaging/surgical-relink.js +195 -0
- package/resolve-advanced/vendor/conform-qc/parse/index.js +54 -0
- package/resolve-advanced/vendor/conform-qc/parse/merge-dto.js +27 -0
- package/resolve-advanced/vendor/conform-qc/parse/xmeml-geometry.js +256 -0
- package/resolve-advanced/vendor/conform-qc/reference/burnin-ocr.js +100 -0
- package/resolve-advanced/vendor/conform-qc/reference/sidecar.js +37 -0
- package/resolve-advanced/vendor/conform-qc/repair/analysis.js +62 -0
- package/resolve-advanced/vendor/conform-qc/repair/index.js +78 -0
- package/resolve-advanced/vendor/conform-qc/repair/media-analysis.js +139 -0
- package/resolve-advanced/vendor/conform-qc/repair/media-index.js +135 -0
- package/resolve-advanced/vendor/conform-qc/repair/strategies.js +250 -0
- package/resolve-advanced/vendor/conform-qc/report/index.js +133 -0
- package/resolve-advanced/vendor/conform-qc/roles/index.js +96 -0
- package/resolve-advanced/vendor/conform-qc/synthetic/generate.js +233 -0
- package/resolve-advanced/vendor/conform-qc/test/advisory.test.js +85 -0
- package/resolve-advanced/vendor/conform-qc/test/cli.test.js +39 -0
- package/resolve-advanced/vendor/conform-qc/test/compare.test.js +152 -0
- package/resolve-advanced/vendor/conform-qc/test/conform-diff.test.js +70 -0
- package/resolve-advanced/vendor/conform-qc/test/emulate.test.js +51 -0
- package/resolve-advanced/vendor/conform-qc/test/frame-sampler.test.js +30 -0
- package/resolve-advanced/vendor/conform-qc/test/geometry.test.js +112 -0
- package/resolve-advanced/vendor/conform-qc/test/knowledge.test.js +56 -0
- package/resolve-advanced/vendor/conform-qc/test/lifecycle.test.js +110 -0
- package/resolve-advanced/vendor/conform-qc/test/local-ffmpeg-sampler.test.js +66 -0
- package/resolve-advanced/vendor/conform-qc/test/locate.test.js +63 -0
- package/resolve-advanced/vendor/conform-qc/test/media-analysis.test.js +63 -0
- package/resolve-advanced/vendor/conform-qc/test/media-index-normalized.test.js +65 -0
- package/resolve-advanced/vendor/conform-qc/test/media-index-origin.test.js +53 -0
- package/resolve-advanced/vendor/conform-qc/test/metrics-fast.test.js +91 -0
- package/resolve-advanced/vendor/conform-qc/test/oracle-reverse.test.js +86 -0
- package/resolve-advanced/vendor/conform-qc/test/oracle.test.js +136 -0
- package/resolve-advanced/vendor/conform-qc/test/p0-acceptance.test.js +60 -0
- package/resolve-advanced/vendor/conform-qc/test/p1-acceptance.test.js +75 -0
- package/resolve-advanced/vendor/conform-qc/test/p45-patch-acceptance.test.js +102 -0
- package/resolve-advanced/vendor/conform-qc/test/packaging.test.js +133 -0
- package/resolve-advanced/vendor/conform-qc/test/parse-stubs.test.js +28 -0
- package/resolve-advanced/vendor/conform-qc/test/repair-strategies.test.js +128 -0
- package/resolve-advanced/vendor/conform-qc/test/repair.test.js +84 -0
- package/resolve-advanced/vendor/conform-qc/test/report.test.js +52 -0
- package/resolve-advanced/vendor/conform-qc/test/resolve-readback.test.js +71 -0
- package/resolve-advanced/vendor/conform-qc/test/roles.test.js +107 -0
- package/resolve-advanced/vendor/conform-qc/test/runbook.test.js +30 -0
- package/resolve-advanced/vendor/conform-qc/test/scale-residual.test.js +40 -0
- package/resolve-advanced/vendor/conform-qc/test/sidecar.test.js +51 -0
- package/resolve-advanced/vendor/conform-qc/test/smoke.test.js +87 -0
- package/resolve-advanced/vendor/conform-qc/test/surgical-relink.test.js +73 -0
- package/resolve-advanced/vendor/conform-qc/test/synthetic.test.js +98 -0
- package/resolve-advanced/vendor/conform-qc/test/verify.test.js +93 -0
- package/resolve-advanced/vendor/conform-qc/test/vision-validator.test.js +43 -0
- package/resolve-advanced/vendor/conform-qc/test/workflow-driver.test.js +46 -0
- package/resolve-advanced/vendor/conform-qc/toolset/index.js +53 -0
- package/resolve-advanced/vendor/drp-format/README.md +116 -0
- package/resolve-advanced/vendor/drp-format/__tests__/_resolve-verify.js +39 -0
- package/resolve-advanced/vendor/drp-format/__tests__/add-media-clip.test.js +30 -0
- package/resolve-advanced/vendor/drp-format/__tests__/assemble-timeline.test.js +68 -0
- package/resolve-advanced/vendor/drp-format/__tests__/author-project.test.js +57 -0
- package/resolve-advanced/vendor/drp-format/__tests__/composition-text.test.js +98 -0
- package/resolve-advanced/vendor/drp-format/__tests__/diff.test.js +272 -0
- package/resolve-advanced/vendor/drp-format/__tests__/effect-filters-compressed.test.js +143 -0
- package/resolve-advanced/vendor/drp-format/__tests__/fixtures/p0-3-session30-empty-effectfilters.drp +0 -0
- package/resolve-advanced/vendor/drp-format/__tests__/fixtures/retimed-timemap-50pct.hex +1 -0
- package/resolve-advanced/vendor/drp-format/__tests__/fixtures/retimed-timemap-dynamic.hex +1 -0
- package/resolve-advanced/vendor/drp-format/__tests__/inject-grades.test.js +238 -0
- package/resolve-advanced/vendor/drp-format/__tests__/keyed-dict.test.js +85 -0
- package/resolve-advanced/vendor/drp-format/__tests__/lut-refs.test.js +119 -0
- package/resolve-advanced/vendor/drp-format/__tests__/media-blobs.test.js +25 -0
- package/resolve-advanced/vendor/drp-format/__tests__/media-timemap.test.js +119 -0
- package/resolve-advanced/vendor/drp-format/__tests__/place-fusion-title.test.js +52 -0
- package/resolve-advanced/vendor/drp-format/__tests__/place-generator.test.js +40 -0
- package/resolve-advanced/vendor/drp-format/__tests__/place-transition.test.js +48 -0
- package/resolve-advanced/vendor/drp-format/__tests__/protobuf-wire.test.js +64 -0
- package/resolve-advanced/vendor/drp-format/__tests__/relink-media.test.js +106 -0
- package/resolve-advanced/vendor/drp-format/__tests__/splice-clips.test.js +256 -0
- package/resolve-advanced/vendor/drp-format/assemble-timeline.js +65 -0
- package/resolve-advanced/vendor/drp-format/author-project.js +118 -0
- package/resolve-advanced/vendor/drp-format/composition-text.js +213 -0
- package/resolve-advanced/vendor/drp-format/curves-encoder.js +763 -0
- package/resolve-advanced/vendor/drp-format/diff.js +310 -0
- package/resolve-advanced/vendor/drp-format/drp-packager.js +201 -0
- package/resolve-advanced/vendor/drp-format/drp-validator.js +809 -0
- package/resolve-advanced/vendor/drp-format/effect-encoder.js +640 -0
- package/resolve-advanced/vendor/drp-format/extract-lut-refs.js +119 -0
- package/resolve-advanced/vendor/drp-format/grade-encoder.example.js +133 -0
- package/resolve-advanced/vendor/drp-format/grade-encoder.js +401 -0
- package/resolve-advanced/vendor/drp-format/grade-encoder.test.js +171 -0
- package/resolve-advanced/vendor/drp-format/grade-encoder.verify.js +249 -0
- package/resolve-advanced/vendor/drp-format/grade-node-extractor.js +495 -0
- package/resolve-advanced/vendor/drp-format/grade-parameter-decoder.js +774 -0
- package/resolve-advanced/vendor/drp-format/index.js +174 -0
- package/resolve-advanced/vendor/drp-format/inject-grades.js +219 -0
- package/resolve-advanced/vendor/drp-format/keyed-dict.js +227 -0
- package/resolve-advanced/vendor/drp-format/marker-encoder.js +532 -0
- package/resolve-advanced/vendor/drp-format/media-blobs.js +53 -0
- package/resolve-advanced/vendor/drp-format/media-timemap.js +164 -0
- package/resolve-advanced/vendor/drp-format/mp-folder-builder.js +293 -0
- package/resolve-advanced/vendor/drp-format/node-tree-encoder.js +1246 -0
- package/resolve-advanced/vendor/drp-format/package.json +18 -0
- package/resolve-advanced/vendor/drp-format/place-fusion-title.js +131 -0
- package/resolve-advanced/vendor/drp-format/place-generator.js +77 -0
- package/resolve-advanced/vendor/drp-format/place-transition.js +91 -0
- package/resolve-advanced/vendor/drp-format/power-window-encoder.js +776 -0
- package/resolve-advanced/vendor/drp-format/protobuf-wire.js +111 -0
- package/resolve-advanced/vendor/drp-format/qualifier-encoder.js +706 -0
- package/resolve-advanced/vendor/drp-format/relink-media.js +218 -0
- package/resolve-advanced/vendor/drp-format/rich-title-encoder.js +447 -0
- package/resolve-advanced/vendor/drp-format/seq-container-builder.js +572 -0
- package/resolve-advanced/vendor/drp-format/seq-surgery.js +155 -0
- package/resolve-advanced/vendor/drp-format/splice-clips.js +445 -0
- package/resolve-advanced/vendor/drp-format/templates/empty-project.drp +0 -0
- package/resolve-advanced/vendor/drp-format/templates/fusion-title.xml +56 -0
- package/resolve-advanced/vendor/drp-format/templates/generator-solid-color.xml +21 -0
- package/resolve-advanced/vendor/drp-format/templates/media-clip-h264.drp +0 -0
- package/resolve-advanced/vendor/drp-format/templates/transition-cross-dissolve.xml +22 -0
- package/resolve-advanced/vendor/drp-format/tool-registry.descriptor.js +80 -0
- package/resolve-advanced/vendor/drp-format/utils/safe-archive.js +54 -0
- package/resolve-advanced/vendor/drp-format/xml-builder.js +759 -0
- package/resolve-advanced/vendor/drt-format/__tests__/_resolve-verify.js +21 -0
- package/resolve-advanced/vendor/drt-format/__tests__/build.test.js +113 -0
- package/resolve-advanced/vendor/drt-format/__tests__/multi-format.test.js +93 -0
- package/resolve-advanced/vendor/drt-format/__tests__/parse-real-resolve21.test.js +51 -0
- package/resolve-advanced/vendor/drt-format/__tests__/parse.test.js +134 -0
- package/resolve-advanced/vendor/drt-format/__tests__/resolve-versions.test.js +120 -0
- package/resolve-advanced/vendor/drt-format/__tests__/schema-fingerprint.test.js +62 -0
- package/resolve-advanced/vendor/drt-format/__tests__/smoke.test.js +27 -0
- package/resolve-advanced/vendor/drt-format/__tests__/validate.test.js +109 -0
- package/resolve-advanced/vendor/drt-format/capabilities.js +69 -0
- package/resolve-advanced/vendor/drt-format/capability-domains.js +56 -0
- package/resolve-advanced/vendor/drt-format/drt-builder.js +42 -0
- package/resolve-advanced/vendor/drt-format/drt-parser.js +164 -0
- package/resolve-advanced/vendor/drt-format/drt-validator.js +98 -0
- package/resolve-advanced/vendor/drt-format/index.js +45 -0
- package/resolve-advanced/vendor/drt-format/package.json +17 -0
- package/resolve-advanced/vendor/drt-format/resolve-versions.js +153 -0
- package/resolve-advanced/vendor/drt-format/schema-fingerprint.js +74 -0
- package/resolve-advanced/vendor/drt-format/templates/drt-project-shell.xml +124 -0
- package/resolve-advanced/vendor/drx-codec/__tests__/curves-roundtrip.test.js +148 -0
- package/resolve-advanced/vendor/drx-codec/__tests__/fixtures/p2-1-face-refinement.drx +58 -0
- package/resolve-advanced/vendor/drx-codec/__tests__/fixtures/p2-1-filmgrain.drx +58 -0
- package/resolve-advanced/vendor/drx-codec/__tests__/fixtures/p2-1-glow.drx +58 -0
- package/resolve-advanced/vendor/drx-codec/__tests__/fixtures/p2-1-lens-flare.drx +58 -0
- package/resolve-advanced/vendor/drx-codec/__tests__/fixtures/p2-3-beauty-25.drx +58 -0
- package/resolve-advanced/vendor/drx-codec/__tests__/fixtures/p2-3-beauty-75.drx +58 -0
- package/resolve-advanced/vendor/drx-codec/__tests__/fixtures/p5-1-node-no-lut.drx +58 -0
- package/resolve-advanced/vendor/drx-codec/__tests__/fixtures/p5-1-node-with-lut.drx +58 -0
- package/resolve-advanced/vendor/drx-codec/__tests__/hsl-curves-roundtrip.test.js +117 -0
- package/resolve-advanced/vendor/drx-codec/__tests__/lut-refs-roundtrip.test.js +112 -0
- package/resolve-advanced/vendor/drx-codec/__tests__/matte-roundtrip.test.js +167 -0
- package/resolve-advanced/vendor/drx-codec/__tests__/ofx-roundtrip.test.js +215 -0
- package/resolve-advanced/vendor/drx-codec/__tests__/ofx-slugs-discovery.test.js +149 -0
- package/resolve-advanced/vendor/drx-codec/__tests__/power-window-roundtrip.test.js +230 -0
- package/resolve-advanced/vendor/drx-codec/__tests__/qualifier-roundtrip.test.js +226 -0
- package/resolve-advanced/vendor/drx-codec/cdl-exporter.js +470 -0
- package/resolve-advanced/vendor/drx-codec/color-nlp-parser.js +833 -0
- package/resolve-advanced/vendor/drx-codec/data/color-nlp-action-mappings.json +2232 -0
- package/resolve-advanced/vendor/drx-codec/drx-generator.js +4123 -0
- package/resolve-advanced/vendor/drx-codec/drx-merger.js +320 -0
- package/resolve-advanced/vendor/drx-codec/drx-parser.js +1808 -0
- package/resolve-advanced/vendor/drx-codec/extract-custom-curves.js +215 -0
- package/resolve-advanced/vendor/drx-codec/extract-hsl-curves.js +180 -0
- package/resolve-advanced/vendor/drx-codec/extract-lut-refs.js +158 -0
- package/resolve-advanced/vendor/drx-codec/extract-matte-finesse.js +83 -0
- package/resolve-advanced/vendor/drx-codec/extract-ofx-params.js +226 -0
- package/resolve-advanced/vendor/drx-codec/extract-power-window.js +184 -0
- package/resolve-advanced/vendor/drx-codec/extract-qualifier.js +99 -0
- package/resolve-advanced/vendor/drx-codec/index.js +109 -0
- package/resolve-advanced/vendor/drx-codec/node-layout.js +249 -0
- package/resolve-advanced/vendor/drx-codec/resolved-drx-generator.js +393 -0
- package/resolve-advanced/vendor/drx-codec/tool-resolver.js +974 -0
- package/resolve-advanced/vendor/drx-codec/training-examples.js +548 -0
- package/resolve-advanced/vendor/drx-codec/vocabulary-intents.js +539 -0
- package/resolve-advanced/vendor/drx-parameters/CALIBRATION-STATUS.md +319 -0
- package/resolve-advanced/vendor/drx-parameters/DRX-VALUE-SCALING.md +251 -0
- package/resolve-advanced/vendor/drx-parameters/calibrated-ranges.json +17737 -0
- package/resolve-advanced/vendor/drx-parameters/corrector-types.js +120 -0
- package/resolve-advanced/vendor/drx-parameters/index.js +193 -0
- package/resolve-advanced/vendor/drx-parameters/parameter-codec.js +451 -0
- package/resolve-advanced/vendor/drx-parameters/parameter-ids.js +1292 -0
- package/resolve-advanced/vendor/drx-parameters/parameter-ranges.js +570 -0
- package/resolve-advanced/vendor/drx-parameters/parameter-validator.js +392 -0
- package/resolve-advanced/vendor/drx-parameters/resolvefx-registry.json +14973 -0
- package/resolve-advanced/vendor/drx-parameters/tool-registry.descriptor.js +38 -0
- package/resolve-advanced/vendor/drx-parameters/tools/drx-analyzer.js +371 -0
- package/resolve-advanced/vendor/fairlight/index.js +833 -0
- package/resolve-advanced/vendor/fusion-codec/composition-generator.js +401 -0
- package/resolve-advanced/vendor/fusion-codec/templates/blur-region.js +103 -0
- package/resolve-advanced/vendor/fusion-codec/templates/color-correct.js +87 -0
- package/resolve-advanced/vendor/fusion-codec/templates/film-grain.js +106 -0
- package/resolve-advanced/vendor/fusion-codec/templates/lower-third.js +241 -0
- package/resolve-advanced/vendor/fusion-codec/templates/picture-in-picture.js +188 -0
- package/resolve-advanced/vendor/fusion-codec/templates/text-overlay.js +161 -0
- package/resolve-advanced/vendor/fusion-codec/templates/title-card.js +184 -0
- package/resolve-advanced/vendor/fusion-codec/templates/vignette.js +107 -0
- package/resolve-advanced/vendor/fusion-codec/templates/watermark.js +101 -0
- package/scripts/agent-rules/README.md +68 -0
- package/src/analysis_dashboard.py +233 -1
- package/src/granular/common.py +1 -1
- package/src/server.py +479 -29
- package/src/utils/api_truth.py +117 -0
- package/src/utils/cut_ir.py +1 -1
- package/src/utils/failure_tracker.py +0 -2
- package/src/utils/timeline_xml.py +405 -0
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DaVinci Resolve Project (DRP) Generator
|
|
3
|
+
*
|
|
4
|
+
* This module provides utilities for generating DaVinci Resolve Project files (.drp)
|
|
5
|
+
* with support for timelines, media pools, color grades, effects, markers, and project settings.
|
|
6
|
+
*
|
|
7
|
+
* Includes comprehensive encoding/decoding for:
|
|
8
|
+
* - Grade parameters (color wheels, adjustments)
|
|
9
|
+
* - Node trees (serial, parallel, layer mixers)
|
|
10
|
+
* - Custom curves (RGB, Hue vs Sat/Lum, etc.)
|
|
11
|
+
* - Qualifiers (HSL, luminance, 3D)
|
|
12
|
+
* - Power windows (circle, linear, polygon, curve)
|
|
13
|
+
*
|
|
14
|
+
* @module drp-generator
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const xmlBuilder = require('./xml-builder');
|
|
18
|
+
const gradeEncoder = require('./grade-encoder');
|
|
19
|
+
const drpPackager = require('./drp-packager');
|
|
20
|
+
const effectEncoder = require('./effect-encoder');
|
|
21
|
+
const markerEncoder = require('./marker-encoder');
|
|
22
|
+
const injectGradesModule = require('./inject-grades');
|
|
23
|
+
const diffModule = require('./diff');
|
|
24
|
+
const extractLutRefsModule = require('./extract-lut-refs');
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* High-level convenience: build a full DRP buffer from a spec.
|
|
28
|
+
*
|
|
29
|
+
* Spec shape:
|
|
30
|
+
* {
|
|
31
|
+
* projectName: string,
|
|
32
|
+
* projectSettings: object, // → buildProjectXml input
|
|
33
|
+
* timelines: [{ name, frameRate, startTimecode, resolution,
|
|
34
|
+
* videoTracks, audioTracks, markers }],
|
|
35
|
+
* mediaPool: object | null,
|
|
36
|
+
* metadata: object,
|
|
37
|
+
* }
|
|
38
|
+
*
|
|
39
|
+
* Returns a Buffer ready to write to disk as a .drp file.
|
|
40
|
+
*/
|
|
41
|
+
async function buildDRP(spec = {}) {
|
|
42
|
+
const projectSettings = {
|
|
43
|
+
projectName: spec.projectName || 'Untitled Project',
|
|
44
|
+
...(spec.projectSettings || {}),
|
|
45
|
+
};
|
|
46
|
+
const projectXml = xmlBuilder.buildProjectXml(projectSettings);
|
|
47
|
+
return drpPackager.packageFullDRP({
|
|
48
|
+
projectXml,
|
|
49
|
+
timelines: spec.timelines || [],
|
|
50
|
+
mediaPool: spec.mediaPool || null,
|
|
51
|
+
metadata: {
|
|
52
|
+
projectName: projectSettings.projectName,
|
|
53
|
+
...(spec.metadata || {}),
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Advanced grade encoding/decoding modules
|
|
59
|
+
const gradeParameterDecoder = require('./grade-parameter-decoder');
|
|
60
|
+
const nodeTreeEncoder = require('./node-tree-encoder');
|
|
61
|
+
const curvesEncoder = require('./curves-encoder');
|
|
62
|
+
const qualifierEncoder = require('./qualifier-encoder');
|
|
63
|
+
const powerWindowEncoder = require('./power-window-encoder');
|
|
64
|
+
|
|
65
|
+
module.exports = {
|
|
66
|
+
// XML Builder functions
|
|
67
|
+
...xmlBuilder,
|
|
68
|
+
|
|
69
|
+
// Grade Encoder functions
|
|
70
|
+
...gradeEncoder,
|
|
71
|
+
|
|
72
|
+
// DRP Packager functions
|
|
73
|
+
...drpPackager,
|
|
74
|
+
|
|
75
|
+
// Effect Encoder functions
|
|
76
|
+
...effectEncoder,
|
|
77
|
+
|
|
78
|
+
// Marker Encoder functions
|
|
79
|
+
...markerEncoder,
|
|
80
|
+
|
|
81
|
+
// Grade Parameter Decoder functions
|
|
82
|
+
...gradeParameterDecoder,
|
|
83
|
+
|
|
84
|
+
// Node Tree Encoder functions
|
|
85
|
+
...nodeTreeEncoder,
|
|
86
|
+
|
|
87
|
+
// Curves Encoder functions
|
|
88
|
+
...curvesEncoder,
|
|
89
|
+
|
|
90
|
+
// Qualifier Encoder functions
|
|
91
|
+
...qualifierEncoder,
|
|
92
|
+
|
|
93
|
+
// Power Window Encoder functions
|
|
94
|
+
...powerWindowEncoder,
|
|
95
|
+
|
|
96
|
+
// High-level convenience entry point
|
|
97
|
+
buildDRP,
|
|
98
|
+
|
|
99
|
+
// Grade injection into an existing DRP (P0.1)
|
|
100
|
+
injectGrades: injectGradesModule.injectGrades,
|
|
101
|
+
|
|
102
|
+
// Structural diff of two DRPs (P0.2)
|
|
103
|
+
diff: diffModule.diff,
|
|
104
|
+
|
|
105
|
+
// Diff module internals — exposed so the MCP extract_node_graphs
|
|
106
|
+
// action can walk the clip index without duplicating the SeqContainer
|
|
107
|
+
// walker. Not part of the supported public API; subject to change.
|
|
108
|
+
diffInternals: diffModule._internals,
|
|
109
|
+
|
|
110
|
+
// Project-level LUT reference extraction (P5.2 — clf-lut-bridge)
|
|
111
|
+
extractProjectLUTRefs: extractLutRefsModule.extractProjectLUTRefs,
|
|
112
|
+
LUT_RECOGNIZED_SLOTS: extractLutRefsModule.RECOGNIZED_SLOTS,
|
|
113
|
+
|
|
114
|
+
// Place a Fusion Title (Text+) on a chosen video track — the #74 track-targeting
|
|
115
|
+
// bypass (clone-based; carries a real CompositionBA verbatim).
|
|
116
|
+
placeFusionTitle: require('./place-fusion-title').placeFusionTitle,
|
|
117
|
+
|
|
118
|
+
// Place a built-in generator (Solid Color, etc.) on a chosen video track.
|
|
119
|
+
placeGenerator: require('./place-generator').placeGenerator,
|
|
120
|
+
|
|
121
|
+
// Insert a cross-dissolve between two abutting clips (the one op the Resolve API can't do).
|
|
122
|
+
placeTransition: require('./place-transition').placeTransition,
|
|
123
|
+
|
|
124
|
+
// Read/replace a Fusion Title's text + style (font/size/justify) inside a CompositionBA blob.
|
|
125
|
+
decodeTitleText: require('./composition-text').decodeTitleText,
|
|
126
|
+
|
|
127
|
+
// Fixed-size Media Pool/timeline metadata blobs (Resolution/FrameRate/MediaFrameRate/MediaExtents).
|
|
128
|
+
mediaBlobs: require('./media-blobs'),
|
|
129
|
+
// Reader for the Media Pool keyed-dict metadata blobs (Geometry/Time/VideoMetadata/Proxy).
|
|
130
|
+
keyedDict: require('./keyed-dict'),
|
|
131
|
+
// Per-clip retime/speed map (MediaTimemapBA) — [u8 type][BE float64 seconds].
|
|
132
|
+
mediaTimemap: require('./media-timemap'),
|
|
133
|
+
// Generic protobuf wire codec for Radiometry / EffectFiltersBA / protobuf FieldsBlob.
|
|
134
|
+
protobufWire: require('./protobuf-wire'),
|
|
135
|
+
setTitleText: require('./composition-text').setTitleText,
|
|
136
|
+
setTitleInputs: require('./composition-text').setTitleInputs,
|
|
137
|
+
decodeTitleInputs: require('./composition-text').decodeTitleInputs,
|
|
138
|
+
|
|
139
|
+
// In-place clip edits (move/delete/trim, optional track-scoped ripple) — generalizes #74.
|
|
140
|
+
moveClip: require('./splice-clips').moveClip,
|
|
141
|
+
deleteClip: require('./splice-clips').deleteClip,
|
|
142
|
+
trimClip: require('./splice-clips').trimClip,
|
|
143
|
+
trimClipHead: require('./splice-clips').trimClipHead,
|
|
144
|
+
splitClip: require('./splice-clips').splitClip,
|
|
145
|
+
rippleTimeline: require('./splice-clips').rippleTimeline,
|
|
146
|
+
|
|
147
|
+
// Create a fresh, importable Resolve project (one empty timeline) from a bundled template.
|
|
148
|
+
createEmptyProject: require('./author-project').createEmptyProject,
|
|
149
|
+
|
|
150
|
+
// Build a full importable timeline from a declarative spec (titles/generators/transitions).
|
|
151
|
+
assembleTimeline: require('./assemble-timeline').assembleTimeline,
|
|
152
|
+
|
|
153
|
+
// Author a project with one media clip referencing an arbitrary h264 file, from scratch [P8].
|
|
154
|
+
addMediaClip: require('./author-project').addMediaClip,
|
|
155
|
+
|
|
156
|
+
// Offline media relink — repoint media to new paths in the Media Pool blobs (no Resolve).
|
|
157
|
+
relinkMedia: require('./relink-media').relinkMedia,
|
|
158
|
+
// Relink + fix cached specs (resolution/frames/fps) for a differently-formatted file [P8].
|
|
159
|
+
repointMedia: require('./relink-media').repointMedia,
|
|
160
|
+
|
|
161
|
+
// Convenience re-exports for common operations
|
|
162
|
+
xml: xmlBuilder,
|
|
163
|
+
grade: gradeEncoder,
|
|
164
|
+
packager: drpPackager,
|
|
165
|
+
effect: effectEncoder,
|
|
166
|
+
marker: markerEncoder,
|
|
167
|
+
|
|
168
|
+
// Advanced encoding/decoding modules
|
|
169
|
+
gradeDecoder: gradeParameterDecoder,
|
|
170
|
+
nodeTree: nodeTreeEncoder,
|
|
171
|
+
curves: curvesEncoder,
|
|
172
|
+
qualifier: qualifierEncoder,
|
|
173
|
+
window: powerWindowEncoder,
|
|
174
|
+
};
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DaVinci Resolve DRP Grade Injection
|
|
3
|
+
*
|
|
4
|
+
* Replaces the per-clip <Body> grade payload inside SeqContainer XMLs of a
|
|
5
|
+
* DRP archive with new payloads carried by DRX files. The DRX file's own
|
|
6
|
+
* <Body>HEX</Body> blob and the clip's <Body>HEX</Body> blob use the same
|
|
7
|
+
* marker-byte+zstd-compressed-protobuf encoding, so the injection is byte
|
|
8
|
+
* substitution — no protobuf understanding required at this layer.
|
|
9
|
+
*
|
|
10
|
+
* Resolve emits SeqContainer*.xml under "SeqContainer/" in exported DRPs.
|
|
11
|
+
* The the packager writes them under "Primary1/". This module
|
|
12
|
+
* discovers the folder by scanning zip entries so both layouts work.
|
|
13
|
+
*
|
|
14
|
+
* @module drp-format/inject-grades
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const fs = require('node:fs/promises');
|
|
18
|
+
const JSZip = require('jszip');
|
|
19
|
+
|
|
20
|
+
/** Find all SeqContainer*.xml entries regardless of folder name. */
|
|
21
|
+
function listSeqContainerEntries(zip) {
|
|
22
|
+
const out = [];
|
|
23
|
+
zip.forEach((relativePath, entry) => {
|
|
24
|
+
if (entry.dir) return;
|
|
25
|
+
// Match any "<anything>/SeqContainer<N>.xml" path.
|
|
26
|
+
if (/(^|\/)SeqContainer\d*\.xml$/.test(relativePath)) {
|
|
27
|
+
out.push(relativePath);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
return out;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Extract the hex blob from a DRX XML string. The blob is the same payload
|
|
35
|
+
* (marker 0x81 + zstd-compressed protobuf) that lives inside a clip's
|
|
36
|
+
* <Body> field in a SeqContainer.
|
|
37
|
+
*/
|
|
38
|
+
function extractDrxBodyHex(drxContent) {
|
|
39
|
+
// Use a non-greedy match across the entire XML — fast-xml-parser would
|
|
40
|
+
// be overkill and would normalize whitespace we don't control.
|
|
41
|
+
const match = drxContent.match(/<Body>([\s\S]*?)<\/Body>/);
|
|
42
|
+
if (!match) {
|
|
43
|
+
throw new Error('DRX content has no <Body>HEX</Body> block');
|
|
44
|
+
}
|
|
45
|
+
// The hex stream may contain whitespace from how Resolve formats DRX
|
|
46
|
+
// exports — strip everything that isn't hex.
|
|
47
|
+
return match[1].replace(/[^0-9a-fA-F]/g, '');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Replace the <Body> content of the first clip in seqXml whose DbId
|
|
52
|
+
* matches targetDbId. Returns null when no match (caller must decide
|
|
53
|
+
* whether that's an error or just a miss across multiple SeqContainers).
|
|
54
|
+
*
|
|
55
|
+
* Scoping: the regex anchors on `<Sm2TiVideoClip ... DbId="<id>"` and
|
|
56
|
+
* runs forward to the matching `</Sm2TiVideoClip>`. Inside that range we
|
|
57
|
+
* replace exactly one <Body>HEX</Body>. If a clip has no Body yet (a
|
|
58
|
+
* brand-new clip with no grade) we don't synthesize the surrounding
|
|
59
|
+
* LmVersionTable scaffolding — that's a builder responsibility, not an
|
|
60
|
+
* injector one. Callers wanting to add grades to clean clips should
|
|
61
|
+
* build the DRP fresh via buildDRP with the grade in the spec.
|
|
62
|
+
*/
|
|
63
|
+
function replaceBodyForClip(seqXml, targetDbId, newBodyHex) {
|
|
64
|
+
// 1. Locate the clip element opening tag.
|
|
65
|
+
// Sm2TiVideoClip is the post-monorepo tag (matches seq-container-builder).
|
|
66
|
+
// Older / different layouts may use Sm2VideoClip — accept both.
|
|
67
|
+
const clipOpenRe = new RegExp(
|
|
68
|
+
`<(Sm2TiVideoClip|Sm2VideoClip)([^>]*?)DbId="${escapeRegex(targetDbId)}"([^>]*)>`,
|
|
69
|
+
);
|
|
70
|
+
const openMatch = seqXml.match(clipOpenRe);
|
|
71
|
+
if (!openMatch) return null;
|
|
72
|
+
|
|
73
|
+
const clipTag = openMatch[1];
|
|
74
|
+
const openStart = openMatch.index;
|
|
75
|
+
const openEnd = openStart + openMatch[0].length;
|
|
76
|
+
|
|
77
|
+
// 2. Find the matching close tag, respecting potential nesting (a clip
|
|
78
|
+
// doesn't nest a clip of the same tag in practice — confirmed in the
|
|
79
|
+
// builder — but we walk depth anyway to be defensive).
|
|
80
|
+
const closeTag = `</${clipTag}>`;
|
|
81
|
+
const openTag = `<${clipTag}`;
|
|
82
|
+
let depth = 1;
|
|
83
|
+
let cursor = openEnd;
|
|
84
|
+
while (depth > 0) {
|
|
85
|
+
const nextOpen = seqXml.indexOf(openTag, cursor);
|
|
86
|
+
const nextClose = seqXml.indexOf(closeTag, cursor);
|
|
87
|
+
if (nextClose === -1) return null;
|
|
88
|
+
if (nextOpen !== -1 && nextOpen < nextClose) {
|
|
89
|
+
depth += 1;
|
|
90
|
+
cursor = nextOpen + openTag.length;
|
|
91
|
+
} else {
|
|
92
|
+
depth -= 1;
|
|
93
|
+
cursor = nextClose + closeTag.length;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
const clipEnd = cursor;
|
|
97
|
+
|
|
98
|
+
// 3. Within the clip's range, replace exactly one <Body>...</Body>.
|
|
99
|
+
const clipRange = seqXml.slice(openStart, clipEnd);
|
|
100
|
+
const bodyRe = /<Body>([\s\S]*?)<\/Body>/;
|
|
101
|
+
if (!bodyRe.test(clipRange)) return null;
|
|
102
|
+
const newClipRange = clipRange.replace(
|
|
103
|
+
bodyRe,
|
|
104
|
+
`<Body>${newBodyHex}</Body>`,
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
return seqXml.slice(0, openStart) + newClipRange + seqXml.slice(clipEnd);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function escapeRegex(s) {
|
|
111
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Inject DRX grade payloads into the matching clips of an existing DRP.
|
|
116
|
+
*
|
|
117
|
+
* @param {string} drpPath - Absolute path to the source .drp
|
|
118
|
+
* @param {Array<{clipId?: string, resolveId?: string, drxContent: string}>} grades
|
|
119
|
+
* Each entry targets one clip by DbId (clipId is the canonical name;
|
|
120
|
+
* resolveId is accepted as an alias for callers that use Resolve's
|
|
121
|
+
* field naming). drxContent is the full DRX XML; we extract its <Body>
|
|
122
|
+
* blob and inject it into the clip's <Body>.
|
|
123
|
+
* @param {object} [opts]
|
|
124
|
+
* @param {string} [opts.outputPath] - Where to write the modified DRP.
|
|
125
|
+
* If omitted, the source file is overwritten in place atomically.
|
|
126
|
+
* @returns {Promise<{bytes: number, clipsInjected: number, misses: string[]}>}
|
|
127
|
+
* bytes: size of the written DRP.
|
|
128
|
+
* clipsInjected: number of grade entries that found their target.
|
|
129
|
+
* misses: clip IDs that didn't match any SeqContainer's clip.
|
|
130
|
+
*/
|
|
131
|
+
async function injectGrades(drpPath, grades, opts = {}) {
|
|
132
|
+
if (typeof drpPath !== 'string') {
|
|
133
|
+
throw new TypeError('injectGrades: drpPath must be a string');
|
|
134
|
+
}
|
|
135
|
+
if (!Array.isArray(grades) || grades.length === 0) {
|
|
136
|
+
throw new TypeError('injectGrades: grades must be a non-empty array');
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const sourceBuf = await fs.readFile(drpPath);
|
|
140
|
+
const zip = await JSZip.loadAsync(sourceBuf);
|
|
141
|
+
|
|
142
|
+
const seqPaths = listSeqContainerEntries(zip);
|
|
143
|
+
if (seqPaths.length === 0) {
|
|
144
|
+
throw new Error(`No SeqContainer*.xml found in ${drpPath}`);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Cache each SeqContainer's XML in memory; rewrite only the ones whose
|
|
148
|
+
// content changes. Cheaper than per-clip re-read and keeps the original
|
|
149
|
+
// file order stable in the output zip.
|
|
150
|
+
const seqXmls = new Map();
|
|
151
|
+
for (const p of seqPaths) {
|
|
152
|
+
seqXmls.set(p, await zip.file(p).async('string'));
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
let clipsInjected = 0;
|
|
156
|
+
const misses = [];
|
|
157
|
+
|
|
158
|
+
for (const entry of grades) {
|
|
159
|
+
const targetId = entry.clipId || entry.resolveId;
|
|
160
|
+
if (!targetId) {
|
|
161
|
+
throw new Error('injectGrades: every grade entry needs clipId or resolveId');
|
|
162
|
+
}
|
|
163
|
+
if (typeof entry.drxContent !== 'string' || entry.drxContent.length === 0) {
|
|
164
|
+
throw new Error(`injectGrades: grade for clip ${targetId} has no drxContent`);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const bodyHex = extractDrxBodyHex(entry.drxContent);
|
|
168
|
+
|
|
169
|
+
let injected = false;
|
|
170
|
+
for (const p of seqPaths) {
|
|
171
|
+
const before = seqXmls.get(p);
|
|
172
|
+
const after = replaceBodyForClip(before, targetId, bodyHex);
|
|
173
|
+
if (after !== null) {
|
|
174
|
+
seqXmls.set(p, after);
|
|
175
|
+
injected = true;
|
|
176
|
+
break; // a clip appears in exactly one SeqContainer
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (injected) {
|
|
181
|
+
clipsInjected += 1;
|
|
182
|
+
} else {
|
|
183
|
+
misses.push(targetId);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Write modified SeqContainer XMLs back into the zip in place.
|
|
188
|
+
for (const p of seqPaths) {
|
|
189
|
+
zip.file(p, seqXmls.get(p));
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const outBuf = await zip.generateAsync({
|
|
193
|
+
type: 'nodebuffer',
|
|
194
|
+
compression: 'DEFLATE',
|
|
195
|
+
compressionOptions: { level: 6 },
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
const outputPath = opts.outputPath || drpPath;
|
|
199
|
+
if (opts.outputPath && opts.outputPath !== drpPath) {
|
|
200
|
+
await fs.writeFile(outputPath, outBuf);
|
|
201
|
+
} else {
|
|
202
|
+
// Atomic in-place overwrite.
|
|
203
|
+
const tmp = `${drpPath}.injecting`;
|
|
204
|
+
await fs.writeFile(tmp, outBuf);
|
|
205
|
+
await fs.rename(tmp, drpPath);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return { bytes: outBuf.length, clipsInjected, misses };
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
module.exports = {
|
|
212
|
+
injectGrades,
|
|
213
|
+
// Exposed for unit tests; not part of the public API.
|
|
214
|
+
_internals: {
|
|
215
|
+
listSeqContainerEntries,
|
|
216
|
+
extractDrxBodyHex,
|
|
217
|
+
replaceBodyForClip,
|
|
218
|
+
},
|
|
219
|
+
};
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* keyed-dict — read AND write the Media Pool metadata blob format shared by
|
|
3
|
+
* Geometry / Time / VideoMetadata / Proxy (the BtVideoInfo sub-blobs).
|
|
4
|
+
*
|
|
5
|
+
* Layout (all big-endian):
|
|
6
|
+
* [u32 hdr=1][u32 count]
|
|
7
|
+
* then `count` entries: [u32 keyLen][UTF-16BE key][value]
|
|
8
|
+
* value = [u32 valType][u8 subType][payload]
|
|
9
|
+
* valType 0x0a (STRING) : [u32 byteLen][UTF-16BE chars]
|
|
10
|
+
* valType 0x0c (BYTES) : [u32 byteLen][raw bytes] (e.g. Resolution, FrameRate;
|
|
11
|
+
* also a NESTED keyed-dict — e.g. TracksBA → BtAudioTrack)
|
|
12
|
+
* valType 0x02 (INT32) : [i32 value] (NumChannels, IdxTrack)
|
|
13
|
+
* valType 0x03 (UINT32) : [u32 value] (SampleRate, BitDepth)
|
|
14
|
+
* valType 0x04/0x06 (I64): [i64 value, BigInt] (Duration, StartTime)
|
|
15
|
+
*
|
|
16
|
+
* Audio config (BtAudioInfo) uses the SAME format: TracksBA = { "<idx>": BYTES(inner
|
|
17
|
+
* BtAudioTrack dict) }; the inner dict carries SampleRate/NumChannels/CodecName/
|
|
18
|
+
* ChannelLayout/BitDepth. VirtualAudioTrackBA = { ChannelsBA: BYTES(dict), AudioType }.
|
|
19
|
+
*
|
|
20
|
+
* The first key is always `UniqueId` (STRING UUID); the last is `DbType` (STRING)
|
|
21
|
+
* naming the concrete type (BtGeometry / BtVideoTime / BtVideoMetadata / BtVideoProxy).
|
|
22
|
+
*
|
|
23
|
+
* Verified by exact round-trip reconstruction against real Resolve 21 exports — encode()
|
|
24
|
+
* of decode() reproduces the original blob byte-for-byte. See knowledge/blob-map.md.
|
|
25
|
+
*
|
|
26
|
+
* @module drp-format/keyed-dict
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
const T_BOOL = 0x01; // 1-byte boolean (UI view-options: ShowSubtitle, …)
|
|
30
|
+
const T_INT32 = 0x02; // signed 32-bit
|
|
31
|
+
const T_UINT32 = 0x03; // unsigned 32-bit (sample rate, bit depth)
|
|
32
|
+
const T_INT64A = 0x04; // 64-bit int (duration, ProjectDbMigrationState)
|
|
33
|
+
const T_INT64C = 0x05; // 64-bit int (ProjectFeatures)
|
|
34
|
+
const T_DOUBLE = 0x06; // float64 (timemap XMax/LastValidYOffset; audio StartTime)
|
|
35
|
+
const T_STRING = 0x0a;
|
|
36
|
+
const T_BYTES = 0x0c; // length-delimited; len 0xffffffff means null/empty
|
|
37
|
+
const T_DATE = 0x10; // fixed 9-byte timestamp (CreateTime/ModTime); raw hex, round-tripped
|
|
38
|
+
|
|
39
|
+
function _utf16beDecode(buf) {
|
|
40
|
+
const sw = Buffer.alloc(buf.length);
|
|
41
|
+
for (let j = 0; j + 1 < buf.length; j += 2) { sw[j] = buf[j + 1]; sw[j + 1] = buf[j]; }
|
|
42
|
+
return sw.toString('utf16le');
|
|
43
|
+
}
|
|
44
|
+
function _utf16beEncode(str) {
|
|
45
|
+
const le = Buffer.from(str, 'utf16le');
|
|
46
|
+
const be = Buffer.alloc(le.length);
|
|
47
|
+
for (let j = 0; j + 1 < le.length; j += 2) { be[j] = le[j + 1]; be[j + 1] = le[j]; }
|
|
48
|
+
return be;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Parse a keyed-dict blob (hex string or Buffer) into typed entries.
|
|
53
|
+
* Returns { hdr, count, entries: [{ key, type, subType, value }] } where value is:
|
|
54
|
+
* string for T_STRING, hex string for T_BYTES, number for T_INT32.
|
|
55
|
+
* Throws if the bytes don't parse cleanly to the end (guards against wrong format).
|
|
56
|
+
*/
|
|
57
|
+
function decodeKeyedDict(input) {
|
|
58
|
+
const b = Buffer.isBuffer(input) ? input : Buffer.from(input, 'hex');
|
|
59
|
+
if (b.length < 8) throw new Error('keyed-dict too short');
|
|
60
|
+
const hdr = b.readUInt32BE(0);
|
|
61
|
+
const count = b.readUInt32BE(4);
|
|
62
|
+
let o = 8;
|
|
63
|
+
const entries = [];
|
|
64
|
+
while (o < b.length - 4) {
|
|
65
|
+
const kl = b.readUInt32BE(o);
|
|
66
|
+
if (!(kl >= 2 && kl <= 200 && kl % 2 === 0 && o + 4 + kl <= b.length)) break;
|
|
67
|
+
const key = _utf16beDecode(b.slice(o + 4, o + 4 + kl));
|
|
68
|
+
o += 4 + kl;
|
|
69
|
+
const valType = b.readUInt32BE(o); o += 4;
|
|
70
|
+
const subType = b[o]; o += 1;
|
|
71
|
+
let value;
|
|
72
|
+
if (valType === T_STRING) {
|
|
73
|
+
const ln = b.readUInt32BE(o); o += 4;
|
|
74
|
+
value = _utf16beDecode(b.slice(o, o + ln)); o += ln;
|
|
75
|
+
} else if (valType === T_BYTES) {
|
|
76
|
+
const ln = b.readUInt32BE(o); o += 4;
|
|
77
|
+
if (ln === 0xffffffff) { value = null; } // null/empty marker — no payload
|
|
78
|
+
else { value = b.slice(o, o + ln).toString('hex'); o += ln; }
|
|
79
|
+
} else if (valType === T_BOOL) {
|
|
80
|
+
value = b[o] !== 0; o += 1;
|
|
81
|
+
} else if (valType === T_INT32) {
|
|
82
|
+
value = b.readInt32BE(o); o += 4;
|
|
83
|
+
} else if (valType === T_UINT32) {
|
|
84
|
+
value = b.readUInt32BE(o); o += 4;
|
|
85
|
+
} else if (valType === T_INT64A || valType === T_INT64C) {
|
|
86
|
+
value = b.readBigInt64BE(o); o += 8;
|
|
87
|
+
} else if (valType === T_DOUBLE) {
|
|
88
|
+
value = b.readDoubleBE(o); o += 8;
|
|
89
|
+
} else if (valType === T_DATE) {
|
|
90
|
+
value = b.slice(o, o + 9).toString('hex'); o += 9;
|
|
91
|
+
} else {
|
|
92
|
+
throw new Error(`keyed-dict unknown valType 0x${valType.toString(16)} at ${o - 5} (key=${key})`);
|
|
93
|
+
}
|
|
94
|
+
entries.push({ key, type: valType, subType, value });
|
|
95
|
+
}
|
|
96
|
+
return { hdr, count, entries };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** Re-encode a parsed keyed-dict ({ hdr, count, entries }) to a Buffer (round-trips exactly). */
|
|
100
|
+
function encodeKeyedDict({ hdr = 1, entries }) {
|
|
101
|
+
const parts = [];
|
|
102
|
+
const head = Buffer.alloc(8);
|
|
103
|
+
head.writeUInt32BE(hdr, 0);
|
|
104
|
+
head.writeUInt32BE(entries.length, 4);
|
|
105
|
+
parts.push(head);
|
|
106
|
+
for (const e of entries) {
|
|
107
|
+
const keyBuf = _utf16beEncode(e.key);
|
|
108
|
+
const kh = Buffer.alloc(4); kh.writeUInt32BE(keyBuf.length, 0);
|
|
109
|
+
parts.push(kh, keyBuf);
|
|
110
|
+
const vh = Buffer.alloc(5);
|
|
111
|
+
vh.writeUInt32BE(e.type, 0); vh.writeUInt8(e.subType || 0, 4);
|
|
112
|
+
parts.push(vh);
|
|
113
|
+
if (e.type === T_STRING) {
|
|
114
|
+
const sb = _utf16beEncode(e.value);
|
|
115
|
+
const lh = Buffer.alloc(4); lh.writeUInt32BE(sb.length, 0);
|
|
116
|
+
parts.push(lh, sb);
|
|
117
|
+
} else if (e.type === T_BYTES) {
|
|
118
|
+
if (e.value === null) {
|
|
119
|
+
const lh = Buffer.alloc(4); lh.writeUInt32BE(0xffffffff, 0);
|
|
120
|
+
parts.push(lh);
|
|
121
|
+
} else {
|
|
122
|
+
const raw = Buffer.from(e.value, 'hex');
|
|
123
|
+
const lh = Buffer.alloc(4); lh.writeUInt32BE(raw.length, 0);
|
|
124
|
+
parts.push(lh, raw);
|
|
125
|
+
}
|
|
126
|
+
} else if (e.type === T_BOOL) {
|
|
127
|
+
parts.push(Buffer.from([e.value ? 1 : 0]));
|
|
128
|
+
} else if (e.type === T_INT32) {
|
|
129
|
+
const ib = Buffer.alloc(4); ib.writeInt32BE(e.value | 0, 0);
|
|
130
|
+
parts.push(ib);
|
|
131
|
+
} else if (e.type === T_UINT32) {
|
|
132
|
+
const ib = Buffer.alloc(4); ib.writeUInt32BE(e.value >>> 0, 0);
|
|
133
|
+
parts.push(ib);
|
|
134
|
+
} else if (e.type === T_INT64A || e.type === T_INT64C) {
|
|
135
|
+
const ib = Buffer.alloc(8); ib.writeBigInt64BE(BigInt(e.value), 0);
|
|
136
|
+
parts.push(ib);
|
|
137
|
+
} else if (e.type === T_DOUBLE) {
|
|
138
|
+
const db = Buffer.alloc(8); db.writeDoubleBE(e.value, 0);
|
|
139
|
+
parts.push(db);
|
|
140
|
+
} else if (e.type === T_DATE) {
|
|
141
|
+
parts.push(Buffer.from(e.value, 'hex'));
|
|
142
|
+
} else {
|
|
143
|
+
throw new Error(`keyed-dict cannot encode valType 0x${e.type.toString(16)}`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return Buffer.concat(parts);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** Tolerant reader: [{ key, value }] (value = decoded string/number/hex). Back-compat. */
|
|
150
|
+
function readKeyedDict(input) {
|
|
151
|
+
return decodeKeyedDict(input).entries.map((e) => ({ key: e.key, value: e.value }));
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/** The concrete DbType name (BtGeometry / BtVideoTime / …) declared in the blob. */
|
|
155
|
+
function keyedDictType(input) {
|
|
156
|
+
const e = decodeKeyedDict(input).entries.find((x) => x.key === 'DbType');
|
|
157
|
+
return e ? e.value : null;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** Get a single entry's value by key (typed). */
|
|
161
|
+
function getKeyedValue(input, key) {
|
|
162
|
+
const e = decodeKeyedDict(input).entries.find((x) => x.key === key);
|
|
163
|
+
return e ? e.value : undefined;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** Set a single entry's value by key, returning a re-encoded hex string. Preserves order/type. */
|
|
167
|
+
function setKeyedValue(input, key, value) {
|
|
168
|
+
const parsed = decodeKeyedDict(input);
|
|
169
|
+
const e = parsed.entries.find((x) => x.key === key);
|
|
170
|
+
if (!e) throw new Error(`keyed-dict has no key ${key}`);
|
|
171
|
+
e.value = value;
|
|
172
|
+
return encodeKeyedDict(parsed).toString('hex');
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Read audio config from a TracksBA blob: returns an array of per-track objects
|
|
177
|
+
* { idx, sampleRate, numChannels, codecName, channelLayout, bitDepth, … } by
|
|
178
|
+
* recursively decoding each track's nested BYTES dict.
|
|
179
|
+
*/
|
|
180
|
+
function readAudioTracks(input) {
|
|
181
|
+
const outer = decodeKeyedDict(input);
|
|
182
|
+
const tracks = [];
|
|
183
|
+
for (const e of outer.entries) {
|
|
184
|
+
if (e.type !== T_BYTES) continue; // skip non-track scalar entries
|
|
185
|
+
let inner;
|
|
186
|
+
try { inner = decodeKeyedDict(Buffer.from(e.value, 'hex')); } catch { continue; }
|
|
187
|
+
const m = Object.fromEntries(inner.entries.map((x) => [x.key, x.value]));
|
|
188
|
+
if (m.DbType !== 'BtAudioTrack') continue;
|
|
189
|
+
tracks.push({
|
|
190
|
+
idx: e.key,
|
|
191
|
+
sampleRate: m.SampleRate,
|
|
192
|
+
numChannels: m.NumChannels,
|
|
193
|
+
codecName: m.CodecName,
|
|
194
|
+
channelLayout: m.ChannelLayout,
|
|
195
|
+
bitDepth: m.BitDepth,
|
|
196
|
+
startTime: m.StartTime,
|
|
197
|
+
duration: m.Duration,
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
return tracks;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Classify any Resolve metadata blob by its leading bytes — useful for the
|
|
205
|
+
* heterogeneous FieldsBlob (which is a keyed-dict for small element state but a
|
|
206
|
+
* zstd-compressed protobuf for large internal/UI/cache state).
|
|
207
|
+
* 'keyed-dict' : [u32 hdr=1][u32 count] … (decode with decodeKeyedDict)
|
|
208
|
+
* 'protobuf' : [u32 hdr=2][u32 len][protobuf] (decode with decodeEffectFilters)
|
|
209
|
+
* 'zstd' : protobuf envelope wrapping a zstd frame (magic 28 b5 2f fd) —
|
|
210
|
+
* internal state, carried verbatim (Node lacks built-in zstd)
|
|
211
|
+
* 'unknown' : none of the above
|
|
212
|
+
*/
|
|
213
|
+
function classifyBlob(input) {
|
|
214
|
+
const b = Buffer.isBuffer(input) ? input : Buffer.from(input, 'hex');
|
|
215
|
+
if (b.length >= 8 && b.indexOf(Buffer.from('28b52ffd', 'hex')) >= 0) return 'zstd';
|
|
216
|
+
const hdr = b.length >= 4 ? b.readUInt32BE(0) : -1;
|
|
217
|
+
if (hdr === 1) return 'keyed-dict';
|
|
218
|
+
if (hdr === 2) return 'protobuf';
|
|
219
|
+
return 'unknown';
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
module.exports = {
|
|
223
|
+
decodeKeyedDict, encodeKeyedDict,
|
|
224
|
+
readKeyedDict, keyedDictType, getKeyedValue, setKeyedValue, readAudioTracks,
|
|
225
|
+
classifyBlob,
|
|
226
|
+
T_BOOL, T_INT32, T_UINT32, T_INT64A, T_INT64C, T_DOUBLE, T_STRING, T_BYTES, T_DATE,
|
|
227
|
+
};
|