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: set-item-metadata → ppb_setItemMetadata
2
+ // Loaded lazily by ppb_dispatch (host/index.jsx) on first use, into the
3
+ // same global ExtendScript context.
4
+ //
5
+ // Project-item addressing: at least one of nodeId/name is required (see
6
+ // get-item-metadata.jsx for the shared design note — the resolver is
7
+ // duplicated per-file since command files load independently).
8
+ //
9
+ // WRITE command — this is the write-side counterpart of get-item-metadata.
10
+ // setProjectMetadata(value, [fieldPath]) replaces the value at one field
11
+ // path (e.g. "Column.Intrinsic.Description"); it does NOT touch other
12
+ // fields. Undo is NON-FUNCTIONAL on this build — `previousValue` (the
13
+ // full metadata blob read before mutating) is the only restoration path
14
+ // if a caller needs to revert.
15
+
16
+ function ppbFindItemSetItemMetadata_walk(item, args, depth) {
17
+ if (depth > 32) {
18
+ return null;
19
+ }
20
+
21
+ var isBin = false;
22
+ try {
23
+ isBin = typeof ProjectItemType !== "undefined" && item.type === ProjectItemType.BIN;
24
+ } catch (e) {
25
+ isBin = false;
26
+ }
27
+
28
+ var matched = false;
29
+ if (args.nodeId !== null) {
30
+ try { matched = item.nodeId === args.nodeId; } catch (e) { matched = false; }
31
+ } else if (args.name !== null) {
32
+ try { matched = item.name === args.name; } catch (e) { matched = false; }
33
+ }
34
+ if (matched) {
35
+ return item;
36
+ }
37
+
38
+ if (isBin && item.children) {
39
+ for (var i = 0; i < item.children.numItems; i++) {
40
+ var found = ppbFindItemSetItemMetadata_walk(item.children[i], args, depth + 1);
41
+ if (found) {
42
+ return found;
43
+ }
44
+ }
45
+ }
46
+ return null;
47
+ }
48
+
49
+ function ppbFindItemSetItemMetadata_resolve(args) {
50
+ var root = app.project.rootItem;
51
+ for (var i = 0; i < root.children.numItems; i++) {
52
+ var found = ppbFindItemSetItemMetadata_walk(root.children[i], args, 1);
53
+ if (found) {
54
+ return found;
55
+ }
56
+ }
57
+ return null;
58
+ }
59
+
60
+ function ppb_setItemMetadata(argsJson) {
61
+ try {
62
+ var args;
63
+ try {
64
+ args = JSON.parse(argsJson);
65
+ } catch (e) {
66
+ return JSON.stringify({ ok: false, error: "invalid args JSON: " + e.toString() });
67
+ }
68
+
69
+ var hasNodeId = typeof args.nodeId === "string" && args.nodeId.length > 0;
70
+ var hasName = typeof args.name === "string" && args.name.length > 0;
71
+ if (!hasNodeId && !hasName) {
72
+ return JSON.stringify({ ok: false, error: "either nodeId or name is required" });
73
+ }
74
+
75
+ var hasFieldPath = typeof args.fieldPath === "string" && args.fieldPath.length > 0;
76
+ if (!hasFieldPath) {
77
+ return JSON.stringify({ ok: false, error: "fieldPath is required" });
78
+ }
79
+ if (typeof args.value !== "string") {
80
+ return JSON.stringify({ ok: false, error: "value (string) is required" });
81
+ }
82
+
83
+ if (!app.project) {
84
+ return JSON.stringify({ ok: false, error: "no project open" });
85
+ }
86
+
87
+ var item = ppbFindItemSetItemMetadata_resolve({
88
+ nodeId: hasNodeId ? args.nodeId : null,
89
+ name: hasNodeId ? null : args.name
90
+ });
91
+ if (!item) {
92
+ return JSON.stringify({ ok: false, error: "no project item found matching the given nodeId/name" });
93
+ }
94
+
95
+ if (typeof item.setProjectMetadata !== "function") {
96
+ return JSON.stringify({ ok: false, error: "setProjectMetadata is not available on this Premiere build" });
97
+ }
98
+
99
+ var previousValue = null;
100
+ try { previousValue = item.getProjectMetadata(); } catch (e) { previousValue = null; }
101
+
102
+ try {
103
+ item.setProjectMetadata(args.value, [args.fieldPath]);
104
+ } catch (e) {
105
+ return JSON.stringify({ ok: false, error: "setProjectMetadata() failed: " + e.toString() });
106
+ }
107
+
108
+ var newValue = null;
109
+ try { newValue = item.getProjectMetadata(); } catch (e) { newValue = null; }
110
+
111
+ var verified = false;
112
+ try {
113
+ verified = typeof newValue === "string" && newValue.indexOf(args.value) !== -1;
114
+ } catch (e) {
115
+ verified = false;
116
+ }
117
+
118
+ var result = {
119
+ name: null,
120
+ nodeId: null,
121
+ fieldPath: args.fieldPath,
122
+ requestedValue: args.value,
123
+ previousValue: previousValue,
124
+ newValue: newValue,
125
+ verified: verified
126
+ };
127
+ try { result.name = item.name; } catch (e) { result.name = null; }
128
+ try { result.nodeId = item.nodeId; } catch (e) { result.nodeId = null; }
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,109 @@
1
+ // Command: set-item-offline → ppb_setItemOffline
2
+ // Loaded lazily by ppb_dispatch (host/index.jsx) on first use, into the
3
+ // same global ExtendScript context — shared helpers (TICKS_PER_SECOND, ...)
4
+ // are already defined there.
5
+ //
6
+ // Ports the reference project's media.ts set_offline tool. Item resolution
7
+ // by nodeId/name uses children-presence recursion (NOT an isBin() gate),
8
+ // per get-project-item-info.jsx's live-debugged finding. **Destructive-ish**:
9
+ // unlinks the item's media (item.setOffline()); undo is non-functional on
10
+ // this build, so there is no scripted path back — relink-media is the
11
+ // manual recovery path once a new/original file path is known. Verified
12
+ // via an isOffline() read-back.
13
+
14
+ function ppbSetItemOffline_findByNodeId(item, nodeId, depth) {
15
+ if (depth > 32) {
16
+ return null;
17
+ }
18
+ try {
19
+ if (item.nodeId === nodeId) {
20
+ return item;
21
+ }
22
+ } catch (e) {
23
+ // fall through
24
+ }
25
+ if (item.children && item.children.numItems > 0) {
26
+ for (var i = 0; i < item.children.numItems; i++) {
27
+ var found = ppbSetItemOffline_findByNodeId(item.children[i], nodeId, depth + 1);
28
+ if (found) {
29
+ return found;
30
+ }
31
+ }
32
+ }
33
+ return null;
34
+ }
35
+
36
+ function ppbSetItemOffline_findByName(item, name, depth) {
37
+ if (depth > 32) {
38
+ return null;
39
+ }
40
+ try {
41
+ if (item.name === name) {
42
+ return item;
43
+ }
44
+ } catch (e) {
45
+ // fall through
46
+ }
47
+ if (item.children && item.children.numItems > 0) {
48
+ for (var i = 0; i < item.children.numItems; i++) {
49
+ var found = ppbSetItemOffline_findByName(item.children[i], name, depth + 1);
50
+ if (found) {
51
+ return found;
52
+ }
53
+ }
54
+ }
55
+ return null;
56
+ }
57
+
58
+ function ppb_setItemOffline(argsJson) {
59
+ try {
60
+ var args;
61
+ try {
62
+ args = JSON.parse(argsJson);
63
+ } catch (e) {
64
+ return JSON.stringify({ ok: false, error: "invalid args JSON: " + e.toString() });
65
+ }
66
+
67
+ var hasNodeId = typeof args.nodeId === "string" && args.nodeId.length > 0;
68
+ var hasName = typeof args.name === "string" && args.name.length > 0;
69
+ if (!hasNodeId && !hasName) {
70
+ return JSON.stringify({ ok: false, error: "either nodeId or name is required" });
71
+ }
72
+
73
+ if (!app.project) {
74
+ return JSON.stringify({ ok: false, error: "no project open" });
75
+ }
76
+
77
+ var item = hasNodeId
78
+ ? ppbSetItemOffline_findByNodeId(app.project.rootItem, args.nodeId, 0)
79
+ : ppbSetItemOffline_findByName(app.project.rootItem, args.name, 0);
80
+ if (!item) {
81
+ return JSON.stringify({ ok: false, error: "no project item found matching the given nodeId/name" });
82
+ }
83
+
84
+ var previousValue = null;
85
+ try { previousValue = item.isOffline(); } catch (e) { previousValue = null; }
86
+
87
+ try {
88
+ item.setOffline();
89
+ } catch (e) {
90
+ return JSON.stringify({ ok: false, error: "setOffline() failed: " + e.toString() });
91
+ }
92
+
93
+ var newValue = null;
94
+ try { newValue = item.isOffline(); } catch (e) { newValue = null; }
95
+
96
+ return JSON.stringify({
97
+ ok: true,
98
+ result: {
99
+ nodeId: item.nodeId,
100
+ name: item.name,
101
+ previousValue: previousValue,
102
+ newValue: newValue,
103
+ verified: newValue === true
104
+ }
105
+ });
106
+ } catch (e) {
107
+ return JSON.stringify({ ok: false, error: e.toString() });
108
+ }
109
+ }
@@ -0,0 +1,139 @@
1
+ // Command: set-item-start-time → ppb_setItemStartTime
2
+ // Loaded lazily by ppb_dispatch (host/index.jsx) on first use, into the
3
+ // same global ExtendScript context — shared helpers (TICKS_PER_SECOND, ...)
4
+ // are already defined there.
5
+ //
6
+ // Ports the reference project's media.ts/track-targeting.ts set_start_time
7
+ // tool (setStartTime — timecode offset for the source media, not a
8
+ // timeline position). Item resolution by nodeId/name uses children-
9
+ // presence recursion (NOT an isBin() gate), per get-project-item-info.jsx's
10
+ // live-debugged finding. Converts seconds to a ticks string (per
11
+ // PREMIERE_API_NOTES.md's item.setStartTime(ticksStr) signature) before
12
+ // calling. No confirmed getter exists for a project item's start time on
13
+ // this build, so a short list of plausible getter names is probed for
14
+ // read-back verification — same defensive pattern as set-poster-frame.
15
+
16
+ function ppbSetItemStartTime_findByNodeId(item, nodeId, depth) {
17
+ if (depth > 32) {
18
+ return null;
19
+ }
20
+ try {
21
+ if (item.nodeId === nodeId) {
22
+ return item;
23
+ }
24
+ } catch (e) {
25
+ // fall through
26
+ }
27
+ if (item.children && item.children.numItems > 0) {
28
+ for (var i = 0; i < item.children.numItems; i++) {
29
+ var found = ppbSetItemStartTime_findByNodeId(item.children[i], nodeId, depth + 1);
30
+ if (found) {
31
+ return found;
32
+ }
33
+ }
34
+ }
35
+ return null;
36
+ }
37
+
38
+ function ppbSetItemStartTime_findByName(item, name, depth) {
39
+ if (depth > 32) {
40
+ return null;
41
+ }
42
+ try {
43
+ if (item.name === name) {
44
+ return item;
45
+ }
46
+ } catch (e) {
47
+ // fall through
48
+ }
49
+ if (item.children && item.children.numItems > 0) {
50
+ for (var i = 0; i < item.children.numItems; i++) {
51
+ var found = ppbSetItemStartTime_findByName(item.children[i], name, depth + 1);
52
+ if (found) {
53
+ return found;
54
+ }
55
+ }
56
+ }
57
+ return null;
58
+ }
59
+
60
+ // Probes a short list of plausible getter names/shapes for a project
61
+ // item's start time — none is documented in PREMIERE_API_NOTES.md, unlike
62
+ // the well-confirmed getInPoint()/getOutPoint() pair. Returns seconds, or
63
+ // null if none of the probed getters exist/succeed.
64
+ function ppbSetItemStartTime_probeGetter(item) {
65
+ var candidates = ["getStartTime", "getStartOffset"];
66
+ for (var i = 0; i < candidates.length; i++) {
67
+ try {
68
+ if (typeof item[candidates[i]] === "function") {
69
+ var raw = item[candidates[i]]();
70
+ if (raw !== null && typeof raw !== "undefined") {
71
+ return { name: candidates[i], seconds: timeValueToSeconds(raw) };
72
+ }
73
+ }
74
+ } catch (e) {
75
+ // try next candidate
76
+ }
77
+ }
78
+ return null;
79
+ }
80
+
81
+ function ppb_setItemStartTime(argsJson) {
82
+ try {
83
+ var args;
84
+ try {
85
+ args = JSON.parse(argsJson);
86
+ } catch (e) {
87
+ return JSON.stringify({ ok: false, error: "invalid args JSON: " + e.toString() });
88
+ }
89
+
90
+ var hasNodeId = typeof args.nodeId === "string" && args.nodeId.length > 0;
91
+ var hasName = typeof args.name === "string" && args.name.length > 0;
92
+ if (!hasNodeId && !hasName) {
93
+ return JSON.stringify({ ok: false, error: "either nodeId or name is required" });
94
+ }
95
+ if (typeof args.seconds !== "number") {
96
+ return JSON.stringify({ ok: false, error: "seconds is required and must be a number" });
97
+ }
98
+
99
+ if (!app.project) {
100
+ return JSON.stringify({ ok: false, error: "no project open" });
101
+ }
102
+
103
+ var item = hasNodeId
104
+ ? ppbSetItemStartTime_findByNodeId(app.project.rootItem, args.nodeId, 0)
105
+ : ppbSetItemStartTime_findByName(app.project.rootItem, args.name, 0);
106
+ if (!item) {
107
+ return JSON.stringify({ ok: false, error: "no project item found matching the given nodeId/name" });
108
+ }
109
+
110
+ var before = ppbSetItemStartTime_probeGetter(item);
111
+ var ticksStr = String(Math.round(args.seconds * TICKS_PER_SECOND));
112
+
113
+ try {
114
+ item.setStartTime(ticksStr);
115
+ } catch (e) {
116
+ return JSON.stringify({ ok: false, error: "setStartTime() failed: " + e.toString() });
117
+ }
118
+
119
+ var after = ppbSetItemStartTime_probeGetter(item);
120
+
121
+ return JSON.stringify({
122
+ ok: true,
123
+ result: {
124
+ nodeId: item.nodeId,
125
+ name: item.name,
126
+ requestedSeconds: args.seconds,
127
+ ticksString: ticksStr,
128
+ previousValue: before ? before.seconds : null,
129
+ newValue: after ? after.seconds : null,
130
+ readBackGetterUsed: after ? after.name : null,
131
+ note: after
132
+ ? null
133
+ : "no confirmed getter for a project item's start time exists on this build — only the setter's non-throw result is confirmed"
134
+ }
135
+ });
136
+ } catch (e) {
137
+ return JSON.stringify({ ok: false, error: e.toString() });
138
+ }
139
+ }
@@ -0,0 +1,186 @@
1
+ // Command: set-keyframe-interpolation → ppb_setKeyframeInterpolation
2
+ // Loaded lazily by ppb_dispatch (host/index.jsx) on first use, into the
3
+ // same global ExtendScript context — shared helpers (findSequenceByName,
4
+ // findClipComponent, findComponentProperty, timeValueToSeconds,
5
+ // keyTimeToSeconds, tryTimeForms, getSequenceFps, ...) are already defined
6
+ // there.
7
+ //
8
+ // Standard-DOM mutation. Ported from leancoderkavy's premiere-pro-mcp
9
+ // `set_keyframe_interpolation` tool (keyframes.ts), addressed here by
10
+ // trackType/trackIndex/clipIndex + componentName/propertyName. `seconds`
11
+ // is clip-relative, offset by clip.start to sequence time (same half-frame
12
+ // tolerance existing-key lookup as remove-keyframe).
13
+ //
14
+ // interpolationType IS A RAW INT, and its enum meaning is DISPUTED across
15
+ // Premiere builds per PREMIERE_API_NOTES.md's Property API block:
16
+ // leancoderkavy uses 0=Linear, 4=Hold, 5=Bezier; ayushozha's own docs claim
17
+ // a plain 0/1/2 map instead. This command does NOT translate a name to an
18
+ // int — the caller passes the raw int and gets it back unmodified; treat
19
+ // the actual on-screen result as the source of truth until verified live.
20
+ // There is no confirmed getter to read a keyframe's current interpolation
21
+ // back — `verified` here only means the setInterpolationTypeAtKey() call
22
+ // itself did not throw, NOT that Premiere applied the requested type.
23
+ function ppb_setKeyframeInterpolation(argsJson) {
24
+ try {
25
+ var args;
26
+ try {
27
+ args = JSON.parse(argsJson);
28
+ } catch (e) {
29
+ return JSON.stringify({ ok: false, error: "invalid args JSON: " + e.toString() });
30
+ }
31
+
32
+ if (args.trackType !== "video" && args.trackType !== "audio") {
33
+ return JSON.stringify({ ok: false, error: "trackType must be \"video\" or \"audio\"" });
34
+ }
35
+ if (typeof args.trackIndex !== "number" || args.trackIndex < 0 || Math.floor(args.trackIndex) !== args.trackIndex) {
36
+ return JSON.stringify({ ok: false, error: "trackIndex must be a non-negative integer" });
37
+ }
38
+ if (typeof args.clipIndex !== "number" || args.clipIndex < 0 || Math.floor(args.clipIndex) !== args.clipIndex) {
39
+ return JSON.stringify({ ok: false, error: "clipIndex must be a non-negative integer" });
40
+ }
41
+ if (typeof args.componentName !== "string" || !args.componentName) {
42
+ return JSON.stringify({ ok: false, error: "componentName is required" });
43
+ }
44
+ if (typeof args.propertyName !== "string" || !args.propertyName) {
45
+ return JSON.stringify({ ok: false, error: "propertyName is required" });
46
+ }
47
+ if (typeof args.seconds !== "number") {
48
+ return JSON.stringify({ ok: false, error: "seconds must be a number (clip-relative)" });
49
+ }
50
+ if (typeof args.interpolationType !== "number" || Math.floor(args.interpolationType) !== args.interpolationType) {
51
+ return JSON.stringify({ ok: false, error: "interpolationType must be an integer (meaning is version-dependent — see docs)" });
52
+ }
53
+
54
+ if (!app.project) {
55
+ return JSON.stringify({ ok: false, error: "no project open" });
56
+ }
57
+
58
+ var seq = null;
59
+ if (args.sequenceName && typeof args.sequenceName === "string") {
60
+ seq = findSequenceByName(args.sequenceName);
61
+ if (!seq) {
62
+ return JSON.stringify({ ok: false, error: "no sequence named \"" + args.sequenceName + "\" is open" });
63
+ }
64
+ } else {
65
+ seq = app.project.activeSequence;
66
+ if (!seq) {
67
+ return JSON.stringify({ ok: false, error: "no active sequence, and no sequenceName given" });
68
+ }
69
+ }
70
+
71
+ var trackCollection = args.trackType === "video" ? seq.videoTracks : seq.audioTracks;
72
+ var numTracks = trackCollection.numTracks;
73
+ if (args.trackIndex >= numTracks) {
74
+ return JSON.stringify({
75
+ ok: false,
76
+ error: "trackIndex " + args.trackIndex + " is out of range — sequence has " + numTracks + " " + args.trackType + " track(s)"
77
+ });
78
+ }
79
+
80
+ var track = trackCollection[args.trackIndex];
81
+ var numClips = track.clips.numItems;
82
+ if (args.clipIndex >= numClips) {
83
+ return JSON.stringify({
84
+ ok: false,
85
+ error: "clipIndex " + args.clipIndex + " is out of range — track " + args.trackIndex + " has " + numClips + " clip(s)"
86
+ });
87
+ }
88
+
89
+ var clip = track.clips[args.clipIndex];
90
+ var clipName = null;
91
+ try { clipName = clip.name; } catch (e) { clipName = null; }
92
+
93
+ var clipStartSeconds = 0;
94
+ try { clipStartSeconds = timeValueToSeconds(clip.start) || 0; } catch (e) { clipStartSeconds = 0; }
95
+
96
+ var comp = findClipComponent(clip, [args.componentName], [args.componentName]);
97
+ if (!comp) {
98
+ return JSON.stringify({
99
+ ok: false,
100
+ error: "component \"" + args.componentName + "\" not found on clip (matched against matchName and displayName)"
101
+ });
102
+ }
103
+ var compDisplayName = null;
104
+ try { compDisplayName = comp.displayName; } catch (e) { compDisplayName = null; }
105
+
106
+ var prop = findComponentProperty(comp, [args.propertyName]);
107
+ if (!prop) {
108
+ return JSON.stringify({
109
+ ok: false,
110
+ error: "property \"" + args.propertyName + "\" not found on component \"" + compDisplayName + "\""
111
+ });
112
+ }
113
+
114
+ var fps = 30;
115
+ try { fps = getSequenceFps(seq) || 30; } catch (e) { fps = 30; }
116
+ var toleranceSeconds = 0.5 / fps;
117
+
118
+ var sequenceSeconds = clipStartSeconds + args.seconds;
119
+
120
+ var rawKeys = null;
121
+ try { rawKeys = prop.getKeys(); } catch (e) { rawKeys = null; }
122
+
123
+ var matchedKey = null;
124
+ var matchedSeconds = null;
125
+ if (rawKeys) {
126
+ for (var i = 0; i < rawKeys.length; i++) {
127
+ var ks = null;
128
+ try { ks = keyTimeToSeconds(rawKeys[i]); } catch (e1) { ks = null; }
129
+ if (ks !== null && Math.abs(ks - sequenceSeconds) <= toleranceSeconds) {
130
+ matchedKey = rawKeys[i];
131
+ matchedSeconds = ks;
132
+ break;
133
+ }
134
+ }
135
+ }
136
+
137
+ var interpolationType = args.interpolationType;
138
+ var setAttempts = [];
139
+ var set = false;
140
+
141
+ if (matchedKey !== null) {
142
+ try {
143
+ prop.setInterpolationTypeAtKey(matchedKey, interpolationType, true);
144
+ setAttempts.push({ form: "existingKeyObject", success: true });
145
+ set = true;
146
+ } catch (e2) {
147
+ setAttempts.push({ form: "existingKeyObject", success: false, error: e2.toString() });
148
+ }
149
+ }
150
+
151
+ if (!set) {
152
+ var tf = tryTimeForms(sequenceSeconds, function (t) {
153
+ prop.setInterpolationTypeAtKey(t, interpolationType, true);
154
+ });
155
+ setAttempts = setAttempts.concat(tf.attempts);
156
+ set = tf.success;
157
+ }
158
+
159
+ return JSON.stringify({
160
+ ok: true,
161
+ result: {
162
+ sequenceName: seq.name,
163
+ trackType: args.trackType,
164
+ trackIndex: args.trackIndex,
165
+ clipIndex: args.clipIndex,
166
+ clipName: clipName,
167
+ componentName: compDisplayName,
168
+ propertyName: args.propertyName,
169
+ seconds: args.seconds,
170
+ sequenceSeconds: sequenceSeconds,
171
+ toleranceSeconds: toleranceSeconds,
172
+ matchedExistingKey: matchedKey !== null,
173
+ matchedSeconds: matchedSeconds,
174
+ interpolationType: interpolationType,
175
+ setAttempts: setAttempts,
176
+ called: set,
177
+ note: "interpolationType is a RAW INT whose enum meaning is DISPUTED across Premiere builds " +
178
+ "(0=Linear/4=Hold/5=Bezier per one reference repo vs. a plain 0/1/2 map per another) — " +
179
+ "no name-mapping is applied here. `called` only confirms the call didn't throw; there is " +
180
+ "no confirmed getter to read a keyframe's interpolation back and verify the result."
181
+ }
182
+ });
183
+ } catch (e) {
184
+ return JSON.stringify({ ok: false, error: e.toString() });
185
+ }
186
+ }
@@ -0,0 +1,123 @@
1
+ // Command: set-override-frame-rate → ppb_setOverrideFrameRate
2
+ // Loaded lazily by ppb_dispatch (host/index.jsx) on first use, into the
3
+ // same global ExtendScript context — shared helpers (TICKS_PER_SECOND, ...)
4
+ // are already defined there.
5
+ //
6
+ // Ports the reference project's media.ts set_override_frame_rate tool.
7
+ // Item resolution by nodeId/name uses children-presence recursion (NOT an
8
+ // isBin() gate), per get-project-item-info.jsx's live-debugged finding.
9
+ // previousValue/newValue are read via item.getFootageInterpretation().
10
+ // frameRate (same field get-footage-interpretation exposes) before/after
11
+ // calling item.setOverrideFrameRate(fps). Undo is non-functional on this
12
+ // build — previousValue is the only restoration path.
13
+
14
+ function ppbSetOverrideFrameRate_findByNodeId(item, nodeId, depth) {
15
+ if (depth > 32) {
16
+ return null;
17
+ }
18
+ try {
19
+ if (item.nodeId === nodeId) {
20
+ return item;
21
+ }
22
+ } catch (e) {
23
+ // fall through
24
+ }
25
+ if (item.children && item.children.numItems > 0) {
26
+ for (var i = 0; i < item.children.numItems; i++) {
27
+ var found = ppbSetOverrideFrameRate_findByNodeId(item.children[i], nodeId, depth + 1);
28
+ if (found) {
29
+ return found;
30
+ }
31
+ }
32
+ }
33
+ return null;
34
+ }
35
+
36
+ function ppbSetOverrideFrameRate_findByName(item, name, depth) {
37
+ if (depth > 32) {
38
+ return null;
39
+ }
40
+ try {
41
+ if (item.name === name) {
42
+ return item;
43
+ }
44
+ } catch (e) {
45
+ // fall through
46
+ }
47
+ if (item.children && item.children.numItems > 0) {
48
+ for (var i = 0; i < item.children.numItems; i++) {
49
+ var found = ppbSetOverrideFrameRate_findByName(item.children[i], name, depth + 1);
50
+ if (found) {
51
+ return found;
52
+ }
53
+ }
54
+ }
55
+ return null;
56
+ }
57
+
58
+ function ppbSetOverrideFrameRate_readFrameRate(item) {
59
+ try {
60
+ var interp = item.getFootageInterpretation();
61
+ if (interp) {
62
+ return interp.frameRate;
63
+ }
64
+ } catch (e) {
65
+ // fall through
66
+ }
67
+ return null;
68
+ }
69
+
70
+ function ppb_setOverrideFrameRate(argsJson) {
71
+ try {
72
+ var args;
73
+ try {
74
+ args = JSON.parse(argsJson);
75
+ } catch (e) {
76
+ return JSON.stringify({ ok: false, error: "invalid args JSON: " + e.toString() });
77
+ }
78
+
79
+ var hasNodeId = typeof args.nodeId === "string" && args.nodeId.length > 0;
80
+ var hasName = typeof args.name === "string" && args.name.length > 0;
81
+ if (!hasNodeId && !hasName) {
82
+ return JSON.stringify({ ok: false, error: "either nodeId or name is required" });
83
+ }
84
+ if (typeof args.fps !== "number" || args.fps <= 0) {
85
+ return JSON.stringify({ ok: false, error: "fps must be a positive number" });
86
+ }
87
+
88
+ if (!app.project) {
89
+ return JSON.stringify({ ok: false, error: "no project open" });
90
+ }
91
+
92
+ var item = hasNodeId
93
+ ? ppbSetOverrideFrameRate_findByNodeId(app.project.rootItem, args.nodeId, 0)
94
+ : ppbSetOverrideFrameRate_findByName(app.project.rootItem, args.name, 0);
95
+ if (!item) {
96
+ return JSON.stringify({ ok: false, error: "no project item found matching the given nodeId/name" });
97
+ }
98
+
99
+ var previousValue = ppbSetOverrideFrameRate_readFrameRate(item);
100
+
101
+ try {
102
+ item.setOverrideFrameRate(args.fps);
103
+ } catch (e) {
104
+ return JSON.stringify({ ok: false, error: "setOverrideFrameRate() failed: " + e.toString() });
105
+ }
106
+
107
+ var newValue = ppbSetOverrideFrameRate_readFrameRate(item);
108
+
109
+ return JSON.stringify({
110
+ ok: true,
111
+ result: {
112
+ nodeId: item.nodeId,
113
+ name: item.name,
114
+ previousValue: previousValue,
115
+ requestedValue: args.fps,
116
+ newValue: newValue,
117
+ verified: newValue !== null && Math.abs(newValue - args.fps) < 0.001
118
+ }
119
+ });
120
+ } catch (e) {
121
+ return JSON.stringify({ ok: false, error: e.toString() });
122
+ }
123
+ }