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,116 @@
|
|
|
1
|
+
# drp-format — offline DaVinci Resolve project authoring & editing
|
|
2
|
+
|
|
3
|
+
Read, author, and edit **real, importable** DaVinci Resolve 21 projects (`.drp`) and timelines
|
|
4
|
+
(`.drt`) as files — no Resolve required. Everything here is verified by a live round-trip
|
|
5
|
+
(author/edit → `import_project` → `lint`/`clip_where` → re-export decode), not just self-parse.
|
|
6
|
+
|
|
7
|
+
Background + the full schema map: `docs/design/drp-drx-drt-closeout-harness/knowledge/resolve21-schema-reconciliation.md`
|
|
8
|
+
and `.../resolve-authoring-completion.md`.
|
|
9
|
+
|
|
10
|
+
> **Key fact:** a `.drp`/`.drt` is a zip of `SeqContainer/<uuid>.xml` (+ `MpFolder.xml`, `project.xml`,
|
|
11
|
+
> `Gallery.xml`). A clip's track is just which `<Sm2TiTrack>` it sits under. Source in-points live in
|
|
12
|
+
> `<In>` (timeline frames). Resolve links media by the path **inside the Media Pool blob**, not the
|
|
13
|
+
> timeline's `<MediaFilePath>`, and does **not** reconform on import — so cached specs must match.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## MCP actions
|
|
18
|
+
|
|
19
|
+
### `drt` tool — timeline format
|
|
20
|
+
|
|
21
|
+
| Action | Purpose |
|
|
22
|
+
|---|---|
|
|
23
|
+
| `parse` | `.drt`/`.drp` → `{ timelines, metadata }` (reads real Resolve exports) |
|
|
24
|
+
| `author` | spec → `.drt` |
|
|
25
|
+
| `validate` | structural check |
|
|
26
|
+
| `inject_into_drp` / `extract_from_drp` | graft / pull a SeqContainer |
|
|
27
|
+
|
|
28
|
+
### `drp` tool — project authoring + editing (all offline/local unless noted)
|
|
29
|
+
|
|
30
|
+
**Author from scratch**
|
|
31
|
+
| Action | What it does |
|
|
32
|
+
|---|---|
|
|
33
|
+
| `create_empty_project` | fresh `.drp`, one empty timeline → `{ buffer, startFrame: 86400 }` |
|
|
34
|
+
| `assemble_timeline` | declarative spec `{ timelineName, elements:[{type:'title'\|'generator', track, startFrame, …}], transitions }` → importable project |
|
|
35
|
+
| `add_media_clip` | one media clip from an arbitrary **h264** file: `{ mediaFile, spec:{width,height,frameCount,fps}, timelineName?, durationFrames? }` |
|
|
36
|
+
|
|
37
|
+
**Place elements (track-targeted — the #74 bypass, offline)**
|
|
38
|
+
| Action | What it does |
|
|
39
|
+
|---|---|
|
|
40
|
+
| `place_fusion_title` | Text+ on a chosen track. Options: `text, font, style, size, vJustify, hJustify, color:{r,g,b}`, `trackIndex`, `startFrame`, `durationFrames` |
|
|
41
|
+
| `place_generator` | built-in generator (`generatorName`, e.g. "Solid Color") on a chosen track |
|
|
42
|
+
| `place_transition` | cross-dissolve at an abutting cut (`track`, `atFrame`, `durationFrames`) — clips need handle media |
|
|
43
|
+
|
|
44
|
+
**Edit in place (video or audio via `trackType`)**
|
|
45
|
+
| Action | What it does |
|
|
46
|
+
|---|---|
|
|
47
|
+
| `move_clip` | relocate a clip to another track / new `toStart` |
|
|
48
|
+
| `delete_clip` | remove a clip; `ripple` closes the gap on that track |
|
|
49
|
+
| `trim_clip` | tail-trim `newDuration`; `ripple` shifts later clips |
|
|
50
|
+
| `trim_clip_head` | head trim — advances the source `<In>`; `ripple` keeps Start |
|
|
51
|
+
| `split_clip` | razor at `atFrame` → two source-continuous clips |
|
|
52
|
+
| `ripple_timeline` | cross-track ripple — shift all clips ≥ `at` by `delta` (video+audio, keeps sync) |
|
|
53
|
+
|
|
54
|
+
**Media (conform / relink)**
|
|
55
|
+
| Action | What it does |
|
|
56
|
+
|---|---|
|
|
57
|
+
| `relink_media` | repoint media paths in the Media Pool blobs + plain text (`mappings:[{from,to}]`) |
|
|
58
|
+
| `repoint_media` | relink **+ fix cached specs** (res/frames/fps) for a differently-formatted file (`{from,to,fromSpec,toSpec}`) |
|
|
59
|
+
|
|
60
|
+
**Grades + analysis (server/local)**
|
|
61
|
+
| Action | What it does |
|
|
62
|
+
|---|---|
|
|
63
|
+
| `inject_grades` | apply DRX grades into a `.drp` |
|
|
64
|
+
| `extract_node_graphs` | pull per-clip grade `<Body>` blobs as DRX envelopes |
|
|
65
|
+
| `diff` | structural diff of two `.drp`s |
|
|
66
|
+
| `validate` / `validate_async` / `status` | DRP validation (server) |
|
|
67
|
+
|
|
68
|
+
> Clip selectors on the edit ops: `clipIndex` (0-based), `clipDbId`, or `nameContains`.
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Library functions (`require('drp-format')`)
|
|
73
|
+
|
|
74
|
+
Same surface as the MCP actions, returning `{ buffer, … }`:
|
|
75
|
+
`createEmptyProject`, `assembleTimeline`, `addMediaClip`, `placeFusionTitle`, `placeGenerator`,
|
|
76
|
+
`placeTransition`, `moveClip`, `deleteClip`, `trimClip`, `trimClipHead`, `splitClip`, `rippleTimeline`,
|
|
77
|
+
`relinkMedia`, `repointMedia`, `injectGrades`, `diff`.
|
|
78
|
+
|
|
79
|
+
Title text/style codec: `decodeTitleInputs`, `setTitleInputs`, `decodeTitleText`, `setTitleText`.
|
|
80
|
+
Shared surgery primitives: `seq-surgery.js`. Title-comp codec: `composition-text.js`.
|
|
81
|
+
Plus the full **DRX grade** surface (`createSimpleGrade`, curves/qualifiers/windows/node-tree encoders).
|
|
82
|
+
|
|
83
|
+
`drt-format` re-exports the timeline-only surface: `parseDRT`, `buildDRT`, `validateDRT`.
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Live-edit recipe (the one thing that needs Resolve running)
|
|
88
|
+
|
|
89
|
+
**#74 — insert a Text+/generator on a chosen track of the OPEN timeline.** The API's
|
|
90
|
+
`InsertFusionTitleIntoTimeline` takes no track arg (always the lowest unlocked video track). Bypass:
|
|
91
|
+
via computer-use, **lock the video tracks below your target**, then call `timeline.insert_fusion_title` —
|
|
92
|
+
it lands on the chosen track. Verified live. (For export-based workflows, `place_fusion_title` does this
|
|
93
|
+
offline.)
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## What's mapped (honest scope)
|
|
98
|
+
|
|
99
|
+
**Fully mapped (read + write, round-trips byte-for-byte):** project packaging; tracks
|
|
100
|
+
(`Sm2TiTrack`+Type+Sequence); clips; source in-points (`<In>`); Fusion titles
|
|
101
|
+
(text/font/style/size/justify/color); generators; transitions; markers; grades (full DRX body);
|
|
102
|
+
media path + resolution + frame-count + fps. **Plus the full Media-Pool metadata layer:** the
|
|
103
|
+
keyed-dict format (`Geometry`/`Time`/`VideoMetadata`/`Proxy`/audio/small `FieldsBlob`) with typed
|
|
104
|
+
values (`keyed-dict.js`); audio config (`TracksBA`/`VirtualAudioTrackBA` → sample-rate/channels/codec
|
|
105
|
+
via `readAudioTracks`); the protobuf blobs `Radiometry` + transition/generator `EffectFiltersBA`
|
|
106
|
+
(wire-level, `protobuf-wire.js`); and `MediaTimemapBA` retime — both the 1× compact form and the
|
|
107
|
+
retimed `Sm2TimeMap` keyed-dict, including **dynamic/variable-speed ramps** (KeyframesBA = repeated
|
|
108
|
+
`(record,source)` points; per-segment speed = Δsource/Δrecord), decoded *and* authorable
|
|
109
|
+
(`media-timemap.js`, `buildConstantSpeedTimemap` / `buildTimemap`).
|
|
110
|
+
|
|
111
|
+
**Carried verbatim (cloned, not byte-decoded — Resolve accepts, never authored):** zstd-compressed
|
|
112
|
+
internal-state/cache `FieldsBlob`s (`classifyBlob` flags them; Node has no built-in zstd); the
|
|
113
|
+
protobuf blobs' *field names* (wire structure is decoded; names need Resolve's private `.proto`); the
|
|
114
|
+
Fusion comp beyond the title inputs we edit.
|
|
115
|
+
|
|
116
|
+
**Limit:** media authoring is h264-only (cross-codec needs a per-codec template).
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RESOLVE_VERIFY helper.
|
|
3
|
+
*
|
|
4
|
+
* Test files that include Resolve-in-loop assertions (apply DRX via
|
|
5
|
+
* davinci-resolve-mcp, frame-compare via export_frame_as_still, etc.)
|
|
6
|
+
* gate themselves with this helper so CI environments without Resolve
|
|
7
|
+
* don't fail spuriously, and local sessions can opt-in by setting
|
|
8
|
+
* RESOLVE_VERIFY=1.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* const test = require('node:test');
|
|
12
|
+
* const { resolveVerifyTest } = require('./_resolve-verify');
|
|
13
|
+
* resolveVerifyTest('apply DRX matches direct render', async () => {
|
|
14
|
+
* // ... uses davinci-resolve-mcp via stdio ...
|
|
15
|
+
* });
|
|
16
|
+
*
|
|
17
|
+
* When RESOLVE_VERIFY is set to '1' or 'true', the test runs
|
|
18
|
+
* normally. Otherwise it's marked skip with a clear reason.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
const test = require('node:test');
|
|
22
|
+
|
|
23
|
+
function isResolveVerifyEnabled() {
|
|
24
|
+
const v = process.env.RESOLVE_VERIFY;
|
|
25
|
+
return v === '1' || v === 'true' || v === 'yes';
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function resolveVerifyTest(name, fn) {
|
|
29
|
+
if (!isResolveVerifyEnabled()) {
|
|
30
|
+
test.skip(`${name} (skipped — set RESOLVE_VERIFY=1 to enable)`, () => {});
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
test(name, fn);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
module.exports = {
|
|
37
|
+
isResolveVerifyEnabled,
|
|
38
|
+
resolveVerifyTest,
|
|
39
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const test = require('node:test');
|
|
2
|
+
const assert = require('node:assert');
|
|
3
|
+
const fs = require('node:fs');
|
|
4
|
+
const { addMediaClip } = require('../author-project');
|
|
5
|
+
const { parseDRT } = require('../../drt-format');
|
|
6
|
+
|
|
7
|
+
test('addMediaClip authors a project with the target media + patched specs', async () => {
|
|
8
|
+
const { buffer, timelineName, mediaFile } = await addMediaClip({
|
|
9
|
+
mediaFile: '/media/Sample Montage.mp4',
|
|
10
|
+
spec: { width: 1280, height: 960, frameCount: 3094, fps: 30000 / 1001 },
|
|
11
|
+
timelineName: 'Authored Cut', durationFrames: 240,
|
|
12
|
+
});
|
|
13
|
+
assert.strictEqual(timelineName, 'Authored Cut');
|
|
14
|
+
const parsed = await parseDRT(buffer);
|
|
15
|
+
const clip = parsed.timelines[0].videoTracks[0].clips[0];
|
|
16
|
+
assert.ok(clip.mediaFilePath.includes('Sample Montage.mp4'), 'clip points at target file');
|
|
17
|
+
assert.strictEqual(clip.duration, 240, 'clip trimmed to durationFrames');
|
|
18
|
+
const JSZip = require('jszip');
|
|
19
|
+
const zip = await JSZip.loadAsync(buffer);
|
|
20
|
+
const mp = await zip.file('MediaPool/Master/MpFolder.xml').async('string');
|
|
21
|
+
assert.ok(mp.includes('<Name>Authored Cut</Name>'), 'timeline renamed');
|
|
22
|
+
assert.ok(mp.includes('<Name>Sample Montage.mp4</Name>'), 'media entry renamed');
|
|
23
|
+
const geo = Buffer.from(mp.match(/<Geometry>([0-9a-fA-F]+)<\/Geometry>/)[1], 'hex');
|
|
24
|
+
assert.ok(geo.includes(Buffer.from([0, 0, 5, 0])), 'width 1280 patched'); // 1280 = 0x500
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test('addMediaClip validates inputs', async () => {
|
|
28
|
+
await assert.rejects(() => addMediaClip({ mediaFile: 'rel.mov', spec: { width: 1, height: 1, frameCount: 1, fps: 24 } }), /absolute/);
|
|
29
|
+
await assert.rejects(() => addMediaClip({ mediaFile: '/a/b.mov', spec: { width: 1 } }), /spec must/);
|
|
30
|
+
});
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// Unit: assemble a full timeline from a declarative spec (composes the verified primitives).
|
|
2
|
+
// Live Resolve round-trip is the acceptance gate.
|
|
3
|
+
|
|
4
|
+
const test = require('node:test');
|
|
5
|
+
const assert = require('node:assert');
|
|
6
|
+
const { assembleTimeline } = require('../assemble-timeline');
|
|
7
|
+
const { decodeTitleInputs } = require('../composition-text');
|
|
8
|
+
const { parseDRT } = require('../../drt-format');
|
|
9
|
+
|
|
10
|
+
async function compositionTexts(buffer) {
|
|
11
|
+
const JSZip = require('jszip');
|
|
12
|
+
const zip = await JSZip.loadAsync(buffer);
|
|
13
|
+
const out = [];
|
|
14
|
+
for (const n of Object.keys(zip.files)) {
|
|
15
|
+
if (!/SeqContainer\/[^/]+\.xml$|SeqContainer\d*\.xml$/.test(n)) continue;
|
|
16
|
+
const x = await zip.files[n].async('string');
|
|
17
|
+
for (const tag of x.match(/<CompositionBA>[0-9a-fA-F]+<\/CompositionBA>/g) || []) {
|
|
18
|
+
out.push(decodeTitleInputs(tag.replace(/<\/?CompositionBA>/g, '')));
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return out;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
test('assembleTimeline composes a title (V2) + generator (V3) from a spec', async () => {
|
|
25
|
+
const start = 86400;
|
|
26
|
+
const { buffer, timelineName, startFrame } = await assembleTimeline({
|
|
27
|
+
timelineName: 'Assembled',
|
|
28
|
+
elements: [
|
|
29
|
+
{ type: 'generator', generatorName: 'Solid Color', track: 3, startFrame: start, durationFrames: 120 },
|
|
30
|
+
{ type: 'title', track: 2, startFrame: start, durationFrames: 120, text: 'HELLO', color: { r: 0.2, g: 0.8, b: 1 } },
|
|
31
|
+
],
|
|
32
|
+
});
|
|
33
|
+
assert.strictEqual(timelineName, 'Assembled');
|
|
34
|
+
assert.strictEqual(startFrame, 86400);
|
|
35
|
+
|
|
36
|
+
const parsed = await parseDRT(buffer);
|
|
37
|
+
const tl = parsed.timelines[0];
|
|
38
|
+
assert.ok(tl.videoTracks.length >= 3, 'V1/V2/V3 present');
|
|
39
|
+
|
|
40
|
+
const titles = await compositionTexts(buffer);
|
|
41
|
+
const t = titles.find((x) => x.text === 'HELLO');
|
|
42
|
+
assert.ok(t, 'title text composed');
|
|
43
|
+
assert.deepStrictEqual(t.color, { r: 0.2, g: 0.8, b: 1 }, 'title color composed');
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test('assembleTimeline places two abutting generators + a transition between them', async () => {
|
|
47
|
+
const s = 86400;
|
|
48
|
+
const { buffer } = await assembleTimeline({
|
|
49
|
+
timelineName: 'GenTrans',
|
|
50
|
+
elements: [
|
|
51
|
+
{ type: 'generator', track: 1, startFrame: s, durationFrames: 100 },
|
|
52
|
+
{ type: 'generator', track: 1, startFrame: s + 100, durationFrames: 100 },
|
|
53
|
+
],
|
|
54
|
+
transitions: [{ track: 1, atFrame: s + 100, durationFrames: 24 }],
|
|
55
|
+
});
|
|
56
|
+
const JSZip = require('jszip');
|
|
57
|
+
const zip = await JSZip.loadAsync(buffer);
|
|
58
|
+
let body = '';
|
|
59
|
+
for (const n of Object.keys(zip.files)) {
|
|
60
|
+
if (!/SeqContainer\/[^/]+\.xml$/.test(n)) continue;
|
|
61
|
+
body = await zip.files[n].async('string');
|
|
62
|
+
}
|
|
63
|
+
assert.ok(/<Sm2TiTransition\b/.test(body), 'transition composed between the generators');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test('assembleTimeline rejects unknown element types', async () => {
|
|
67
|
+
await assert.rejects(() => assembleTimeline({ elements: [{ type: 'bogus', track: 1, startFrame: 86400 }] }), /unknown type/);
|
|
68
|
+
});
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// Unit: create a fresh importable project from scratch, then populate it.
|
|
2
|
+
// Live Resolve round-trip is the acceptance gate (proven manually — see
|
|
3
|
+
// resolve21-schema-reconciliation.md).
|
|
4
|
+
|
|
5
|
+
const test = require('node:test');
|
|
6
|
+
const assert = require('node:assert');
|
|
7
|
+
const { createEmptyProject } = require('../author-project');
|
|
8
|
+
const { placeFusionTitle } = require('../place-fusion-title');
|
|
9
|
+
const { decodeTitleText } = require('../composition-text');
|
|
10
|
+
const { parseDRT } = require('../../drt-format');
|
|
11
|
+
|
|
12
|
+
test('createEmptyProject returns an importable empty timeline', async () => {
|
|
13
|
+
const { buffer, timelineName } = await createEmptyProject();
|
|
14
|
+
assert.strictEqual(timelineName, 'Timeline 1');
|
|
15
|
+
const parsed = await parseDRT(buffer);
|
|
16
|
+
assert.strictEqual(parsed.timelines.length, 1, 'one timeline');
|
|
17
|
+
const tl = parsed.timelines[0];
|
|
18
|
+
assert.ok((tl.videoTracks || []).length >= 1, 'has a video track');
|
|
19
|
+
assert.strictEqual((tl.videoTracks[0].clips || []).length, 0, 'timeline is empty');
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test('createEmptyProject renames the timeline', async () => {
|
|
23
|
+
const { buffer } = await createEmptyProject({ timelineName: 'My Fresh Cut' });
|
|
24
|
+
const JSZip = require('jszip');
|
|
25
|
+
const zip = await JSZip.loadAsync(buffer);
|
|
26
|
+
const mp = await zip.file('MediaPool/Master/MpFolder.xml').async('string');
|
|
27
|
+
assert.ok(mp.includes('<Name>My Fresh Cut</Name>'), 'renamed in media pool');
|
|
28
|
+
assert.ok(!mp.includes('<Name>Timeline 1</Name>'), 'default name gone');
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('createEmptyProject rejects angle brackets in the name', async () => {
|
|
32
|
+
await assert.rejects(() => createEmptyProject({ timelineName: 'bad<name>' }), /< or >/);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test('from scratch: createEmptyProject -> placeFusionTitle on V2 with text', async () => {
|
|
36
|
+
const { buffer, startFrame } = await createEmptyProject({ timelineName: 'Scratch' });
|
|
37
|
+
assert.strictEqual(startFrame, 86400, 'timeline origin exposed for correct clip placement');
|
|
38
|
+
const res = await placeFusionTitle(buffer, { trackIndex: 2, startFrame, durationFrames: 120, text: 'FROM SCRATCH' });
|
|
39
|
+
// The placed title is on V2 with the requested text.
|
|
40
|
+
const JSZip = require('jszip');
|
|
41
|
+
const zip = await JSZip.loadAsync(res.buffer);
|
|
42
|
+
let text = null;
|
|
43
|
+
for (const n of Object.keys(zip.files)) {
|
|
44
|
+
if (!/SeqContainer\/[^/]+\.xml$|SeqContainer\d*\.xml$/.test(n)) continue;
|
|
45
|
+
const xml = await zip.files[n].async('string');
|
|
46
|
+
for (const tag of xml.match(/<CompositionBA>[0-9a-fA-F]+<\/CompositionBA>/g) || []) {
|
|
47
|
+
const t = decodeTitleText(tag.replace(/<\/?CompositionBA>/g, ''));
|
|
48
|
+
if (t === 'FROM SCRATCH') text = t;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
assert.strictEqual(text, 'FROM SCRATCH', 'title text present');
|
|
52
|
+
|
|
53
|
+
const parsed = await parseDRT(res.buffer);
|
|
54
|
+
const tl = parsed.timelines.find((t2) => (t2.videoTracks || []).length >= 2);
|
|
55
|
+
assert.ok(tl, 'V2 exists');
|
|
56
|
+
assert.strictEqual(tl.videoTracks[1].clips.length, 1, 'title on V2');
|
|
57
|
+
});
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// Unit: read/replace a Fusion Title's on-screen text in a CompositionBA blob.
|
|
2
|
+
// Encoding verified by live Resolve 21 import + re-export (the re-export reads back the
|
|
3
|
+
// edited StyledText) — see resolve21-schema-reconciliation.md.
|
|
4
|
+
|
|
5
|
+
const test = require('node:test');
|
|
6
|
+
const assert = require('node:assert');
|
|
7
|
+
const path = require('node:path');
|
|
8
|
+
const fs = require('node:fs');
|
|
9
|
+
const { decodeTitleText, setTitleText } = require('../composition-text');
|
|
10
|
+
const { placeFusionTitle } = require('../place-fusion-title');
|
|
11
|
+
const { parseDRT } = require('../../drt-format');
|
|
12
|
+
|
|
13
|
+
const TEMPLATE = fs.readFileSync(path.join(__dirname, '..', 'templates', 'fusion-title.xml'), 'utf8');
|
|
14
|
+
const TEMPLATE_BA = TEMPLATE.match(/<CompositionBA>([0-9a-fA-F]*)<\/CompositionBA>/)[1];
|
|
15
|
+
const FIXTURE = path.resolve(
|
|
16
|
+
__dirname,
|
|
17
|
+
'../../../docs/design/drp-drx-drt-closeout-harness/fixtures/canary-resolve21.drp',
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
test('decodeTitleText reads the bundled template text', () => {
|
|
21
|
+
assert.strictEqual(decodeTitleText(TEMPLATE_BA), 'Custom Title');
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test('setTitleText round-trips through decodeTitleText', () => {
|
|
25
|
+
const enc = setTitleText(TEMPLATE_BA, 'SAMPLE TITLE');
|
|
26
|
+
assert.strictEqual(decodeTitleText(enc), 'SAMPLE TITLE');
|
|
27
|
+
// idempotent re-edit
|
|
28
|
+
assert.strictEqual(decodeTitleText(setTitleText(enc, 'Second Edit 2')), 'Second Edit 2');
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('setTitleText round-trips text with quotes, backslashes, and newlines (Lua escaping)', () => {
|
|
32
|
+
for (const s of ['He said "hi"', 'back\\slash', 'line1\nline2', 'mix: "a"\\b\tc', 'price $5 & up']) {
|
|
33
|
+
const enc = setTitleText(TEMPLATE_BA, s);
|
|
34
|
+
assert.strictEqual(decodeTitleText(enc), s, `round-trip failed for ${JSON.stringify(s)}`);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test('placeFusionTitle honors the text option (CompositionBA rewritten in place)', async (t) => {
|
|
39
|
+
if (!fs.existsSync(FIXTURE)) { t.skip(`fixture missing: ${FIXTURE}`); return; }
|
|
40
|
+
const res = await placeFusionTitle(FIXTURE, { trackIndex: 2, startFrame: 1000, text: 'HELLO WORLD' });
|
|
41
|
+
// Re-read the placed clip's CompositionBA from the output zip and confirm the text.
|
|
42
|
+
const JSZip = require('jszip');
|
|
43
|
+
const zip = await JSZip.loadAsync(res.buffer);
|
|
44
|
+
let found = null;
|
|
45
|
+
for (const name of Object.keys(zip.files)) {
|
|
46
|
+
if (!/SeqContainer\/[^/]+\.xml$|SeqContainer\d*\.xml$/.test(name)) continue;
|
|
47
|
+
const xml = await zip.files[name].async('string');
|
|
48
|
+
for (const tag of xml.match(/<CompositionBA>[0-9a-fA-F]+<\/CompositionBA>/g) || []) {
|
|
49
|
+
const ba = tag.replace(/<\/?CompositionBA>/g, '');
|
|
50
|
+
const txt = decodeTitleText(ba);
|
|
51
|
+
if (txt === 'HELLO WORLD') found = txt;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
assert.strictEqual(found, 'HELLO WORLD', 'placed title carries the requested on-screen text');
|
|
55
|
+
|
|
56
|
+
// And it still parses as a clip on V2.
|
|
57
|
+
const parsed = await parseDRT(res.buffer);
|
|
58
|
+
const tl = parsed.timelines.find((t2) => (t2.videoTracks || []).length >= 2 && t2.videoTracks[1].clips?.length === 1);
|
|
59
|
+
assert.ok(tl, 'title present on V2');
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const { setTitleInputs, decodeTitleInputs } = require('../composition-text');
|
|
63
|
+
|
|
64
|
+
test('setTitleInputs sets font/style/size/justify and round-trips', () => {
|
|
65
|
+
const enc = setTitleInputs(TEMPLATE_BA, { text: 'STYLED', font: 'Helvetica Neue', style: 'Bold', size: 0.14, hJustify: 0, vJustify: 1 });
|
|
66
|
+
const got = decodeTitleInputs(enc);
|
|
67
|
+
assert.strictEqual(got.text, 'STYLED');
|
|
68
|
+
assert.strictEqual(got.font, 'Helvetica Neue');
|
|
69
|
+
assert.strictEqual(got.style, 'Bold');
|
|
70
|
+
assert.strictEqual(got.size, 0.14);
|
|
71
|
+
assert.strictEqual(got.hJustify, 0);
|
|
72
|
+
assert.strictEqual(got.vJustify, 1);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test('decodeTitleInputs reads template defaults', () => {
|
|
76
|
+
const got = decodeTitleInputs(TEMPLATE_BA);
|
|
77
|
+
assert.strictEqual(got.text, 'Custom Title');
|
|
78
|
+
assert.strictEqual(got.font, 'Open Sans');
|
|
79
|
+
assert.strictEqual(typeof got.size, 'number');
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test('setTitleInputs rejects unknown inputs', () => {
|
|
83
|
+
assert.throws(() => setTitleInputs(TEMPLATE_BA, { bogus: 1 }), /unknown input/);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test('setTitleInputs injects + round-trips text color (Red1/Green1/Blue1)', () => {
|
|
87
|
+
const enc = setTitleInputs(TEMPLATE_BA, { text: 'COLORED', color: { r: 0.9, g: 0.1, b: 0.2 } });
|
|
88
|
+
const got = decodeTitleInputs(enc);
|
|
89
|
+
assert.strictEqual(got.text, 'COLORED');
|
|
90
|
+
assert.deepStrictEqual(got.color, { r: 0.9, g: 0.1, b: 0.2 });
|
|
91
|
+
// re-setting replaces in place (no duplicate injection)
|
|
92
|
+
const enc2 = setTitleInputs(enc, { color: { r: 0.3 } });
|
|
93
|
+
assert.strictEqual(decodeTitleInputs(enc2).color.r, 0.3);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test('setTitleInputs rejects out-of-range color', () => {
|
|
97
|
+
assert.throws(() => setTitleInputs(TEMPLATE_BA, { color: { r: 2 } }), /0\.\.1/);
|
|
98
|
+
});
|