premiere-cli 0.2.0__py3-none-any.whl

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 (247) hide show
  1. premiere_cli/__init__.py +11 -0
  2. premiere_cli/cli.py +3664 -0
  3. premiere_cli/log.py +66 -0
  4. premiere_cli/panel/.debug +8 -0
  5. premiere_cli/panel/CSXS/manifest.xml +52 -0
  6. premiere_cli/panel/css/style.css +53 -0
  7. premiere_cli/panel/host/commands/add-adjustment-layer.jsx +190 -0
  8. premiere_cli/panel/host/commands/add-audio-keyframes.jsx +176 -0
  9. premiere_cli/panel/host/commands/add-custom-metadata-field.jsx +63 -0
  10. premiere_cli/panel/host/commands/add-keyframe.jsx +181 -0
  11. premiere_cli/panel/host/commands/add-marker-to-project-item.jsx +194 -0
  12. premiere_cli/panel/host/commands/add-marker.jsx +129 -0
  13. premiere_cli/panel/host/commands/add-text-overlay.jsx +101 -0
  14. premiere_cli/panel/host/commands/add-to-render-queue.jsx +120 -0
  15. premiere_cli/panel/host/commands/add-to-timeline.jsx +300 -0
  16. premiere_cli/panel/host/commands/add-track.jsx +188 -0
  17. premiere_cli/panel/host/commands/add-transition.jsx +134 -0
  18. premiere_cli/panel/host/commands/adjust-audio-levels.jsx +132 -0
  19. premiere_cli/panel/host/commands/apply-audio-effect.jsx +118 -0
  20. premiere_cli/panel/host/commands/apply-effect.jsx +122 -0
  21. premiere_cli/panel/host/commands/apply-lut.jsx +107 -0
  22. premiere_cli/panel/host/commands/attach-custom-property.jsx +66 -0
  23. premiere_cli/panel/host/commands/auto-reframe-sequence.jsx +124 -0
  24. premiere_cli/panel/host/commands/batch-add-transitions.jsx +158 -0
  25. premiere_cli/panel/host/commands/batch-apply-effect.jsx +182 -0
  26. premiere_cli/panel/host/commands/batch-rename-clips.jsx +170 -0
  27. premiere_cli/panel/host/commands/batch-set-clips-enabled.jsx +145 -0
  28. premiere_cli/panel/host/commands/clear-item-in-out.jsx +117 -0
  29. premiere_cli/panel/host/commands/clear-sequence-in-out.jsx +100 -0
  30. premiere_cli/panel/host/commands/close-all-source-clips.jsx +32 -0
  31. premiere_cli/panel/host/commands/close-sequence.jsx +80 -0
  32. premiere_cli/panel/host/commands/close-source-monitor.jsx +32 -0
  33. premiere_cli/panel/host/commands/color-correct.jsx +164 -0
  34. premiere_cli/panel/host/commands/copy-effect-values.jsx +183 -0
  35. premiere_cli/panel/host/commands/copy-effects-between-clips.jsx +234 -0
  36. premiere_cli/panel/host/commands/create-bars-and-tone.jsx +152 -0
  37. premiere_cli/panel/host/commands/create-bin.jsx +76 -0
  38. premiere_cli/panel/host/commands/create-caption-track.jsx +159 -0
  39. premiere_cli/panel/host/commands/create-sequence-from-clips.jsx +170 -0
  40. premiere_cli/panel/host/commands/create-sequence-from-preset.jsx +90 -0
  41. premiere_cli/panel/host/commands/create-sequence.jsx +166 -0
  42. premiere_cli/panel/host/commands/create-smart-bin.jsx +80 -0
  43. premiere_cli/panel/host/commands/create-subclip.jsx +248 -0
  44. premiere_cli/panel/host/commands/create-subsequence.jsx +136 -0
  45. premiere_cli/panel/host/commands/debug-qe-inspect.jsx +248 -0
  46. premiere_cli/panel/host/commands/debug-qe-try-mutate.jsx +305 -0
  47. premiere_cli/panel/host/commands/delete-marker.jsx +178 -0
  48. premiere_cli/panel/host/commands/deselect-all-clips.jsx +98 -0
  49. premiere_cli/panel/host/commands/detach-proxy.jsx +116 -0
  50. premiere_cli/panel/host/commands/duplicate-clip.jsx +140 -0
  51. premiere_cli/panel/host/commands/duplicate-sequence.jsx +150 -0
  52. premiere_cli/panel/host/commands/encode-file.jsx +86 -0
  53. premiere_cli/panel/host/commands/encode-project-item.jsx +145 -0
  54. premiere_cli/panel/host/commands/export-aaf.jsx +77 -0
  55. premiere_cli/panel/host/commands/export-fcp-xml.jsx +65 -0
  56. premiere_cli/panel/host/commands/export-frame.jsx +381 -0
  57. premiere_cli/panel/host/commands/export-omf.jsx +95 -0
  58. premiere_cli/panel/host/commands/export-sequence-as-project.jsx +65 -0
  59. premiere_cli/panel/host/commands/export-sequence.jsx +100 -0
  60. premiere_cli/panel/host/commands/extract-audio-track.jsx +181 -0
  61. premiere_cli/panel/host/commands/extract-selection.jsx +113 -0
  62. premiere_cli/panel/host/commands/find-items-by-media-path.jsx +112 -0
  63. premiere_cli/panel/host/commands/freeze-frame.jsx +215 -0
  64. premiere_cli/panel/host/commands/get-active-sequence.jsx +103 -0
  65. premiere_cli/panel/host/commands/get-all-project-paths.jsx +72 -0
  66. premiere_cli/panel/host/commands/get-bin-contents.jsx +176 -0
  67. premiere_cli/panel/host/commands/get-clip-adjustment-layer.jsx +83 -0
  68. premiere_cli/panel/host/commands/get-clip-at-playhead.jsx +99 -0
  69. premiere_cli/panel/host/commands/get-clip-at-position.jsx +120 -0
  70. premiere_cli/panel/host/commands/get-clip-links.jsx +135 -0
  71. premiere_cli/panel/host/commands/get-clip-markers.jsx +144 -0
  72. premiere_cli/panel/host/commands/get-clip-speed.jsx +69 -0
  73. premiere_cli/panel/host/commands/get-color-label.jsx +89 -0
  74. premiere_cli/panel/host/commands/get-color-space.jsx +99 -0
  75. premiere_cli/panel/host/commands/get-duplicate-media.jsx +78 -0
  76. premiere_cli/panel/host/commands/get-effect-properties.jsx +129 -0
  77. premiere_cli/panel/host/commands/get-encoder-presets.jsx +131 -0
  78. premiere_cli/panel/host/commands/get-export-file-extension.jsx +56 -0
  79. premiere_cli/panel/host/commands/get-footage-interpretation.jsx +113 -0
  80. premiere_cli/panel/host/commands/get-full-clip-info.jsx +162 -0
  81. premiere_cli/panel/host/commands/get-full-project-overview.jsx +193 -0
  82. premiere_cli/panel/host/commands/get-full-sequence-info.jsx +184 -0
  83. premiere_cli/panel/host/commands/get-graphics-white-luminance.jsx +31 -0
  84. premiere_cli/panel/host/commands/get-insertion-bin.jsx +35 -0
  85. premiere_cli/panel/host/commands/get-item-metadata.jsx +94 -0
  86. premiere_cli/panel/host/commands/get-keyframes.jsx +158 -0
  87. premiere_cli/panel/host/commands/get-mogrt-component.jsx +111 -0
  88. premiere_cli/panel/host/commands/get-next-edit-point.jsx +131 -0
  89. premiere_cli/panel/host/commands/get-offline-media.jsx +67 -0
  90. premiere_cli/panel/host/commands/get-open-projects.jsx +50 -0
  91. premiere_cli/panel/host/commands/get-premiere-state.jsx +164 -0
  92. premiere_cli/panel/host/commands/get-project-info.jsx +63 -0
  93. premiere_cli/panel/host/commands/get-project-item-info.jsx +247 -0
  94. premiere_cli/panel/host/commands/get-project-panel-metadata.jsx +30 -0
  95. premiere_cli/panel/host/commands/get-qe-clip-info.jsx +198 -0
  96. premiere_cli/panel/host/commands/get-render-queue-status.jsx +35 -0
  97. premiere_cli/panel/host/commands/get-sequence-count.jsx +24 -0
  98. premiere_cli/panel/host/commands/get-sequence-in-out.jsx +76 -0
  99. premiere_cli/panel/host/commands/get-sequence-markers-by-type.jsx +89 -0
  100. premiere_cli/panel/host/commands/get-source-monitor-info.jsx +34 -0
  101. premiere_cli/panel/host/commands/get-source-monitor-position.jsx +30 -0
  102. premiere_cli/panel/host/commands/get-target-tracks.jsx +76 -0
  103. premiere_cli/panel/host/commands/get-timeline-gaps.jsx +124 -0
  104. premiere_cli/panel/host/commands/get-timeline-summary.jsx +154 -0
  105. premiere_cli/panel/host/commands/get-total-clip-count.jsx +67 -0
  106. premiere_cli/panel/host/commands/get-track-info.jsx +117 -0
  107. premiere_cli/panel/host/commands/get-unused-media.jsx +99 -0
  108. premiere_cli/panel/host/commands/get-used-media-report.jsx +125 -0
  109. premiere_cli/panel/host/commands/get-value-at-time.jsx +135 -0
  110. premiere_cli/panel/host/commands/get-version-info.jsx +33 -0
  111. premiere_cli/panel/host/commands/get-work-area.jsx +52 -0
  112. premiere_cli/panel/host/commands/get-workspaces.jsx +29 -0
  113. premiere_cli/panel/host/commands/get-xmp-metadata.jsx +107 -0
  114. premiere_cli/panel/host/commands/import-ae-comps.jsx +129 -0
  115. premiere_cli/panel/host/commands/import-fcp-xml.jsx +103 -0
  116. premiere_cli/panel/host/commands/import-folder.jsx +193 -0
  117. premiere_cli/panel/host/commands/import-image-sequence.jsx +171 -0
  118. premiere_cli/panel/host/commands/import-media.jsx +217 -0
  119. premiere_cli/panel/host/commands/import-mogrt-from-library.jsx +105 -0
  120. premiere_cli/panel/host/commands/import-mogrt.jsx +111 -0
  121. premiere_cli/panel/host/commands/import-sequences-from-project.jsx +80 -0
  122. premiere_cli/panel/host/commands/insert-from-source.jsx +162 -0
  123. premiere_cli/panel/host/commands/inspect-dom-object.jsx +162 -0
  124. premiere_cli/panel/host/commands/invert-selection.jsx +114 -0
  125. premiere_cli/panel/host/commands/is-work-area-enabled.jsx +88 -0
  126. premiere_cli/panel/host/commands/lift-selection.jsx +106 -0
  127. premiere_cli/panel/host/commands/link-selection.jsx +111 -0
  128. premiere_cli/panel/host/commands/list-available-audio-effects.jsx +33 -0
  129. premiere_cli/panel/host/commands/list-available-audio-transitions.jsx +35 -0
  130. premiere_cli/panel/host/commands/list-available-effects.jsx +33 -0
  131. premiere_cli/panel/host/commands/list-available-transitions.jsx +74 -0
  132. premiere_cli/panel/host/commands/list-markers.jsx +69 -0
  133. premiere_cli/panel/host/commands/list-project-items.jsx +105 -0
  134. premiere_cli/panel/host/commands/lock-track.jsx +114 -0
  135. premiere_cli/panel/host/commands/manage-proxies.jsx +176 -0
  136. premiere_cli/panel/host/commands/match-frame.jsx +141 -0
  137. premiere_cli/panel/host/commands/move-clip-to-track.jsx +220 -0
  138. premiere_cli/panel/host/commands/move-clip.jsx +97 -0
  139. premiere_cli/panel/host/commands/move-items-to-bin.jsx +194 -0
  140. premiere_cli/panel/host/commands/move-playhead-to-edit.jsx +174 -0
  141. premiere_cli/panel/host/commands/move-playhead.jsx +137 -0
  142. premiere_cli/panel/host/commands/nest-clips.jsx +179 -0
  143. premiere_cli/panel/host/commands/open-in-source.jsx +117 -0
  144. premiere_cli/panel/host/commands/open-project.jsx +61 -0
  145. premiere_cli/panel/host/commands/overwrite-clip-at.jsx +350 -0
  146. premiere_cli/panel/host/commands/overwrite-from-source.jsx +150 -0
  147. premiere_cli/panel/host/commands/play-source-monitor.jsx +36 -0
  148. premiere_cli/panel/host/commands/play-timeline.jsx +24 -0
  149. premiere_cli/panel/host/commands/razor-all-tracks.jsx +171 -0
  150. premiere_cli/panel/host/commands/redo.jsx +69 -0
  151. premiere_cli/panel/host/commands/refresh-media.jsx +101 -0
  152. premiere_cli/panel/host/commands/relink-media.jsx +132 -0
  153. premiere_cli/panel/host/commands/remove-all-effects.jsx +114 -0
  154. premiere_cli/panel/host/commands/remove-effect-by-name.jsx +143 -0
  155. premiere_cli/panel/host/commands/remove-effect.jsx +122 -0
  156. premiere_cli/panel/host/commands/remove-from-timeline.jsx +93 -0
  157. premiere_cli/panel/host/commands/remove-keyframe-range.jsx +173 -0
  158. premiere_cli/panel/host/commands/remove-keyframe.jsx +192 -0
  159. premiere_cli/panel/host/commands/remove-selected-clips.jsx +126 -0
  160. premiere_cli/panel/host/commands/remove-track-intervals.jsx +254 -0
  161. premiere_cli/panel/host/commands/remove-transition.jsx +164 -0
  162. premiere_cli/panel/host/commands/rename-bin.jsx +88 -0
  163. premiere_cli/panel/host/commands/rename-clip.jsx +127 -0
  164. premiere_cli/panel/host/commands/rename-project-item.jsx +111 -0
  165. premiere_cli/panel/host/commands/rename-track.jsx +84 -0
  166. premiere_cli/panel/host/commands/replace-clip-media.jsx +140 -0
  167. premiere_cli/panel/host/commands/replace-clip.jsx +188 -0
  168. premiere_cli/panel/host/commands/reverse-clip.jsx +187 -0
  169. premiere_cli/panel/host/commands/ripple-delete-clip.jsx +156 -0
  170. premiere_cli/panel/host/commands/roll-edit.jsx +162 -0
  171. premiere_cli/panel/host/commands/save-project-as.jsx +55 -0
  172. premiere_cli/panel/host/commands/save-project.jsx +57 -0
  173. premiere_cli/panel/host/commands/scene-edit-detection.jsx +128 -0
  174. premiere_cli/panel/host/commands/search-project-items.jsx +180 -0
  175. premiere_cli/panel/host/commands/select-all-clips.jsx +107 -0
  176. premiere_cli/panel/host/commands/select-clips-by-color.jsx +142 -0
  177. premiere_cli/panel/host/commands/select-clips-by-name.jsx +133 -0
  178. premiere_cli/panel/host/commands/select-clips-in-range.jsx +133 -0
  179. premiere_cli/panel/host/commands/select-disabled-clips.jsx +127 -0
  180. premiere_cli/panel/host/commands/select-project-item.jsx +115 -0
  181. premiere_cli/panel/host/commands/set-active-project.jsx +184 -0
  182. premiere_cli/panel/host/commands/set-active-sequence.jsx +87 -0
  183. premiere_cli/panel/host/commands/set-all-tracks-targeted.jsx +112 -0
  184. premiere_cli/panel/host/commands/set-anti-alias-quality.jsx +125 -0
  185. premiere_cli/panel/host/commands/set-blend-mode.jsx +111 -0
  186. premiere_cli/panel/host/commands/set-clip-anchor-point.jsx +102 -0
  187. premiere_cli/panel/host/commands/set-clip-enabled.jsx +127 -0
  188. premiere_cli/panel/host/commands/set-clip-opacity.jsx +102 -0
  189. premiere_cli/panel/host/commands/set-clip-pan.jsx +107 -0
  190. premiere_cli/panel/host/commands/set-clip-position.jsx +103 -0
  191. premiere_cli/panel/host/commands/set-clip-properties.jsx +221 -0
  192. premiere_cli/panel/host/commands/set-clip-rotation.jsx +102 -0
  193. premiere_cli/panel/host/commands/set-clip-scale.jsx +102 -0
  194. premiere_cli/panel/host/commands/set-clip-selection.jsx +144 -0
  195. premiere_cli/panel/host/commands/set-clip-speed.jsx +160 -0
  196. premiere_cli/panel/host/commands/set-clip-volume.jsx +128 -0
  197. premiere_cli/panel/host/commands/set-color-label.jsx +123 -0
  198. premiere_cli/panel/host/commands/set-color-value.jsx +155 -0
  199. premiere_cli/panel/host/commands/set-effect-property.jsx +124 -0
  200. premiere_cli/panel/host/commands/set-footage-interpretation.jsx +173 -0
  201. premiere_cli/panel/host/commands/set-frame-blend.jsx +145 -0
  202. premiere_cli/panel/host/commands/set-graphics-white-luminance.jsx +73 -0
  203. premiere_cli/panel/host/commands/set-item-in-out.jsx +180 -0
  204. premiere_cli/panel/host/commands/set-item-metadata.jsx +134 -0
  205. premiere_cli/panel/host/commands/set-item-offline.jsx +109 -0
  206. premiere_cli/panel/host/commands/set-item-start-time.jsx +139 -0
  207. premiere_cli/panel/host/commands/set-keyframe-interpolation.jsx +186 -0
  208. premiere_cli/panel/host/commands/set-override-frame-rate.jsx +123 -0
  209. premiere_cli/panel/host/commands/set-override-pixel-aspect-ratio.jsx +129 -0
  210. premiere_cli/panel/host/commands/set-poster-frame.jsx +192 -0
  211. premiere_cli/panel/host/commands/set-project-panel-metadata.jsx +69 -0
  212. premiere_cli/panel/host/commands/set-scale-to-frame-size.jsx +101 -0
  213. premiere_cli/panel/host/commands/set-scale-width-height.jsx +140 -0
  214. premiere_cli/panel/host/commands/set-sequence-in-out.jsx +65 -0
  215. premiere_cli/panel/host/commands/set-sequence-settings.jsx +184 -0
  216. premiere_cli/panel/host/commands/set-source-in-out.jsx +98 -0
  217. premiere_cli/panel/host/commands/set-target-track.jsx +91 -0
  218. premiere_cli/panel/host/commands/set-time-interpolation.jsx +141 -0
  219. premiere_cli/panel/host/commands/set-track-mute.jsx +91 -0
  220. premiere_cli/panel/host/commands/set-track-visibility.jsx +94 -0
  221. premiere_cli/panel/host/commands/set-transcode-on-ingest.jsx +78 -0
  222. premiere_cli/panel/host/commands/set-uniform-scale.jsx +104 -0
  223. premiere_cli/panel/host/commands/set-work-area.jsx +76 -0
  224. premiere_cli/panel/host/commands/set-workspace.jsx +35 -0
  225. premiere_cli/panel/host/commands/set-xmp-metadata.jsx +157 -0
  226. premiere_cli/panel/host/commands/slide-edit.jsx +162 -0
  227. premiere_cli/panel/host/commands/slip-edit.jsx +167 -0
  228. premiere_cli/panel/host/commands/split-clip.jsx +144 -0
  229. premiere_cli/panel/host/commands/stabilize-clip.jsx +139 -0
  230. premiere_cli/panel/host/commands/stop-playback.jsx +23 -0
  231. premiere_cli/panel/host/commands/trim-clip.jsx +111 -0
  232. premiere_cli/panel/host/commands/undo.jsx +114 -0
  233. premiere_cli/panel/host/commands/unlink-selection.jsx +106 -0
  234. premiere_cli/panel/host/commands/unnest-sequence.jsx +207 -0
  235. premiere_cli/panel/host/commands/update-marker.jsx +120 -0
  236. premiere_cli/panel/host/index.jsx +1227 -0
  237. premiere_cli/panel/host/presets/default-4k-25fps.sqpreset +63 -0
  238. premiere_cli/panel/index.html +16 -0
  239. premiere_cli/panel/js/cs-interface.js +11 -0
  240. premiere_cli/panel/js/main.js +409 -0
  241. premiere_cli/panel_install.py +220 -0
  242. premiere_cli-0.2.0.dist-info/METADATA +127 -0
  243. premiere_cli-0.2.0.dist-info/RECORD +247 -0
  244. premiere_cli-0.2.0.dist-info/WHEEL +5 -0
  245. premiere_cli-0.2.0.dist-info/entry_points.txt +3 -0
  246. premiere_cli-0.2.0.dist-info/licenses/LICENSE +428 -0
  247. premiere_cli-0.2.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,134 @@
1
+ // Command: add-transition → ppb_addTransition
2
+ // Loaded lazily by ppb_dispatch (host/index.jsx) on first use, into the
3
+ // same global ExtendScript context — shared helpers (ensureQEEnabled,
4
+ // activateSequenceForQE, qeFindNthNonEmptyClip, ppbApplyTransitionToClip,
5
+ // ...) are already defined there.
6
+ //
7
+ // Merges the reference project's add_transition (track+cut-point
8
+ // addressing) and add_transition_to_clip (node_id+position addressing)
9
+ // into one command, addressed the same way as get-full-clip-info
10
+ // (trackType/trackIndex/clipIndex) rather than either reference's own
11
+ // scheme — video-only (no reference repo demonstrates an audio-transition
12
+ // "add" API). `atEnd: true` applies the transition at the clip's end
13
+ // (matching add_transition_to_clip's "end" position and the cut-point
14
+ // between this clip and its successor); `atEnd: false` applies it at the
15
+ // clip's start. The reference's "both" position is intentionally dropped
16
+ // — call this command twice (once per atEnd value) to reproduce it.
17
+ // `transitionName` omitted/null = the default transition, per ayushozha's
18
+ // addTransition(null, true, "1.0") convention. All the actual apply-and-
19
+ // verify work (disputed addTransition signature, transitions-count
20
+ // verification) lives in the shared ppbApplyTransitionToClip helper.
21
+ function ppb_addTransition(argsJson) {
22
+ try {
23
+ var args;
24
+ try {
25
+ args = JSON.parse(argsJson);
26
+ } catch (e) {
27
+ return JSON.stringify({ ok: false, error: "invalid args JSON: " + e.toString() });
28
+ }
29
+
30
+ if (args.trackType !== "video") {
31
+ return JSON.stringify({ ok: false, error: "trackType must be \"video\" — no reference API demonstrates adding a transition to an audio track" });
32
+ }
33
+ if (typeof args.trackIndex !== "number" || args.trackIndex < 0 || Math.floor(args.trackIndex) !== args.trackIndex) {
34
+ return JSON.stringify({ ok: false, error: "trackIndex must be a non-negative integer" });
35
+ }
36
+ if (typeof args.clipIndex !== "number" || args.clipIndex < 0 || Math.floor(args.clipIndex) !== args.clipIndex) {
37
+ return JSON.stringify({ ok: false, error: "clipIndex must be a non-negative integer" });
38
+ }
39
+ if (typeof args.atEnd !== "boolean") {
40
+ return JSON.stringify({ ok: false, error: "atEnd must be a boolean (true = end of clip, false = start of clip)" });
41
+ }
42
+ var transitionName = null;
43
+ if (typeof args.transitionName === "string" && args.transitionName.length > 0) {
44
+ transitionName = args.transitionName;
45
+ } else if (args.transitionName !== undefined && args.transitionName !== null) {
46
+ return JSON.stringify({ ok: false, error: "transitionName must be a non-empty string, or omitted/null for the default transition" });
47
+ }
48
+
49
+ // Live-tested 2026-07-17: the null-transition ("default") form
50
+ // no-throws but adds NOTHING on this build, while a NAMED transition
51
+ // works and verifies. Substitute Cross Dissolve (Premiere's factory
52
+ // default) rather than burning attempts on a known-dead path.
53
+ if (transitionName === null) {
54
+ transitionName = "Cross Dissolve";
55
+ }
56
+ var durationSeconds = 1.0;
57
+ if (typeof args.durationSeconds === "number") {
58
+ if (args.durationSeconds <= 0) {
59
+ return JSON.stringify({ ok: false, error: "durationSeconds must be a positive number" });
60
+ }
61
+ durationSeconds = args.durationSeconds;
62
+ } else if (args.durationSeconds !== undefined && args.durationSeconds !== null) {
63
+ return JSON.stringify({ ok: false, error: "durationSeconds must be a number" });
64
+ }
65
+
66
+ if (!app.project) {
67
+ return JSON.stringify({ ok: false, error: "no project open" });
68
+ }
69
+
70
+ var seq = null;
71
+ if (args.sequenceName && typeof args.sequenceName === "string") {
72
+ seq = findSequenceByName(args.sequenceName);
73
+ if (!seq) {
74
+ return JSON.stringify({ ok: false, error: "no sequence named \"" + args.sequenceName + "\" is open" });
75
+ }
76
+ } else {
77
+ seq = app.project.activeSequence;
78
+ if (!seq) {
79
+ return JSON.stringify({ ok: false, error: "no active sequence, and no sequenceName given" });
80
+ }
81
+ }
82
+
83
+ try {
84
+ ensureQEEnabled();
85
+ activateSequenceForQE(seq);
86
+ } catch (e2) {
87
+ return JSON.stringify({ ok: false, error: "app.enableQE()/sequence activation failed: " + e2.toString() });
88
+ }
89
+ if (typeof qe === "undefined" || !qe.project) {
90
+ return JSON.stringify({ ok: false, error: "QE DOM not available after enableQE()" });
91
+ }
92
+
93
+ var transitionQE = null;
94
+ if (transitionName !== null) {
95
+ try {
96
+ if (qe.project.getVideoTransitionByName) {
97
+ transitionQE = qe.project.getVideoTransitionByName(transitionName);
98
+ }
99
+ } catch (e3) {
100
+ transitionQE = null;
101
+ }
102
+ if (!transitionQE) {
103
+ return JSON.stringify({ ok: false, error: "transition not found: \"" + transitionName + "\" (try list-available-transitions)" });
104
+ }
105
+ }
106
+
107
+ var applyResult = ppbApplyTransitionToClip(seq, args.trackIndex, args.clipIndex, transitionQE, args.atEnd, durationSeconds);
108
+
109
+ var result = {
110
+ sequenceName: seq.name,
111
+ trackType: "video",
112
+ trackIndex: args.trackIndex,
113
+ clipIndex: args.clipIndex,
114
+ clipName: applyResult.clipName,
115
+ transitionName: transitionName,
116
+ atEnd: args.atEnd,
117
+ durationSeconds: durationSeconds,
118
+ previousTransitionCount: applyResult.previousCount,
119
+ newTransitionCount: applyResult.newCount,
120
+ addedTransitionName: applyResult.addedTransitionName,
121
+ verified: applyResult.verified,
122
+ attempts: applyResult.attempts,
123
+ succeededWithArgs: applyResult.succeededWithArgs
124
+ };
125
+
126
+ if (!applyResult.verified) {
127
+ return JSON.stringify({ ok: false, error: applyResult.error, result: result });
128
+ }
129
+
130
+ return JSON.stringify({ ok: true, result: result });
131
+ } catch (e) {
132
+ return JSON.stringify({ ok: false, error: e.toString() });
133
+ }
134
+ }
@@ -0,0 +1,132 @@
1
+ // Command: adjust-audio-levels → ppb_adjustAudioLevels
2
+ // Loaded lazily by ppb_dispatch (host/index.jsx) on first use, into the
3
+ // same global ExtendScript context — shared helpers (findSequenceByName,
4
+ // findVolumeLevelProperty, dbToLinearCalibrated, linearToDbCalibrated, ...)
5
+ // are already defined there.
6
+ //
7
+ // Standard-DOM mutation. Ported from leancoderkavy's premiere-pro-mcp
8
+ // `adjust_audio_levels` tool (audio.ts) — but note that reference handler
9
+ // actually SETS the Level property directly to the raw `level_db` argument
10
+ // (an absolute set, same bug as set_clip_volume's reference, and also not
11
+ // really "adjust" semantics despite the tool's name). This command instead
12
+ // implements true relative-adjustment semantics: read the clip's current
13
+ // linear Level, convert it to an estimated dB via the same calibration
14
+ // formula as set-clip-volume, add the requested `db` delta, convert back to
15
+ // linear, and apply. See set-clip-volume.jsx's header for the calibration-
16
+ // uncertainty caveat — it applies here identically, compounded because this
17
+ // command both reads AND writes through the same unverified formula.
18
+ function ppb_adjustAudioLevels(argsJson) {
19
+ try {
20
+ var args;
21
+ try {
22
+ args = JSON.parse(argsJson);
23
+ } catch (e) {
24
+ return JSON.stringify({ ok: false, error: "invalid args JSON: " + e.toString() });
25
+ }
26
+
27
+ if (args.trackType !== "video" && args.trackType !== "audio") {
28
+ return JSON.stringify({ ok: false, error: "trackType must be \"video\" or \"audio\"" });
29
+ }
30
+ if (typeof args.trackIndex !== "number" || args.trackIndex < 0 || Math.floor(args.trackIndex) !== args.trackIndex) {
31
+ return JSON.stringify({ ok: false, error: "trackIndex must be a non-negative integer" });
32
+ }
33
+ if (typeof args.clipIndex !== "number" || args.clipIndex < 0 || Math.floor(args.clipIndex) !== args.clipIndex) {
34
+ return JSON.stringify({ ok: false, error: "clipIndex must be a non-negative integer" });
35
+ }
36
+ if (typeof args.db !== "number" || isNaN(args.db)) {
37
+ return JSON.stringify({ ok: false, error: "db must be a number (a DELTA relative to the clip's current level)" });
38
+ }
39
+
40
+ if (!app.project) {
41
+ return JSON.stringify({ ok: false, error: "no project open" });
42
+ }
43
+
44
+ var seq = null;
45
+ if (args.sequenceName && typeof args.sequenceName === "string") {
46
+ seq = findSequenceByName(args.sequenceName);
47
+ if (!seq) {
48
+ return JSON.stringify({ ok: false, error: "no sequence named \"" + args.sequenceName + "\" is open" });
49
+ }
50
+ } else {
51
+ seq = app.project.activeSequence;
52
+ if (!seq) {
53
+ return JSON.stringify({ ok: false, error: "no active sequence, and no sequenceName given" });
54
+ }
55
+ }
56
+
57
+ var trackCollection = args.trackType === "video" ? seq.videoTracks : seq.audioTracks;
58
+ var numTracks = trackCollection.numTracks;
59
+ if (args.trackIndex >= numTracks) {
60
+ return JSON.stringify({
61
+ ok: false,
62
+ error: "trackIndex " + args.trackIndex + " is out of range — sequence has " + numTracks + " " + args.trackType + " track(s)"
63
+ });
64
+ }
65
+
66
+ var track = trackCollection[args.trackIndex];
67
+ var numClips = track.clips.numItems;
68
+ if (args.clipIndex >= numClips) {
69
+ return JSON.stringify({
70
+ ok: false,
71
+ error: "clipIndex " + args.clipIndex + " is out of range — track " + args.trackIndex + " has " + numClips + " clip(s)"
72
+ });
73
+ }
74
+
75
+ var clip = track.clips[args.clipIndex];
76
+ var clipName = null;
77
+ try { clipName = clip.name; } catch (e) { clipName = null; }
78
+
79
+ var levelProp = findVolumeLevelProperty(clip);
80
+ if (!levelProp) {
81
+ return JSON.stringify({ ok: false, error: "could not find Volume/Level property on clip — is this an audio clip?" });
82
+ }
83
+
84
+ var previousLinear = null;
85
+ try { previousLinear = levelProp.getValue(); } catch (e) { previousLinear = null; }
86
+ if (previousLinear === null) {
87
+ return JSON.stringify({ ok: false, error: "could not read the clip's current Level value" });
88
+ }
89
+
90
+ var previousDbEstimate = linearToDbCalibrated(previousLinear);
91
+ if (previousDbEstimate === null) {
92
+ return JSON.stringify({ ok: false, error: "current Level value (" + previousLinear + ") is not a valid positive amplitude — cannot estimate its dB" });
93
+ }
94
+
95
+ var requestedDbTotal = previousDbEstimate + args.db;
96
+ var requestedLinear = dbToLinearCalibrated(requestedDbTotal);
97
+
98
+ try {
99
+ levelProp.setValue(requestedLinear, true);
100
+ } catch (e) {
101
+ return JSON.stringify({ ok: false, error: "levelProp.setValue() failed: " + e.toString() });
102
+ }
103
+
104
+ var newLinear = null;
105
+ try { newLinear = levelProp.getValue(); } catch (e) { newLinear = null; }
106
+
107
+ var verified = newLinear !== null && Math.abs(newLinear - requestedLinear) < 0.0005;
108
+
109
+ var result = {
110
+ sequenceName: seq.name,
111
+ trackType: args.trackType,
112
+ trackIndex: args.trackIndex,
113
+ clipIndex: args.clipIndex,
114
+ clipName: clipName,
115
+ requestedDeltaDb: args.db,
116
+ previousValue: previousLinear,
117
+ previousDbEstimate: previousDbEstimate,
118
+ requestedValue: requestedLinear,
119
+ requestedDbTotal: requestedDbTotal,
120
+ newValue: newLinear,
121
+ verified: verified,
122
+ calibrationNote: "Level is LINEAR AMPLITUDE, not dB — this command estimates the clip's " +
123
+ "current dB from its linear value via hetpatel's UNVERIFIED calibration formula " +
124
+ "(linear = 10^((db-15)/20)), adds the requested delta, then converts back. Compounds " +
125
+ "the same calibration uncertainty documented in set-clip-volume.jsx."
126
+ };
127
+
128
+ return JSON.stringify({ ok: true, result: result });
129
+ } catch (e) {
130
+ return JSON.stringify({ ok: false, error: e.toString() });
131
+ }
132
+ }
@@ -0,0 +1,118 @@
1
+ // Command: apply-audio-effect → ppb_applyAudioEffect
2
+ // Loaded lazily by ppb_dispatch (host/index.jsx) on first use, into the
3
+ // same global ExtendScript context — shared helpers (findSequenceByName,
4
+ // ensureQEEnabled, activateSequenceForQE, resolveTimelineClip,
5
+ // resolveQeClip, serializeClipComponents, findQeAudioEffectByName, ...) are
6
+ // already defined there.
7
+ //
8
+ // Audio counterpart of apply-effect: qe.project.getAudioEffectByName() (or a
9
+ // getAudioEffectList() scan fallback) + qeClip.addAudioEffect(fx). Same
10
+ // verification approach — clip.components.numItems increasing by exactly 1,
11
+ // never trusting addAudioEffect()'s own return value.
12
+ function ppb_applyAudioEffect(argsJson) {
13
+ try {
14
+ var args;
15
+ try {
16
+ args = JSON.parse(argsJson);
17
+ } catch (e) {
18
+ return JSON.stringify({ ok: false, error: "invalid args JSON: " + e.toString() });
19
+ }
20
+
21
+ if (args.trackType !== "audio") {
22
+ return JSON.stringify({ ok: false, error: "trackType must be \"audio\" — apply-audio-effect applies an AUDIO effect (use apply-effect for video clips)" });
23
+ }
24
+ if (typeof args.trackIndex !== "number" || args.trackIndex < 0 || Math.floor(args.trackIndex) !== args.trackIndex) {
25
+ return JSON.stringify({ ok: false, error: "trackIndex must be a non-negative integer" });
26
+ }
27
+ if (typeof args.clipIndex !== "number" || args.clipIndex < 0 || Math.floor(args.clipIndex) !== args.clipIndex) {
28
+ return JSON.stringify({ ok: false, error: "clipIndex must be a non-negative integer" });
29
+ }
30
+ if (typeof args.effectName !== "string" || !args.effectName) {
31
+ return JSON.stringify({ ok: false, error: "effectName must be a non-empty string" });
32
+ }
33
+
34
+ if (!app.project) {
35
+ return JSON.stringify({ ok: false, error: "no project open" });
36
+ }
37
+
38
+ var seq = null;
39
+ if (args.sequenceName && typeof args.sequenceName === "string") {
40
+ seq = findSequenceByName(args.sequenceName);
41
+ if (!seq) {
42
+ return JSON.stringify({ ok: false, error: "no sequence named \"" + args.sequenceName + "\" is open" });
43
+ }
44
+ } else {
45
+ seq = app.project.activeSequence;
46
+ if (!seq) {
47
+ return JSON.stringify({ ok: false, error: "no active sequence, and no sequenceName given" });
48
+ }
49
+ }
50
+
51
+ var resolvedClip = resolveTimelineClip(seq, "audio", args.trackIndex, args.clipIndex);
52
+ if (resolvedClip.error) {
53
+ return JSON.stringify({ ok: false, error: resolvedClip.error });
54
+ }
55
+ var clip = resolvedClip.clip;
56
+ var clipName = null;
57
+ try { clipName = clip.name; } catch (e) { clipName = null; }
58
+
59
+ var previousComponents = serializeClipComponents(clip);
60
+
61
+ try {
62
+ ensureQEEnabled();
63
+ activateSequenceForQE(seq);
64
+ } catch (e) {
65
+ return JSON.stringify({ ok: false, error: "app.enableQE()/sequence activation failed: " + e.toString() });
66
+ }
67
+ if (typeof qe === "undefined" || !qe.project) {
68
+ return JSON.stringify({ ok: false, error: "QE DOM not available after enableQE()" });
69
+ }
70
+
71
+ var qeSeq;
72
+ try {
73
+ qeSeq = qe.project.getActiveSequence();
74
+ } catch (e) {
75
+ return JSON.stringify({ ok: false, error: "qe.project.getActiveSequence() failed: " + e.toString() });
76
+ }
77
+
78
+ var qeResolved = resolveQeClip(qeSeq, "audio", args.trackIndex, args.clipIndex);
79
+ if (qeResolved.error) {
80
+ return JSON.stringify({ ok: false, error: qeResolved.error });
81
+ }
82
+
83
+ var fx = findQeAudioEffectByName(args.effectName);
84
+ if (!fx) {
85
+ return JSON.stringify({ ok: false, error: "audio effect not found in this Premiere install's QE effect catalog: \"" + args.effectName + "\"" });
86
+ }
87
+
88
+ try {
89
+ qeResolved.qeClip.addAudioEffect(fx);
90
+ } catch (e) {
91
+ return JSON.stringify({ ok: false, error: "qeClip.addAudioEffect() failed: " + e.toString() });
92
+ }
93
+
94
+ var newComponents = serializeClipComponents(clip);
95
+ var verified = newComponents.length === previousComponents.length + 1;
96
+ var newComponent = verified ? newComponents[newComponents.length - 1] : null;
97
+
98
+ return JSON.stringify({
99
+ ok: true,
100
+ result: {
101
+ sequenceName: seq.name,
102
+ trackType: "audio",
103
+ trackIndex: args.trackIndex,
104
+ clipIndex: args.clipIndex,
105
+ clipName: clipName,
106
+ effectName: args.effectName,
107
+ verified: verified,
108
+ newComponent: newComponent,
109
+ previousComponentCount: previousComponents.length,
110
+ newComponentCount: newComponents.length,
111
+ previousComponents: previousComponents,
112
+ newComponents: newComponents
113
+ }
114
+ });
115
+ } catch (e) {
116
+ return JSON.stringify({ ok: false, error: e.toString() });
117
+ }
118
+ }
@@ -0,0 +1,122 @@
1
+ // Command: apply-effect → ppb_applyEffect
2
+ // Loaded lazily by ppb_dispatch (host/index.jsx) on first use, into the
3
+ // same global ExtendScript context — shared helpers (findSequenceByName,
4
+ // ensureQEEnabled, activateSequenceForQE, resolveTimelineClip,
5
+ // resolveQeClip, serializeClipComponents, findQeVideoEffectByName, ...) are
6
+ // already defined there.
7
+ //
8
+ // Applies a video effect to a clip via the "universal dance" documented in
9
+ // PREMIERE_API_NOTES.md's "Effects, transitions, keyframes" section:
10
+ // look up the effect in the QE catalog (getVideoEffectByName, falling back
11
+ // to a getVideoEffectList() scan), call qeClip.addVideoEffect(fx), then read
12
+ // the standard-DOM clip.components back — the new component is appended
13
+ // LAST. Mutation is verified by clip.components.numItems increasing by
14
+ // exactly 1, not by trusting addVideoEffect()'s return value (undocumented
15
+ // on this build, like every other QE mutation call here).
16
+ function ppb_applyEffect(argsJson) {
17
+ try {
18
+ var args;
19
+ try {
20
+ args = JSON.parse(argsJson);
21
+ } catch (e) {
22
+ return JSON.stringify({ ok: false, error: "invalid args JSON: " + e.toString() });
23
+ }
24
+
25
+ if (args.trackType !== "video") {
26
+ return JSON.stringify({ ok: false, error: "trackType must be \"video\" — apply-effect applies a VIDEO effect (use apply-audio-effect for audio clips)" });
27
+ }
28
+ if (typeof args.trackIndex !== "number" || args.trackIndex < 0 || Math.floor(args.trackIndex) !== args.trackIndex) {
29
+ return JSON.stringify({ ok: false, error: "trackIndex must be a non-negative integer" });
30
+ }
31
+ if (typeof args.clipIndex !== "number" || args.clipIndex < 0 || Math.floor(args.clipIndex) !== args.clipIndex) {
32
+ return JSON.stringify({ ok: false, error: "clipIndex must be a non-negative integer" });
33
+ }
34
+ if (typeof args.effectName !== "string" || !args.effectName) {
35
+ return JSON.stringify({ ok: false, error: "effectName must be a non-empty string" });
36
+ }
37
+
38
+ if (!app.project) {
39
+ return JSON.stringify({ ok: false, error: "no project open" });
40
+ }
41
+
42
+ var seq = null;
43
+ if (args.sequenceName && typeof args.sequenceName === "string") {
44
+ seq = findSequenceByName(args.sequenceName);
45
+ if (!seq) {
46
+ return JSON.stringify({ ok: false, error: "no sequence named \"" + args.sequenceName + "\" is open" });
47
+ }
48
+ } else {
49
+ seq = app.project.activeSequence;
50
+ if (!seq) {
51
+ return JSON.stringify({ ok: false, error: "no active sequence, and no sequenceName given" });
52
+ }
53
+ }
54
+
55
+ var resolvedClip = resolveTimelineClip(seq, "video", args.trackIndex, args.clipIndex);
56
+ if (resolvedClip.error) {
57
+ return JSON.stringify({ ok: false, error: resolvedClip.error });
58
+ }
59
+ var clip = resolvedClip.clip;
60
+ var clipName = null;
61
+ try { clipName = clip.name; } catch (e) { clipName = null; }
62
+
63
+ var previousComponents = serializeClipComponents(clip);
64
+
65
+ try {
66
+ ensureQEEnabled();
67
+ activateSequenceForQE(seq);
68
+ } catch (e) {
69
+ return JSON.stringify({ ok: false, error: "app.enableQE()/sequence activation failed: " + e.toString() });
70
+ }
71
+ if (typeof qe === "undefined" || !qe.project) {
72
+ return JSON.stringify({ ok: false, error: "QE DOM not available after enableQE()" });
73
+ }
74
+
75
+ var qeSeq;
76
+ try {
77
+ qeSeq = qe.project.getActiveSequence();
78
+ } catch (e) {
79
+ return JSON.stringify({ ok: false, error: "qe.project.getActiveSequence() failed: " + e.toString() });
80
+ }
81
+
82
+ var qeResolved = resolveQeClip(qeSeq, "video", args.trackIndex, args.clipIndex);
83
+ if (qeResolved.error) {
84
+ return JSON.stringify({ ok: false, error: qeResolved.error });
85
+ }
86
+
87
+ var fx = findQeVideoEffectByName(args.effectName);
88
+ if (!fx) {
89
+ return JSON.stringify({ ok: false, error: "video effect not found in this Premiere install's QE effect catalog: \"" + args.effectName + "\"" });
90
+ }
91
+
92
+ try {
93
+ qeResolved.qeClip.addVideoEffect(fx);
94
+ } catch (e) {
95
+ return JSON.stringify({ ok: false, error: "qeClip.addVideoEffect() failed: " + e.toString() });
96
+ }
97
+
98
+ var newComponents = serializeClipComponents(clip);
99
+ var verified = newComponents.length === previousComponents.length + 1;
100
+ var newComponent = verified ? newComponents[newComponents.length - 1] : null;
101
+
102
+ return JSON.stringify({
103
+ ok: true,
104
+ result: {
105
+ sequenceName: seq.name,
106
+ trackType: "video",
107
+ trackIndex: args.trackIndex,
108
+ clipIndex: args.clipIndex,
109
+ clipName: clipName,
110
+ effectName: args.effectName,
111
+ verified: verified,
112
+ newComponent: newComponent,
113
+ previousComponentCount: previousComponents.length,
114
+ newComponentCount: newComponents.length,
115
+ previousComponents: previousComponents,
116
+ newComponents: newComponents
117
+ }
118
+ });
119
+ } catch (e) {
120
+ return JSON.stringify({ ok: false, error: e.toString() });
121
+ }
122
+ }
@@ -0,0 +1,107 @@
1
+ // Command: apply-lut → ppb_applyLut
2
+ // Loaded lazily by ppb_dispatch (host/index.jsx) on first use, into the
3
+ // same global ExtendScript context — shared helpers (findSequenceByName,
4
+ // resolveTimelineClip, serializeClipComponents,
5
+ // ensureLumetriColorComponent, findComponentProperty, ...) are already
6
+ // defined there.
7
+ //
8
+ // Ensures Lumetri Color is applied (adding it if missing, same helper
9
+ // color-correct uses), then sets its "Input LUT" property to a path string
10
+ // per PREMIERE_API_NOTES.md. This is a plain string setValue — no numeric
11
+ // falsy-check concern here, but the same "0 is legit" caveat from
12
+ // color-correct doesn't apply since a LUT path is never numeric.
13
+ function ppb_applyLut(argsJson) {
14
+ try {
15
+ var args;
16
+ try {
17
+ args = JSON.parse(argsJson);
18
+ } catch (e) {
19
+ return JSON.stringify({ ok: false, error: "invalid args JSON: " + e.toString() });
20
+ }
21
+
22
+ if (args.trackType !== "video") {
23
+ return JSON.stringify({ ok: false, error: "trackType must be \"video\" — Lumetri Color is a video effect" });
24
+ }
25
+ if (typeof args.trackIndex !== "number" || args.trackIndex < 0 || Math.floor(args.trackIndex) !== args.trackIndex) {
26
+ return JSON.stringify({ ok: false, error: "trackIndex must be a non-negative integer" });
27
+ }
28
+ if (typeof args.clipIndex !== "number" || args.clipIndex < 0 || Math.floor(args.clipIndex) !== args.clipIndex) {
29
+ return JSON.stringify({ ok: false, error: "clipIndex must be a non-negative integer" });
30
+ }
31
+ if (typeof args.lutPath !== "string" || !args.lutPath) {
32
+ return JSON.stringify({ ok: false, error: "lutPath must be a non-empty string" });
33
+ }
34
+
35
+ if (!app.project) {
36
+ return JSON.stringify({ ok: false, error: "no project open" });
37
+ }
38
+
39
+ var seq = null;
40
+ if (args.sequenceName && typeof args.sequenceName === "string") {
41
+ seq = findSequenceByName(args.sequenceName);
42
+ if (!seq) {
43
+ return JSON.stringify({ ok: false, error: "no sequence named \"" + args.sequenceName + "\" is open" });
44
+ }
45
+ } else {
46
+ seq = app.project.activeSequence;
47
+ if (!seq) {
48
+ return JSON.stringify({ ok: false, error: "no active sequence, and no sequenceName given" });
49
+ }
50
+ }
51
+
52
+ var resolvedClip = resolveTimelineClip(seq, "video", args.trackIndex, args.clipIndex);
53
+ if (resolvedClip.error) {
54
+ return JSON.stringify({ ok: false, error: resolvedClip.error });
55
+ }
56
+ var clip = resolvedClip.clip;
57
+ var clipName = null;
58
+ try { clipName = clip.name; } catch (e) { clipName = null; }
59
+
60
+ var previousComponents = serializeClipComponents(clip);
61
+
62
+ var lumetri = ensureLumetriColorComponent(seq, "video", args.trackIndex, args.clipIndex, clip);
63
+ if (lumetri.error) {
64
+ return JSON.stringify({ ok: false, error: lumetri.error });
65
+ }
66
+ var lumetriComp = lumetri.component;
67
+
68
+ var prop = findComponentProperty(lumetriComp, ["Input LUT"]);
69
+ if (!prop) {
70
+ return JSON.stringify({ ok: false, error: "could not find an \"Input LUT\" property on Lumetri Color" });
71
+ }
72
+
73
+ var previousValue = null;
74
+ try { previousValue = prop.getValue(); } catch (e) { previousValue = null; }
75
+
76
+ try {
77
+ prop.setValue(args.lutPath, true);
78
+ } catch (e) {
79
+ return JSON.stringify({ ok: false, error: "prop.setValue(lutPath) failed: " + e.toString() });
80
+ }
81
+
82
+ var newValue = null;
83
+ try { newValue = prop.getValue(); } catch (e) { newValue = null; }
84
+
85
+ var newComponents = serializeClipComponents(clip);
86
+
87
+ return JSON.stringify({
88
+ ok: true,
89
+ result: {
90
+ sequenceName: seq.name,
91
+ trackType: "video",
92
+ trackIndex: args.trackIndex,
93
+ clipIndex: args.clipIndex,
94
+ clipName: clipName,
95
+ lumetriApplied: lumetri.applied,
96
+ previousValue: previousValue,
97
+ requestedValue: args.lutPath,
98
+ newValue: newValue,
99
+ verified: newValue === args.lutPath,
100
+ previousComponents: previousComponents,
101
+ newComponents: newComponents
102
+ }
103
+ });
104
+ } catch (e) {
105
+ return JSON.stringify({ ok: false, error: e.toString() });
106
+ }
107
+ }
@@ -0,0 +1,66 @@
1
+ // Command: attach-custom-property → ppb_attachCustomProperty
2
+ // Loaded lazily by ppb_dispatch (host/index.jsx) on first use, into the
3
+ // same global ExtendScript context — shared helpers (findSequenceByName,
4
+ // ...) are already defined there.
5
+ //
6
+ // Ported from leancoderkavy/premiere-pro-mcp's sequence.ts
7
+ // attach_custom_property tool: `seq.attachCustomProperty(id, value)` per
8
+ // PREMIERE_API_NOTES.md's Sequences table. No confirmed getter/read-back
9
+ // API exists for custom sequence properties in ANY of the reference
10
+ // repos studied — this command can only confirm the setter call didn't
11
+ // throw, never that the value actually persisted or is retrievable
12
+ // later. value is coerced to a string (attachCustomProperty's own
13
+ // signature is key/value strings per the reference tool).
14
+ function ppb_attachCustomProperty(argsJson) {
15
+ try {
16
+ var args;
17
+ try {
18
+ args = JSON.parse(argsJson);
19
+ } catch (e) {
20
+ return JSON.stringify({ ok: false, error: "invalid args JSON: " + e.toString() });
21
+ }
22
+
23
+ if (!app.project) {
24
+ return JSON.stringify({ ok: false, error: "no project open" });
25
+ }
26
+ if (!args.propertyId || typeof args.propertyId !== "string") {
27
+ return JSON.stringify({ ok: false, error: "propertyId is required" });
28
+ }
29
+ if (typeof args.value === "undefined" || args.value === null) {
30
+ return JSON.stringify({ ok: false, error: "value is required" });
31
+ }
32
+
33
+ var seq;
34
+ if (args.sequenceName && typeof args.sequenceName === "string") {
35
+ seq = findSequenceByName(args.sequenceName);
36
+ if (!seq) {
37
+ return JSON.stringify({ ok: false, error: "no sequence named \"" + args.sequenceName + "\" is open" });
38
+ }
39
+ } else {
40
+ seq = app.project.activeSequence;
41
+ if (!seq) {
42
+ return JSON.stringify({ ok: false, error: "no active sequence, and no sequenceName given" });
43
+ }
44
+ }
45
+
46
+ var valueString = String(args.value);
47
+
48
+ try {
49
+ seq.attachCustomProperty(args.propertyId, valueString);
50
+ } catch (e) {
51
+ return JSON.stringify({ ok: false, error: "seq.attachCustomProperty() failed: " + e.toString() });
52
+ }
53
+
54
+ return JSON.stringify({
55
+ ok: true,
56
+ result: {
57
+ sequenceName: seq.name,
58
+ propertyId: args.propertyId,
59
+ value: valueString,
60
+ note: "no confirmed getter exists for custom sequence properties across any reference repo studied — this result confirms only that attachCustomProperty() did not throw, not that the value actually persisted or is retrievable"
61
+ }
62
+ });
63
+ } catch (e) {
64
+ return JSON.stringify({ ok: false, error: e.toString() });
65
+ }
66
+ }