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