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,91 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Fixture-free checks for the fast (coarse→fine) search paths in compare/metrics:
|
|
5
|
+
* - findOffset's pyramid path returns the SAME peak as an exhaustive brute force,
|
|
6
|
+
* - coarseToFinePeak finds a 1D peak with far fewer evaluations than a linear scan.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const test = require('node:test');
|
|
10
|
+
const assert = require('node:assert/strict');
|
|
11
|
+
const Mx = require('../compare/metrics');
|
|
12
|
+
|
|
13
|
+
const W = 160;
|
|
14
|
+
const H = 120;
|
|
15
|
+
|
|
16
|
+
// A textured image (not separable, so shifts are unambiguous): mixed frequencies.
|
|
17
|
+
function texture(w, h) {
|
|
18
|
+
const d = new Float64Array(w * h);
|
|
19
|
+
for (let y = 0; y < h; y++) {
|
|
20
|
+
for (let x = 0; x < w; x++) {
|
|
21
|
+
d[y * w + x] = 0.5 +
|
|
22
|
+
0.25 * Math.sin(x * 0.21 + y * 0.07) +
|
|
23
|
+
0.15 * Math.cos(x * 0.05 - y * 0.17) +
|
|
24
|
+
0.08 * Math.sin((x + y) * 0.33);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return d;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Shift src by (sx, sy) into a w×h buffer (out-of-bounds -> 0).
|
|
31
|
+
function shifted(src, w, h, sx, sy) {
|
|
32
|
+
const d = new Float64Array(w * h);
|
|
33
|
+
for (let y = 0; y < h; y++) {
|
|
34
|
+
for (let x = 0; x < w; x++) {
|
|
35
|
+
const ox = x - sx;
|
|
36
|
+
const oy = y - sy;
|
|
37
|
+
d[y * w + x] = ox >= 0 && ox < w && oy >= 0 && oy < h ? src[oy * w + ox] : 0;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return d;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
test('findOffset pyramid path equals exhaustive brute force', () => {
|
|
44
|
+
const a = texture(W, H);
|
|
45
|
+
for (const [sx, sy] of [[20, -14], [-31, 25], [0, 0], [44, 7]]) {
|
|
46
|
+
const b = shifted(a, W, H, sx, sy);
|
|
47
|
+
const maxShift = 50; // > 16 -> exercises the pyramid path
|
|
48
|
+
// Reference: exhaustive window search at full res.
|
|
49
|
+
const brute = Mx.searchShiftWindow(a, b, null, W, H, -maxShift, maxShift, -maxShift, maxShift);
|
|
50
|
+
const fast = Mx.findOffset(a, b, null, W, H, maxShift);
|
|
51
|
+
assert.equal(fast.dx, brute.dx, `dx mismatch for shift (${sx},${sy})`);
|
|
52
|
+
assert.equal(fast.dy, brute.dy, `dy mismatch for shift (${sx},${sy})`);
|
|
53
|
+
// structureAtShift re-aligns b by (dx,dy), so b = a shifted by (sx,sy) peaks at (-sx,-sy).
|
|
54
|
+
assert.equal(fast.dx, -sx || 0); // || 0 normalizes the -0 case
|
|
55
|
+
assert.equal(fast.dy, -sy || 0);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test('small maxShift stays exact brute force (unchanged behaviour)', () => {
|
|
60
|
+
const a = texture(W, H);
|
|
61
|
+
const b = shifted(a, W, H, 5, -3);
|
|
62
|
+
const fast = Mx.findOffset(a, b, null, W, H, 12);
|
|
63
|
+
const brute = Mx.searchShiftWindow(a, b, null, W, H, -12, 12, -12, 12);
|
|
64
|
+
assert.equal(fast.dx, brute.dx);
|
|
65
|
+
assert.equal(fast.dy, brute.dy);
|
|
66
|
+
assert.equal(fast.dx, -5);
|
|
67
|
+
assert.equal(fast.dy, 3);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test('coarseToFinePeak finds the peak with fewer evals than a linear scan', async () => {
|
|
71
|
+
const lo = 0;
|
|
72
|
+
const hi = 300;
|
|
73
|
+
const peak = 211;
|
|
74
|
+
let calls = 0;
|
|
75
|
+
const scoreAt = (x) => { calls += 1; return -Math.abs(x - peak); }; // unimodal
|
|
76
|
+
const r = await Mx.coarseToFinePeak(scoreAt, lo, hi, 8);
|
|
77
|
+
assert.equal(r.x, peak);
|
|
78
|
+
assert.ok(r.evals < hi - lo, `expected sub-linear evals, got ${r.evals}`);
|
|
79
|
+
assert.ok(calls <= 70, `expected ~50 evals, got ${calls}`);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test('downsample averages and halves/quarters dimensions', () => {
|
|
83
|
+
const a = texture(W, H);
|
|
84
|
+
const d = Mx.downsample(a, W, H, 4);
|
|
85
|
+
assert.equal(d.width, 40);
|
|
86
|
+
assert.equal(d.height, 30);
|
|
87
|
+
// A flat region averages to itself.
|
|
88
|
+
const flat = new Float64Array(16).fill(0.5);
|
|
89
|
+
const df = Mx.downsample(flat, 4, 4, 2);
|
|
90
|
+
for (const v of df.data) assert.ok(Math.abs(v - 0.5) < 1e-9);
|
|
91
|
+
});
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Reversed-clip source-frame derivation — fixture-free (synthetic inputs only),
|
|
5
|
+
* so it runs on any machine without the golden answer key.
|
|
6
|
+
*
|
|
7
|
+
* The rule under test: a reversed clip anchors its range at the END of its
|
|
8
|
+
* available source media and mirrors in/out about that end, rather than growing
|
|
9
|
+
* downward from the start. This is fully derivable from the XML (in/out,
|
|
10
|
+
* endoffset) plus the source media's frame count — no reference render needed.
|
|
11
|
+
*
|
|
12
|
+
* forward subclip: source_start = startoffset + in
|
|
13
|
+
* reversed subclip: source_start = (masterFrames - 1 - endoffset) - in
|
|
14
|
+
*
|
|
15
|
+
* The forward path used to be applied to reversed clips too, landing the frame at
|
|
16
|
+
* the wrong (mirrored) position. Values below are validated against a live
|
|
17
|
+
* Resolve readback (get_source_start/end_frame).
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
const test = require('node:test');
|
|
21
|
+
const assert = require('node:assert/strict');
|
|
22
|
+
|
|
23
|
+
const resolve = require('../oracle/resolve');
|
|
24
|
+
|
|
25
|
+
const TPF = 254016000000 / 24; // ticks/frame @24
|
|
26
|
+
|
|
27
|
+
test('reversed subclip end-anchors its range', () => {
|
|
28
|
+
const ctx = { ticksPerFrame: TPF, masterFrames: 47849 };
|
|
29
|
+
const rev = {
|
|
30
|
+
xml_in: 589,
|
|
31
|
+
xml_out: 604,
|
|
32
|
+
pproTicksIn: 589 * TPF,
|
|
33
|
+
is_subclip: true,
|
|
34
|
+
subclip_startoffset: 34727,
|
|
35
|
+
subclip_endoffset: 11832,
|
|
36
|
+
reverse: true,
|
|
37
|
+
};
|
|
38
|
+
// source_start = (masterFrames - 1 - endoffset) - in = 47849 - 1 - 11832 - 589
|
|
39
|
+
// = 35427 (Resolve's get_source_start_frame), NOT the forward start-anchor
|
|
40
|
+
// 34727 + 589 = 35316, nor the out-anchored/off-by-one 35413.
|
|
41
|
+
assert.equal(resolve.deriveSourceFrame(rev, ctx), 35427);
|
|
42
|
+
assert.equal(resolve.deriveSampleFrame(rev, ctx), 35427);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test('forward subclip with identical offsets still start-anchors', () => {
|
|
46
|
+
const ctx = { ticksPerFrame: TPF, masterFrames: 47849 };
|
|
47
|
+
const fwd = {
|
|
48
|
+
xml_in: 589,
|
|
49
|
+
xml_out: 604,
|
|
50
|
+
pproTicksIn: 589 * TPF,
|
|
51
|
+
is_subclip: true,
|
|
52
|
+
subclip_startoffset: 34727,
|
|
53
|
+
subclip_endoffset: 11832,
|
|
54
|
+
reverse: false,
|
|
55
|
+
};
|
|
56
|
+
assert.equal(resolve.deriveSourceFrame(fwd, ctx), 34727 + 589); // 35316
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test('full-master reverse (no subclip): (masterFrames - 1) - in', () => {
|
|
60
|
+
const fullRev = {
|
|
61
|
+
xml_in: 100,
|
|
62
|
+
xml_out: 250,
|
|
63
|
+
pproTicksIn: 100 * TPF,
|
|
64
|
+
is_subclip: false,
|
|
65
|
+
reverse: true,
|
|
66
|
+
master_frames: 1000, // per-source override (clip-level) when ctx has none
|
|
67
|
+
};
|
|
68
|
+
// (1000 - 1 - 0) - 100 = 899
|
|
69
|
+
assert.equal(resolve.deriveSourceFrame(fullRev, { ticksPerFrame: TPF }), 899);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test('reverse without any frame count fails loudly (no silent start-anchor)', () => {
|
|
73
|
+
const rev = {
|
|
74
|
+
xml_in: 589,
|
|
75
|
+
xml_out: 604,
|
|
76
|
+
pproTicksIn: 589 * TPF,
|
|
77
|
+
is_subclip: true,
|
|
78
|
+
subclip_startoffset: 34727,
|
|
79
|
+
subclip_endoffset: 11832,
|
|
80
|
+
reverse: true,
|
|
81
|
+
};
|
|
82
|
+
assert.throws(
|
|
83
|
+
() => resolve.deriveSourceFrame(rev, { ticksPerFrame: TPF }),
|
|
84
|
+
/needs a source frame count/,
|
|
85
|
+
);
|
|
86
|
+
});
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Oracle tests — verified END-TO-END against the committed golden answer key
|
|
5
|
+
* (golden_oracle.json: 327 SAMPLE clips, each with input fields + the
|
|
6
|
+
* expected_source_start that Resolve's clip_where actually produced).
|
|
7
|
+
*
|
|
8
|
+
* No raw XML or frames needed — golden_oracle.json embeds the inputs, so these
|
|
9
|
+
* run client-free on any machine.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const test = require('node:test');
|
|
13
|
+
const assert = require('node:assert/strict');
|
|
14
|
+
const fs = require('node:fs');
|
|
15
|
+
const path = require('node:path');
|
|
16
|
+
|
|
17
|
+
const pkg = require('../index');
|
|
18
|
+
const oracle = require('../oracle');
|
|
19
|
+
const resolveTarget = require('../oracle/resolve');
|
|
20
|
+
|
|
21
|
+
const GOLDEN = JSON.parse(
|
|
22
|
+
fs.readFileSync(path.join(pkg.reelFixtureDir(), 'golden_oracle.json'), 'utf8'),
|
|
23
|
+
);
|
|
24
|
+
const COMPARE = JSON.parse(
|
|
25
|
+
fs.readFileSync(path.join(pkg.reelFixtureDir(), 'golden_compare.json'), 'utf8'),
|
|
26
|
+
);
|
|
27
|
+
const CTX = { ticksPerFrame: GOLDEN.ticksPerFrame, sequenceWidth: GOLDEN.sequence.width };
|
|
28
|
+
|
|
29
|
+
test('Oracle (resolve): source_start for all PLAIN (non-subclip) clips', () => {
|
|
30
|
+
const plain = GOLDEN.clips.filter((c) => !c.is_subclip);
|
|
31
|
+
assert.ok(plain.length > 0, 'fixture must contain non-subclip clips');
|
|
32
|
+
let checked = 0;
|
|
33
|
+
for (const c of plain) {
|
|
34
|
+
const { derivedSourceFrame } = resolveTarget.derive(c, CTX);
|
|
35
|
+
assert.equal(
|
|
36
|
+
derivedSourceFrame,
|
|
37
|
+
c.expected_source_start,
|
|
38
|
+
`plain clip @seq${c.seqstart} (${c.source_basename}): derived ${derivedSourceFrame} != expected ${c.expected_source_start}`,
|
|
39
|
+
);
|
|
40
|
+
checked += 1;
|
|
41
|
+
}
|
|
42
|
+
// eslint-disable-next-line no-console
|
|
43
|
+
console.log(`[oracle] plain clips verified: ${checked}/${plain.length}`);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test('Oracle (resolve): source_start for all SUBCLIP clips (startoffset + in)', () => {
|
|
47
|
+
const subs = GOLDEN.clips.filter((c) => c.is_subclip);
|
|
48
|
+
assert.ok(subs.length > 0, 'fixture must contain subclip clips (pickups)');
|
|
49
|
+
for (const c of subs) {
|
|
50
|
+
const { derivedSourceFrame } = resolveTarget.derive(c, CTX);
|
|
51
|
+
assert.equal(
|
|
52
|
+
derivedSourceFrame,
|
|
53
|
+
c.expected_source_start,
|
|
54
|
+
`subclip @seq${c.seqstart} (${c.source_basename}): derived ${derivedSourceFrame} != expected ${c.expected_source_start} (startoffset ${c.subclip_startoffset} + in ${c.xml_in})`,
|
|
55
|
+
);
|
|
56
|
+
// The subclip offset must actually participate (don't rewrite <in>).
|
|
57
|
+
assert.equal(derivedSourceFrame, (c.subclip_startoffset || 0) + c.xml_in);
|
|
58
|
+
}
|
|
59
|
+
// eslint-disable-next-line no-console
|
|
60
|
+
console.log(`[oracle] subclip clips verified: ${subs.length}`);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test('Oracle (resolve): ACCEPTANCE — source_start for ALL 327 clips, zero mismatches', () => {
|
|
64
|
+
assert.equal(GOLDEN.clips.length, 327);
|
|
65
|
+
const mismatches = [];
|
|
66
|
+
for (const c of GOLDEN.clips) {
|
|
67
|
+
const { derivedSourceFrame } = resolveTarget.derive(c, CTX);
|
|
68
|
+
if (derivedSourceFrame !== c.expected_source_start) {
|
|
69
|
+
mismatches.push({ seq: c.seqstart, derived: derivedSourceFrame, expected: c.expected_source_start });
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
assert.deepEqual(mismatches, [], `expected 0 source_start mismatches, got ${mismatches.length}`);
|
|
73
|
+
// eslint-disable-next-line no-console
|
|
74
|
+
console.log(`[oracle] ACCEPTANCE: 327/327 source_start reproduced (0 mismatches)`);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test('Oracle (pluggable target): registry resolves "resolve" and errors on unknown', () => {
|
|
78
|
+
assert.equal(oracle.DEFAULT_TARGET, 'resolve');
|
|
79
|
+
assert.equal(oracle.getTarget('resolve').id, 'resolve');
|
|
80
|
+
assert.throws(() => oracle.getTarget('premiere'), /unknown target "premiere"/);
|
|
81
|
+
// Deriving via the registry must match the direct ruleset for all 327 clips.
|
|
82
|
+
for (const c of GOLDEN.clips) {
|
|
83
|
+
const viaRegistry = oracle.derive(c, CTX);
|
|
84
|
+
const direct = resolveTarget.derive(c, CTX);
|
|
85
|
+
assert.deepEqual(viaRegistry, direct, `registry vs direct mismatch @seq${c.seqstart}`);
|
|
86
|
+
}
|
|
87
|
+
// eslint-disable-next-line no-console
|
|
88
|
+
console.log('[oracle] pluggable target: registry == direct for 327/327');
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test('Oracle (resolve): corrected scale (double-count fix) for ALL 327, ±0.001', () => {
|
|
92
|
+
const mismatches = [];
|
|
93
|
+
for (const c of GOLDEN.clips) {
|
|
94
|
+
const { derivedScaleCorrected } = resolveTarget.derive(c, CTX);
|
|
95
|
+
if (Math.abs(derivedScaleCorrected - c.expected_scale_corrected) > 0.001) {
|
|
96
|
+
mismatches.push({ seq: c.seqstart, derived: derivedScaleCorrected, expected: c.expected_scale_corrected });
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
assert.deepEqual(mismatches, [], `expected 0 scale mismatches (±0.001), got ${mismatches.length}: ${JSON.stringify(mismatches.slice(0, 5))}`);
|
|
100
|
+
// eslint-disable-next-line no-console
|
|
101
|
+
console.log(`[oracle] corrected scale: 327/327 within ±0.001`);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test('Oracle (resolve): slow-mo via ticks — sample frame ~2*in, readback = in', () => {
|
|
105
|
+
// The retimed clips: ticks/tpf diverges from <in> (50% slow-mo => ~2*in).
|
|
106
|
+
const retimes = GOLDEN.clips.filter((c) => resolveTarget.isRetimed(c, CTX));
|
|
107
|
+
assert.ok(retimes.length >= 1, 'fixture must contain at least one retimed clip');
|
|
108
|
+
for (const c of retimes) {
|
|
109
|
+
const out = resolveTarget.derive(c, CTX);
|
|
110
|
+
// Readback (golden_oracle): Resolve reports the speed-adjusted media-in (= in).
|
|
111
|
+
assert.equal(out.derivedSourceFrame, c.expected_source_start);
|
|
112
|
+
assert.equal(out.derivedSourceFrame, c.xml_in);
|
|
113
|
+
// Sample frame (the ticks path) is ~2x the in-frame for a 50% retime.
|
|
114
|
+
const ratio = out.derivedSampleFrame / c.xml_in;
|
|
115
|
+
assert.ok(ratio > 1.5 && ratio < 2.5, `retime @seq${c.seqstart} sample/in ratio ${ratio} not ~2`);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Cross-check the named golden_compare slow-mo case (seqstart 7894): its
|
|
119
|
+
// documented derived_source_frame (the sampled frame) must match the Oracle's
|
|
120
|
+
// ticks-based sample frame within 1 frame (golden labels 2*in; ticks/tpf is
|
|
121
|
+
// exact and lands within a frame of it).
|
|
122
|
+
const sm = COMPARE.cases.find((x) => x.label === 'slowmo_via_ticks');
|
|
123
|
+
const clip = GOLDEN.clips.find((x) => x.seqstart === sm.seqstart);
|
|
124
|
+
assert.ok(clip, 'golden_oracle must contain the slowmo_via_ticks seqstart');
|
|
125
|
+
const out = resolveTarget.derive(clip, CTX);
|
|
126
|
+
assert.ok(
|
|
127
|
+
Math.abs(out.derivedSampleFrame - sm.derived_source_frame) <= 1,
|
|
128
|
+
`sample frame ${out.derivedSampleFrame} not within 1 of golden_compare ${sm.derived_source_frame}`,
|
|
129
|
+
);
|
|
130
|
+
assert.equal(out.retimed, true);
|
|
131
|
+
// eslint-disable-next-line no-console
|
|
132
|
+
console.log(
|
|
133
|
+
`[oracle] retimes verified: ${retimes.length}; slowmo seq${sm.seqstart} ` +
|
|
134
|
+
`sample=${out.derivedSampleFrame} (golden ${sm.derived_source_frame}), readback=${out.derivedSourceFrame}`,
|
|
135
|
+
);
|
|
136
|
+
});
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* P0 ACCEPTANCE (spec §13 P0): on the SAMPLE reel, the Oracle reproduces Resolve's
|
|
5
|
+
* source_start for all 327 cuts AND the comparator classifies the known matches /
|
|
6
|
+
* the 1 genuine wrong correctly.
|
|
7
|
+
*
|
|
8
|
+
* The Oracle half runs client-free from golden_oracle.json (all 327). The
|
|
9
|
+
* comparator half runs on the 4 representative golden_compare pairs (incl. the
|
|
10
|
+
* 1 genuine WRONG = seq2983) and SKIPS-IF-ABSENT — the full-reel 326-match/1-wrong
|
|
11
|
+
* sweep needs every reference frame (Tier-2/volumes), out of scope for Tier 1.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const test = require('node:test');
|
|
15
|
+
const assert = require('node:assert/strict');
|
|
16
|
+
const fs = require('node:fs');
|
|
17
|
+
const path = require('node:path');
|
|
18
|
+
|
|
19
|
+
const pkg = require('../index');
|
|
20
|
+
const oracle = require('../oracle');
|
|
21
|
+
const compare = require('../compare');
|
|
22
|
+
|
|
23
|
+
const DIR = pkg.reelFixtureDir();
|
|
24
|
+
const GOLDEN = JSON.parse(fs.readFileSync(path.join(DIR, 'golden_oracle.json'), 'utf8'));
|
|
25
|
+
const COMPARE = JSON.parse(fs.readFileSync(path.join(DIR, 'golden_compare.json'), 'utf8'));
|
|
26
|
+
const CTX = { ticksPerFrame: GOLDEN.ticksPerFrame, sequenceWidth: GOLDEN.sequence.width };
|
|
27
|
+
|
|
28
|
+
const FRAMES = path.join(DIR, 'frames');
|
|
29
|
+
const HAVE_FRAMES = fs.existsSync(FRAMES) && fs.readdirSync(FRAMES).some((f) => f.endsWith('.png'));
|
|
30
|
+
|
|
31
|
+
test('P0 ACCEPTANCE (Oracle half): source_start + corrected scale for all 327', () => {
|
|
32
|
+
let srcMismatch = 0;
|
|
33
|
+
let scaleMismatch = 0;
|
|
34
|
+
for (const c of GOLDEN.clips) {
|
|
35
|
+
const out = oracle.derive(c, CTX);
|
|
36
|
+
if (out.derivedSourceFrame !== c.expected_source_start) srcMismatch += 1;
|
|
37
|
+
if (Math.abs(out.derivedScaleCorrected - c.expected_scale_corrected) > 0.001) scaleMismatch += 1;
|
|
38
|
+
}
|
|
39
|
+
assert.equal(srcMismatch, 0, `${srcMismatch} source_start mismatches`);
|
|
40
|
+
assert.equal(scaleMismatch, 0, `${scaleMismatch} scale mismatches`);
|
|
41
|
+
// eslint-disable-next-line no-console
|
|
42
|
+
console.log('[P0] ACCEPTANCE Oracle: 327/327 source_start + 327/327 scale');
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test('P0 ACCEPTANCE (comparator half): the 1 genuine WRONG and the matches classify correctly', { skip: HAVE_FRAMES ? false : 'frames/ absent — skipping comparator half' }, async () => {
|
|
46
|
+
const verdicts = {};
|
|
47
|
+
for (const c of COMPARE.cases) {
|
|
48
|
+
const out = await compare.compareFrames(path.join(DIR, c.reference), path.join(DIR, c.derived));
|
|
49
|
+
verdicts[c.label] = out.verdict;
|
|
50
|
+
assert.equal(out.verdict, c.expected_verdict, `${c.label}: ${out.verdict} != ${c.expected_verdict}`);
|
|
51
|
+
}
|
|
52
|
+
// The genuine wrong (seq2983, 111 frames off) MUST be WRONG; the rest MATCH.
|
|
53
|
+
assert.equal(verdicts.genuine_wrong, 'WRONG');
|
|
54
|
+
const matches = Object.values(verdicts).filter((v) => v === 'MATCH').length;
|
|
55
|
+
const wrongs = Object.values(verdicts).filter((v) => v === 'WRONG').length;
|
|
56
|
+
assert.equal(matches, 3);
|
|
57
|
+
assert.equal(wrongs, 1);
|
|
58
|
+
// eslint-disable-next-line no-console
|
|
59
|
+
console.log(`[P0] ACCEPTANCE comparator: ${matches} MATCH / ${wrongs} WRONG (representative golden set)`);
|
|
60
|
+
});
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* P1 ACCEPTANCE (spec §13 P1): run the verify pass on the SAMPLE reel and get a
|
|
5
|
+
* per-cut report matching the manual finding — the dark cut MATCHes, the slow-mo
|
|
6
|
+
* (via ticks) is correct, and the one genuine wrong is flagged.
|
|
7
|
+
*
|
|
8
|
+
* Runs on the 4 representative golden_compare cuts (the manual finding's
|
|
9
|
+
* exemplars) via the verify pipeline end-to-end. SKIPS-IF-ABSENT (frames are
|
|
10
|
+
* git-ignored). The full-reel 326/1 sweep needs every reference frame
|
|
11
|
+
* (Tier-2/volumes), out of Tier-1 scope.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const test = require('node:test');
|
|
15
|
+
const assert = require('node:assert/strict');
|
|
16
|
+
const fs = require('node:fs');
|
|
17
|
+
const path = require('node:path');
|
|
18
|
+
|
|
19
|
+
const pkg = require('../index');
|
|
20
|
+
const { verify } = require('../ops/verify');
|
|
21
|
+
|
|
22
|
+
const SAMPLE = pkg.reelFixtureDir();
|
|
23
|
+
const GOLDEN = JSON.parse(fs.readFileSync(path.join(SAMPLE, 'golden_oracle.json'), 'utf8'));
|
|
24
|
+
const COMPARE = JSON.parse(fs.readFileSync(path.join(SAMPLE, 'golden_compare.json'), 'utf8'));
|
|
25
|
+
const FRAMES = path.join(SAMPLE, 'frames');
|
|
26
|
+
const HAVE_FRAMES = fs.existsSync(FRAMES) && fs.readdirSync(FRAMES).some((f) => f.endsWith('.png'));
|
|
27
|
+
const SKIP = HAVE_FRAMES ? false : 'frames/ absent (git-ignored) — skipping P1 acceptance';
|
|
28
|
+
|
|
29
|
+
const TPF = GOLDEN.ticksPerFrame;
|
|
30
|
+
|
|
31
|
+
// Build a small "reel" model from the golden_compare exemplars. Oracle inputs are
|
|
32
|
+
// synthesized consistently from each case's derived_source_frame so the pipeline
|
|
33
|
+
// runs intact (the Oracle is separately verified on all 327 in oracle.test.js).
|
|
34
|
+
function modelFromCompare() {
|
|
35
|
+
return {
|
|
36
|
+
sequence: GOLDEN.sequence,
|
|
37
|
+
ticksPerFrame: TPF,
|
|
38
|
+
clips: COMPARE.cases.map((c) => ({
|
|
39
|
+
seqstart: c.seqstart,
|
|
40
|
+
label: c.label,
|
|
41
|
+
xml_in: c.derived_source_frame,
|
|
42
|
+
pproTicksIn: c.derived_source_frame * TPF,
|
|
43
|
+
is_subclip: false,
|
|
44
|
+
subclip_startoffset: 0,
|
|
45
|
+
scale_premiere: 100,
|
|
46
|
+
srcW: GOLDEN.sequence.width,
|
|
47
|
+
srcH: GOLDEN.sequence.height,
|
|
48
|
+
})),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
test('P1 ACCEPTANCE: verify report matches the manual finding on the golden reel', { skip: SKIP }, async () => {
|
|
53
|
+
const model = modelFromCompare();
|
|
54
|
+
const byLabel = Object.fromEntries(COMPARE.cases.map((c) => [c.seqstart, c]));
|
|
55
|
+
const referenceProvider = (clip) => {
|
|
56
|
+
const c = byLabel[clip.seqstart];
|
|
57
|
+
return { referencePath: path.join(SAMPLE, c.reference), derivedPath: path.join(SAMPLE, c.derived), tier: 'A', label: c.label };
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const rep = await verify(model, { referenceProvider, target: 'resolve' });
|
|
61
|
+
|
|
62
|
+
const verdictBySeq = Object.fromEntries(rep.perCut.map((c) => [c.seqstart, c.status]));
|
|
63
|
+
for (const c of COMPARE.cases) {
|
|
64
|
+
assert.equal(verdictBySeq[c.seqstart], c.expected_verdict, `${c.label}: ${verdictBySeq[c.seqstart]} != ${c.expected_verdict}`);
|
|
65
|
+
}
|
|
66
|
+
// The manual finding, explicitly: dark grade MATCHes, slow-mo correct, 1 wrong.
|
|
67
|
+
assert.equal(rep.summary.matched, 3);
|
|
68
|
+
assert.equal(rep.summary.wrong, 1);
|
|
69
|
+
// Tier A with a MATCH => content-verified badge (we had a picture).
|
|
70
|
+
const dark = rep.perCut.find((c) => c.seqstart === 25748);
|
|
71
|
+
assert.equal(dark.status, 'MATCH');
|
|
72
|
+
assert.equal(dark.badge, 'content-verified');
|
|
73
|
+
// eslint-disable-next-line no-console
|
|
74
|
+
console.log(`[P1] ACCEPTANCE: ${rep.summary.matched} MATCH / ${rep.summary.wrong} WRONG (dark-grade MATCH, slow-mo correct)`);
|
|
75
|
+
});
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* P4.5 ACCEPTANCE (patch / re-cut half) — on the real SAMPLE reel: ingest a
|
|
5
|
+
* revised turnover that changes exactly two cuts (one source swap, one duration
|
|
6
|
+
* change) and assert the lifecycle re-conforms ONLY the changed cuts, leaves the
|
|
7
|
+
* other 325 untouched, re-derives the changed cuts correctly (Oracle), and
|
|
8
|
+
* ripples the downstream record positions by the duration delta.
|
|
9
|
+
*
|
|
10
|
+
* Pure (Oracle math + diff; no media), so it runs anywhere. The VFX/Topaz insert
|
|
11
|
+
* half of the umbrella P4.5-acceptance still needs real render artifacts.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const test = require('node:test');
|
|
15
|
+
const assert = require('node:assert/strict');
|
|
16
|
+
const fs = require('node:fs');
|
|
17
|
+
const path = require('node:path');
|
|
18
|
+
|
|
19
|
+
const pkg = require('../index');
|
|
20
|
+
const oracle = require('../oracle');
|
|
21
|
+
const { diffTimelines, reverifyChanged, rippleUpdate } = require('../ops/patch');
|
|
22
|
+
|
|
23
|
+
const GOLDEN = JSON.parse(fs.readFileSync(path.join(pkg.reelFixtureDir(), 'golden_oracle.json'), 'utf8'));
|
|
24
|
+
const CTX = { ticksPerFrame: GOLDEN.ticksPerFrame, sequenceWidth: GOLDEN.sequence.width };
|
|
25
|
+
|
|
26
|
+
// CURRENT online: the 327 conformed cuts (sourceFrame = the Oracle/clip_where frame).
|
|
27
|
+
const sorted = [...GOLDEN.clips].sort((a, b) => a.seqstart - b.seqstart);
|
|
28
|
+
const CURRENT = sorted.map((c) => ({ ...c, sourceFrame: c.expected_source_start }));
|
|
29
|
+
|
|
30
|
+
test('P4.5 ACCEPTANCE (patch): a revised cut re-conforms ONLY changed cuts, ripples downstream', async () => {
|
|
31
|
+
// Pick two real cuts to edit: a duration change early in the reel (so it has
|
|
32
|
+
// downstream neighbours to ripple) and a source swap later.
|
|
33
|
+
const durIdx = 40;
|
|
34
|
+
const swapIdx = 120;
|
|
35
|
+
const durSeq = CURRENT[durIdx].seqstart;
|
|
36
|
+
const swapSeq = CURRENT[swapIdx].seqstart;
|
|
37
|
+
// A donor cut with a genuinely different source for the swap.
|
|
38
|
+
const donor = CURRENT.find((c) => c.source_basename !== CURRENT[swapIdx].source_basename);
|
|
39
|
+
assert.ok(donor, 'a donor with a different source exists');
|
|
40
|
+
|
|
41
|
+
const DELTA = 12;
|
|
42
|
+
const REVISED = CURRENT.map((c) => {
|
|
43
|
+
if (c.seqstart === durSeq) {
|
|
44
|
+
// Duration change only (same source) → changed + drives ripple.
|
|
45
|
+
return { ...c, seqend: c.seqend + DELTA };
|
|
46
|
+
}
|
|
47
|
+
if (c.seqstart === swapSeq) {
|
|
48
|
+
// Source swap: graft the donor's source + Oracle inputs onto this slot,
|
|
49
|
+
// keeping the original record position/duration (a content replacement).
|
|
50
|
+
return {
|
|
51
|
+
...donor,
|
|
52
|
+
seqstart: c.seqstart,
|
|
53
|
+
seqend: c.seqend,
|
|
54
|
+
source_basename: donor.source_basename,
|
|
55
|
+
sourceFrame: donor.expected_source_start,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
return c;
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// 1) DIFF — exactly the two edited cuts are flagged; the rest are unchanged.
|
|
62
|
+
const d = diffTimelines(CURRENT, REVISED);
|
|
63
|
+
assert.equal(d.added.length, 0);
|
|
64
|
+
assert.equal(d.removed.length, 0);
|
|
65
|
+
assert.equal(d.changed.length, 2, `expected 2 changed, got ${d.changed.map((x) => x.seqstart)}`);
|
|
66
|
+
assert.equal(d.unchanged.length, 325);
|
|
67
|
+
const changedSeqs = d.changed.map((x) => x.seqstart).sort((a, b) => a - b);
|
|
68
|
+
assert.deepEqual(changedSeqs, [durSeq, swapSeq].sort((a, b) => a - b));
|
|
69
|
+
|
|
70
|
+
// 2) RE-VERIFY — only changed cuts are re-conformed; the 325 unchanged are skipped.
|
|
71
|
+
const reconformed = [];
|
|
72
|
+
const out = await reverifyChanged(d, async (cut) => {
|
|
73
|
+
const derived = oracle.derive(cut, CTX).derivedSourceFrame;
|
|
74
|
+
reconformed.push({ seqstart: cut.seqstart, derived });
|
|
75
|
+
return 'MATCH';
|
|
76
|
+
});
|
|
77
|
+
assert.equal(out.skipped, 325);
|
|
78
|
+
assert.equal(out.reverified.length, 2);
|
|
79
|
+
assert.deepEqual(reconformed.map((r) => r.seqstart).sort((a, b) => a - b), changedSeqs);
|
|
80
|
+
// The swapped cut re-derives to the DONOR's source frame (the re-conform is correct,
|
|
81
|
+
// not a stale carry-over of the old cut's frame).
|
|
82
|
+
const swapReconform = reconformed.find((r) => r.seqstart === swapSeq);
|
|
83
|
+
assert.equal(swapReconform.derived, donor.expected_source_start, 'swapped cut re-derived to the new source frame');
|
|
84
|
+
|
|
85
|
+
// 3) RIPPLE — the +DELTA duration shifts every downstream cut by DELTA; the
|
|
86
|
+
// changed cut + its neighbour are marked touched.
|
|
87
|
+
const { rippled, touched, totalShift } = rippleUpdate(CURRENT, d);
|
|
88
|
+
assert.equal(totalShift, DELTA, `total downstream shift should equal the duration delta (${DELTA})`);
|
|
89
|
+
const durOrdinal = sorted.findIndex((c) => c.seqstart === durSeq);
|
|
90
|
+
// rippleUpdate preserves the sorted order; verify by ordinal: downstream shifts, upstream doesn't.
|
|
91
|
+
rippled.forEach((r, i) => {
|
|
92
|
+
if (i > durOrdinal) {
|
|
93
|
+
assert.equal(r.seqstart, sorted[i].seqstart + DELTA, `downstream cut #${i} must shift by ${DELTA}`);
|
|
94
|
+
} else if (i < durOrdinal) {
|
|
95
|
+
assert.equal(r.seqstart, sorted[i].seqstart, `upstream cut #${i} must not move`);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
assert.ok(touched.includes(durSeq), 'the duration-changed cut is touched');
|
|
99
|
+
|
|
100
|
+
// eslint-disable-next-line no-console
|
|
101
|
+
console.log(`[P4.5] PATCH ACCEPTANCE: revised SAMPLE reel → 2/327 cuts re-conformed (swap@${swapSeq}, dur@${durSeq}), 325 skipped, downstream rippled +${DELTA}.`);
|
|
102
|
+
});
|