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,159 @@
1
+ // Command: create-caption-track → ppb_createCaptionTrack
2
+ // Loaded lazily by ppb_dispatch (host/index.jsx) on first use, into the
3
+ // same global ExtendScript context.
4
+ //
5
+ // Ported from leancoderkavy's premiere-pro-mcp captions.ts
6
+ // create_caption_track. Creates a caption/subtitle track from an already-
7
+ // imported caption project item (e.g. an .srt file) via
8
+ // seq.createCaptionTrack(item, startSeconds, formatConstant) — the 3-arg
9
+ // signature PREMIERE_API_NOTES.md documents ("3rd arg is an INTEGER
10
+ // CONSTANT, NOT a string — 'Illegal Parameter type' otherwise"). Note
11
+ // this deliberately does NOT resolve to the same call add-text-overlay
12
+ // makes (that file's 1-arg createCaptionTrack(formatNum) — see its header
13
+ // comment for the arity conflict this reveals between reference repos).
14
+ //
15
+ // format is resolved via Sequence.CAPTION_FORMAT_* named constants with a
16
+ // typeof guard first (per PREMIERE_API_NOTES.md's constant list:
17
+ // SUBTITLE/608/708/TELETEXT/OPEN_EBU/OP42/OP47); if the named constant
18
+ // isn't present on this build, a known integer fallback (text.ts's own
19
+ // numeric map) is used ONLY for subtitle/608/708/teletext — ebu/op42/op47
20
+ // have no confirmed integer fallback anywhere in the reference repos, so
21
+ // those fail honestly if the named constant is unavailable.
22
+ //
23
+ // No caption READ API exists (PREMIERE_API_NOTES.md) — this command
24
+ // cannot verify the caption track's actual contents afterward, only that
25
+ // the call itself returned truthy.
26
+ function ppbFindItemCreateCaptionTrack_walk(item, args, depth) {
27
+ if (depth > 32) {
28
+ return null;
29
+ }
30
+ var isBin = false;
31
+ try {
32
+ isBin = typeof ProjectItemType !== "undefined" && item.type === ProjectItemType.BIN;
33
+ } catch (e) {
34
+ isBin = false;
35
+ }
36
+ var matched = false;
37
+ if (args.nodeId !== null) {
38
+ try { matched = item.nodeId === args.nodeId; } catch (e) { matched = false; }
39
+ } else if (args.name !== null) {
40
+ try { matched = item.name === args.name; } catch (e) { matched = false; }
41
+ }
42
+ if (matched) {
43
+ return item;
44
+ }
45
+ if (isBin && item.children) {
46
+ for (var i = 0; i < item.children.numItems; i++) {
47
+ var found = ppbFindItemCreateCaptionTrack_walk(item.children[i], args, depth + 1);
48
+ if (found) {
49
+ return found;
50
+ }
51
+ }
52
+ }
53
+ return null;
54
+ }
55
+
56
+ function ppbFindItemCreateCaptionTrack_resolve(args) {
57
+ var root = app.project.rootItem;
58
+ for (var i = 0; i < root.children.numItems; i++) {
59
+ var found = ppbFindItemCreateCaptionTrack_walk(root.children[i], args, 1);
60
+ if (found) {
61
+ return found;
62
+ }
63
+ }
64
+ return null;
65
+ }
66
+
67
+ var PPB_CAPTION_FORMAT_CONSTANT_NAMES = {
68
+ "subtitle": "CAPTION_FORMAT_SUBTITLE",
69
+ "608": "CAPTION_FORMAT_608",
70
+ "708": "CAPTION_FORMAT_708",
71
+ "teletext": "CAPTION_FORMAT_TELETEXT",
72
+ "ebu": "CAPTION_FORMAT_OPEN_EBU",
73
+ "op42": "CAPTION_FORMAT_OP42",
74
+ "op47": "CAPTION_FORMAT_OP47"
75
+ };
76
+
77
+ var PPB_CAPTION_FORMAT_INT_FALLBACKS = { "subtitle": 3, "608": 1, "708": 2, "teletext": 4 };
78
+
79
+ function ppb_createCaptionTrack(argsJson) {
80
+ try {
81
+ var args;
82
+ try {
83
+ args = JSON.parse(argsJson);
84
+ } catch (e) {
85
+ return JSON.stringify({ ok: false, error: "invalid args JSON: " + e.toString() });
86
+ }
87
+
88
+ var hasNodeId = typeof args.nodeId === "string" && args.nodeId.length > 0;
89
+ var hasName = typeof args.name === "string" && args.name.length > 0;
90
+ if (!hasNodeId && !hasName) {
91
+ return JSON.stringify({ ok: false, error: "either nodeId or name is required to identify the caption project item (e.g. an imported .srt)" });
92
+ }
93
+ if (typeof args.startSeconds !== "number") {
94
+ return JSON.stringify({ ok: false, error: "startSeconds is required" });
95
+ }
96
+
97
+ var format = (typeof args.format === "string" && PPB_CAPTION_FORMAT_CONSTANT_NAMES.hasOwnProperty(args.format)) ? args.format : "subtitle";
98
+
99
+ if (!app.project) {
100
+ return JSON.stringify({ ok: false, error: "no project open" });
101
+ }
102
+ var seq = app.project.activeSequence;
103
+ if (!seq) {
104
+ return JSON.stringify({ ok: false, error: "no active sequence" });
105
+ }
106
+
107
+ var item = ppbFindItemCreateCaptionTrack_resolve({
108
+ nodeId: hasNodeId ? args.nodeId : null,
109
+ name: hasNodeId ? null : args.name
110
+ });
111
+ if (!item) {
112
+ return JSON.stringify({ ok: false, error: "no project item found matching the given nodeId/name" });
113
+ }
114
+
115
+ var constantName = PPB_CAPTION_FORMAT_CONSTANT_NAMES[format];
116
+ var formatValue = null;
117
+ var usedFallbackInt = false;
118
+ try {
119
+ if (typeof Sequence !== "undefined" && typeof Sequence[constantName] !== "undefined") {
120
+ formatValue = Sequence[constantName];
121
+ }
122
+ } catch (e2) {
123
+ formatValue = null;
124
+ }
125
+ if (formatValue === null) {
126
+ if (PPB_CAPTION_FORMAT_INT_FALLBACKS.hasOwnProperty(format)) {
127
+ formatValue = PPB_CAPTION_FORMAT_INT_FALLBACKS[format];
128
+ usedFallbackInt = true;
129
+ } else {
130
+ return JSON.stringify({ ok: false, error: "Sequence." + constantName + " is not available on this build, and format \"" + format + "\" has no known integer fallback" });
131
+ }
132
+ }
133
+
134
+ var createResult = null;
135
+ try {
136
+ createResult = seq.createCaptionTrack(item, args.startSeconds, formatValue);
137
+ } catch (e3) {
138
+ return JSON.stringify({ ok: false, error: "seq.createCaptionTrack() failed: " + e3.toString() });
139
+ }
140
+ if (!createResult) {
141
+ return JSON.stringify({ ok: false, error: "seq.createCaptionTrack() returned a falsy result" });
142
+ }
143
+
144
+ return JSON.stringify({
145
+ ok: true,
146
+ result: {
147
+ created: true,
148
+ item: { name: (function () { try { return item.name; } catch (e) { return null; } })(), nodeId: (function () { try { return item.nodeId; } catch (e) { return null; } })() },
149
+ startSeconds: args.startSeconds,
150
+ format: format,
151
+ formatValue: formatValue,
152
+ usedFallbackInt: usedFallbackInt,
153
+ note: "no caption READ API exists on this build — this only confirms the create call returned truthy, not the track's actual rendered contents."
154
+ }
155
+ });
156
+ } catch (e) {
157
+ return JSON.stringify({ ok: false, error: e.toString() });
158
+ }
159
+ }
@@ -0,0 +1,170 @@
1
+ // Command: create-sequence-from-clips → ppb_createSequenceFromClips
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. Helper names are prefixed
5
+ // ppbCreateSeqFromClips_ to avoid colliding with same-purpose helpers in
6
+ // other lazily-loaded command files evaluated into this same global
7
+ // context (e.g. add-to-timeline.jsx's own project-item walker).
8
+ //
9
+ // Ported from leancoderkavy/premiere-pro-mcp's advanced.ts
10
+ // create_sequence_from_clips tool:
11
+ // `app.project.createNewSequenceFromClips(name, itemsArray[, targetBin])`
12
+ // per PREMIERE_API_NOTES.md's Sequences table ("auto-detects settings from
13
+ // first clip"). nodeIds resolve to project items via the same recursive
14
+ // bin walk used elsewhere in this panel (list-project-items.jsx,
15
+ // add-to-timeline.jsx); targetBinPath (if given) is resolved the same
16
+ // '/'-separated way as create-sequence's --bin, but is NEVER auto-created
17
+ // here (same convention as get-bin-contents.jsx) — pass an existing bin
18
+ // path only.
19
+ function ppbCreateSeqFromClips_findItem(item, nodeId, depth) {
20
+ if (depth > 32) {
21
+ return null;
22
+ }
23
+ var isBin = false;
24
+ try {
25
+ isBin = typeof ProjectItemType !== "undefined" && item.type === ProjectItemType.BIN;
26
+ } catch (e) {
27
+ isBin = false;
28
+ }
29
+ var matched = false;
30
+ try { matched = item.nodeId === nodeId; } catch (e) { matched = false; }
31
+ if (matched) {
32
+ return item;
33
+ }
34
+ if (isBin && item.children) {
35
+ for (var i = 0; i < item.children.numItems; i++) {
36
+ var found = ppbCreateSeqFromClips_findItem(item.children[i], nodeId, depth + 1);
37
+ if (found) {
38
+ return found;
39
+ }
40
+ }
41
+ }
42
+ return null;
43
+ }
44
+
45
+ function ppbCreateSeqFromClips_resolveItem(nodeId) {
46
+ var root = app.project.rootItem;
47
+ for (var i = 0; i < root.children.numItems; i++) {
48
+ var found = ppbCreateSeqFromClips_findItem(root.children[i], nodeId, 1);
49
+ if (found) {
50
+ return found;
51
+ }
52
+ }
53
+ return null;
54
+ }
55
+
56
+ function ppbCreateSeqFromClips_resolveBin(binPath) {
57
+ var segments = binPath.split("/").filter(function (s) { return s.length > 0; });
58
+ var currentBin = app.project.rootItem;
59
+ for (var i = 0; i < segments.length; i++) {
60
+ var segment = segments[i];
61
+ var found = null;
62
+ for (var j = 0; j < currentBin.children.numItems; j++) {
63
+ var child = currentBin.children[j];
64
+ if (child.name === segment && child.type === ProjectItemType.BIN) {
65
+ found = child;
66
+ break;
67
+ }
68
+ }
69
+ if (!found) {
70
+ return null;
71
+ }
72
+ currentBin = found;
73
+ }
74
+ return currentBin;
75
+ }
76
+
77
+ function ppb_createSequenceFromClips(argsJson) {
78
+ try {
79
+ var args;
80
+ try {
81
+ args = JSON.parse(argsJson);
82
+ } catch (e) {
83
+ return JSON.stringify({ ok: false, error: "invalid args JSON: " + e.toString() });
84
+ }
85
+
86
+ if (!app.project) {
87
+ return JSON.stringify({ ok: false, error: "no project open" });
88
+ }
89
+ if (!args.name || typeof args.name !== "string") {
90
+ return JSON.stringify({ ok: false, error: "name is required" });
91
+ }
92
+
93
+ var nodeIds = args.nodeIds;
94
+ if (!nodeIds || !(nodeIds instanceof Array) || nodeIds.length === 0) {
95
+ return JSON.stringify({ ok: false, error: "nodeIds must be a non-empty array" });
96
+ }
97
+
98
+ var items = [];
99
+ var notFound = [];
100
+ for (var i = 0; i < nodeIds.length; i++) {
101
+ var item = ppbCreateSeqFromClips_resolveItem(nodeIds[i]);
102
+ if (item) {
103
+ items.push(item);
104
+ } else {
105
+ notFound.push(nodeIds[i]);
106
+ }
107
+ }
108
+ if (notFound.length > 0) {
109
+ return JSON.stringify({ ok: false, error: "could not find project item(s) for nodeId(s): " + notFound.join(", ") });
110
+ }
111
+
112
+ var targetBin = null;
113
+ if (args.targetBinPath && typeof args.targetBinPath === "string") {
114
+ targetBin = ppbCreateSeqFromClips_resolveBin(args.targetBinPath);
115
+ if (!targetBin) {
116
+ return JSON.stringify({ ok: false, error: "targetBinPath \"" + args.targetBinPath + "\" does not exist (not auto-created)" });
117
+ }
118
+ }
119
+
120
+ var numSequencesBefore = app.project.sequences.numSequences;
121
+
122
+ var callError = null;
123
+ try {
124
+ if (targetBin) {
125
+ app.project.createNewSequenceFromClips(args.name, items, targetBin);
126
+ } else {
127
+ app.project.createNewSequenceFromClips(args.name, items);
128
+ }
129
+ } catch (e) {
130
+ callError = e.toString();
131
+ }
132
+
133
+ var numSequencesAfter = app.project.sequences.numSequences;
134
+
135
+ if (callError !== null || numSequencesAfter <= numSequencesBefore) {
136
+ return JSON.stringify({
137
+ ok: false,
138
+ error: callError !== null
139
+ ? ("createNewSequenceFromClips() failed: " + callError)
140
+ : "createNewSequenceFromClips() did not throw, but the project's sequence count did not increase",
141
+ numSequencesBefore: numSequencesBefore,
142
+ numSequencesAfter: numSequencesAfter
143
+ });
144
+ }
145
+
146
+ var newSeq = findSequenceByName(args.name);
147
+ if (!newSeq) {
148
+ return JSON.stringify({
149
+ ok: false,
150
+ error: "sequence count increased but no sequence named \"" + args.name + "\" was found afterward",
151
+ numSequencesBefore: numSequencesBefore,
152
+ numSequencesAfter: numSequencesAfter
153
+ });
154
+ }
155
+
156
+ return JSON.stringify({
157
+ ok: true,
158
+ result: {
159
+ name: newSeq.name,
160
+ sequenceID: newSeq.sequenceID,
161
+ clipCount: items.length,
162
+ targetBinPath: args.targetBinPath || null,
163
+ numSequencesBefore: numSequencesBefore,
164
+ numSequencesAfter: numSequencesAfter
165
+ }
166
+ });
167
+ } catch (e) {
168
+ return JSON.stringify({ ok: false, error: e.toString() });
169
+ }
170
+ }
@@ -0,0 +1,90 @@
1
+ // Command: create-sequence-from-preset → ppb_createSequenceFromPreset
2
+ // Loaded lazily by ppb_dispatch (host/index.jsx) on first use, into the
3
+ // same global ExtendScript context — shared helpers (ensureQEEnabled,
4
+ // findSequenceByName, getSequenceFps, ...) are already defined there.
5
+ //
6
+ // Sibling of create-sequence.jsx, which bundles its own fixed 4K/25fps
7
+ // .sqpreset — this command is the caller-supplied-preset counterpart per
8
+ // leancoderkavy's advanced.ts create_sequence_from_preset tool
9
+ // (app.project.createNewSequenceFromPreset did not exist on our build per
10
+ // PREMIERE_API_NOTES.md — the QE path `qe.project.newSequence(name,
11
+ // presetPath)` create-sequence.jsx already proved out is reused here with
12
+ // an arbitrary presetPath instead of the bundled one).
13
+ //
14
+ // Unlike create-sequence, there is no --bin argument — the new sequence
15
+ // lands wherever qe.project.newSequence() puts it (bin placement was not
16
+ // part of the reference tool's own scope either). Duplicate-name checking
17
+ // is at the project root only, since there's no bin scope to check within.
18
+ function ppb_createSequenceFromPreset(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.name || typeof args.name !== "string") {
28
+ return JSON.stringify({ ok: false, error: "name is required" });
29
+ }
30
+ if (!args.presetPath || typeof args.presetPath !== "string") {
31
+ return JSON.stringify({ ok: false, error: "presetPath is required" });
32
+ }
33
+
34
+ if (!app.project) {
35
+ return JSON.stringify({ ok: false, error: "no project open" });
36
+ }
37
+
38
+ for (var k = 0; k < app.project.rootItem.children.numItems; k++) {
39
+ var existing = app.project.rootItem.children[k];
40
+ if (existing.name === args.name && existing.type === ProjectItemType.SEQUENCE) {
41
+ return JSON.stringify({ ok: false, error: "sequence \"" + args.name + "\" already exists at the project root" });
42
+ }
43
+ }
44
+
45
+ var presetFile = new File(args.presetPath);
46
+ if (!presetFile.exists) {
47
+ return JSON.stringify({ ok: false, error: "preset file not found: " + presetFile.fsName });
48
+ }
49
+
50
+ try {
51
+ ensureQEEnabled();
52
+ qe.project.newSequence(args.name, presetFile.fsName);
53
+ } catch (e) {
54
+ return JSON.stringify({ ok: false, error: "qe.project.newSequence failed: " + e.toString() });
55
+ }
56
+
57
+ var newSequence = findSequenceByName(args.name);
58
+ if (!newSequence) {
59
+ return JSON.stringify({ ok: false, error: "sequence creation failed: no sequence found after qe.project.newSequence" });
60
+ }
61
+
62
+ var width = null;
63
+ var height = null;
64
+ var fps = null;
65
+ try {
66
+ var settings = newSequence.getSettings();
67
+ width = settings.videoFrameWidth;
68
+ height = settings.videoFrameHeight;
69
+ fps = getSequenceFps(newSequence);
70
+ } catch (e) {
71
+ width = null;
72
+ height = null;
73
+ fps = null;
74
+ }
75
+
76
+ return JSON.stringify({
77
+ ok: true,
78
+ result: {
79
+ name: newSequence.name,
80
+ sequenceID: newSequence.sequenceID,
81
+ presetPath: presetFile.fsName,
82
+ width: width,
83
+ height: height,
84
+ fps: fps
85
+ }
86
+ });
87
+ } catch (e) {
88
+ return JSON.stringify({ ok: false, error: e.toString() });
89
+ }
90
+ }
@@ -0,0 +1,166 @@
1
+ // Command: create-sequence → ppb_createSequence
2
+ // Loaded lazily by ppb_dispatch (host/index.jsx) on first use, into the
3
+ // same global ExtendScript context — shared helpers (ensureQEEnabled, ...)
4
+ // are already defined there.
5
+ //
6
+ // Sequence creation without the interactive "New Sequence" dialog uses the
7
+ // undocumented QE (Quality Engineering) DOM, not the standard app.project
8
+ // DOM: app.project.createNewSequence() pops the dialog regardless of its
9
+ // placeholderID argument, and app.project.importSequencePreset() does not
10
+ // exist at all on this Premiere Pro version. qe.project.newSequence(name,
11
+ // presetPath) creates the sequence directly from a .sqpreset file with no
12
+ // dialog, but returns a QE-DOM object with a different API surface — the
13
+ // standard Sequence object (needed for moveBin/getSettings/setSettings) is
14
+ // re-fetched from app.project.sequences by name afterward.
15
+
16
+ // The bundled preset is 4K/25fps; requests matching those values skip the
17
+ // getSettings()/setSettings() override step entirely (the preset already
18
+ // has the right settings, including Rec.709 color space).
19
+ var DEFAULT_FPS = 25;
20
+ var DEFAULT_WIDTH = 3840;
21
+ var DEFAULT_HEIGHT = 2160;
22
+
23
+ function ppb_createSequence(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.name || typeof args.name !== "string") {
33
+ return JSON.stringify({ ok: false, error: "name is required" });
34
+ }
35
+ if (typeof args.bin !== "string") {
36
+ return JSON.stringify({ ok: false, error: "bin is required" });
37
+ }
38
+ if (typeof args.fps !== "number" || args.fps <= 0) {
39
+ return JSON.stringify({ ok: false, error: "fps must be a positive number" });
40
+ }
41
+ if (typeof args.width !== "number" || args.width <= 0 || Math.floor(args.width) !== args.width) {
42
+ return JSON.stringify({ ok: false, error: "width must be a positive integer" });
43
+ }
44
+ if (typeof args.height !== "number" || args.height <= 0 || Math.floor(args.height) !== args.height) {
45
+ return JSON.stringify({ ok: false, error: "height must be a positive integer" });
46
+ }
47
+
48
+ if (!app.project) {
49
+ return JSON.stringify({ ok: false, error: "no project open" });
50
+ }
51
+
52
+ // Walk (and create, if missing) the bin path.
53
+ var binSegments = args.bin.split("/").filter(function (s) { return s.length > 0; });
54
+ var currentBin = app.project.rootItem;
55
+
56
+ for (var i = 0; i < binSegments.length; i++) {
57
+ var segment = binSegments[i];
58
+ var found = null;
59
+
60
+ for (var j = 0; j < currentBin.children.numItems; j++) {
61
+ var child = currentBin.children[j];
62
+ if (child.name === segment && child.type === ProjectItemType.BIN) {
63
+ found = child;
64
+ break;
65
+ }
66
+ }
67
+
68
+ if (!found) {
69
+ found = currentBin.createBin(segment);
70
+ }
71
+
72
+ currentBin = found;
73
+ }
74
+
75
+ // Duplicate check — before any mutation past bin creation.
76
+ for (var k = 0; k < currentBin.children.numItems; k++) {
77
+ var existing = currentBin.children[k];
78
+ if (existing.name === args.name && existing.type === ProjectItemType.SEQUENCE) {
79
+ return JSON.stringify({
80
+ ok: false,
81
+ error: "sequence \"" + args.name + "\" already exists in bin \"" + args.bin + "\""
82
+ });
83
+ }
84
+ }
85
+
86
+ if (typeof args.pluginDir !== "string" || args.pluginDir.length === 0) {
87
+ return JSON.stringify({ ok: false, error: "pluginDir was not provided by the panel" });
88
+ }
89
+
90
+ var presetFile = new File(args.pluginDir + "/host/presets/default-4k-25fps.sqpreset");
91
+ if (!presetFile.exists) {
92
+ return JSON.stringify({ ok: false, error: "sequence preset file not found: " + presetFile.fsName });
93
+ }
94
+
95
+ try {
96
+ ensureQEEnabled();
97
+ qe.project.newSequence(args.name, presetFile.fsName);
98
+ } catch (e) {
99
+ return JSON.stringify({ ok: false, error: "qe.project.newSequence failed: " + e.toString() });
100
+ }
101
+
102
+ // qe.project.newSequence returns a QE-DOM object, not a standard
103
+ // Sequence — re-fetch the real one by name to use normal DOM methods.
104
+ var newSequence = null;
105
+ for (var s = 0; s < app.project.sequences.numSequences; s++) {
106
+ if (app.project.sequences[s].name === args.name) {
107
+ newSequence = app.project.sequences[s];
108
+ break;
109
+ }
110
+ }
111
+
112
+ if (!newSequence) {
113
+ return JSON.stringify({ ok: false, error: "sequence creation failed: no sequence found after qe.project.newSequence" });
114
+ }
115
+
116
+ var settingsApplied = [];
117
+ var settingsFailed = {};
118
+
119
+ try {
120
+ newSequence.projectItem.moveBin(currentBin);
121
+ } catch (e) {
122
+ settingsFailed.bin = e.toString();
123
+ }
124
+
125
+ var matchesDefaults = (args.fps === DEFAULT_FPS && args.width === DEFAULT_WIDTH && args.height === DEFAULT_HEIGHT);
126
+
127
+ if (matchesDefaults) {
128
+ // The bundled preset already is 4K/25fps — nothing further to apply.
129
+ settingsApplied.push("fps");
130
+ settingsApplied.push("resolution");
131
+ } else {
132
+ try {
133
+ var fpsSettings = newSequence.getSettings();
134
+ fpsSettings.videoFrameRate = TICKS_PER_SECOND / args.fps;
135
+ newSequence.setSettings(fpsSettings);
136
+ settingsApplied.push("fps");
137
+ } catch (e) {
138
+ settingsFailed.fps = e.toString();
139
+ }
140
+
141
+ try {
142
+ var resSettings = newSequence.getSettings();
143
+ resSettings.videoFrameWidth = args.width;
144
+ resSettings.videoFrameHeight = args.height;
145
+ newSequence.setSettings(resSettings);
146
+ settingsApplied.push("resolution");
147
+ } catch (e) {
148
+ settingsFailed.resolution = e.toString();
149
+ }
150
+ }
151
+
152
+ return JSON.stringify({
153
+ ok: true,
154
+ result: {
155
+ name: newSequence.name,
156
+ bin: args.bin,
157
+ sequenceID: newSequence.sequenceID,
158
+ settingsApplied: settingsApplied,
159
+ settingsFailed: settingsFailed,
160
+ colorSpace: "rec709 (from bundled sequence preset)"
161
+ }
162
+ });
163
+ } catch (e) {
164
+ return JSON.stringify({ ok: false, error: e.toString() });
165
+ }
166
+ }
@@ -0,0 +1,80 @@
1
+ // Command: create-smart-bin → ppb_createSmartBin
2
+ // Loaded lazily by ppb_dispatch (host/index.jsx) on first use, into the
3
+ // same global ExtendScript context.
4
+ //
5
+ // Ported from leancoderkavy/premiere-pro-mcp's project.ts create_smart_bin.
6
+ // Calls rootItem.createSmartBin(name, query) — per PREMIERE_API_NOTES.md.
7
+ // Verified by a root-bin children-count increase and locating the newly-
8
+ // appeared bin by name (never trusting the call's own return value, same
9
+ // distrust pattern as add-adjustment-layer.jsx's item creation step).
10
+ // query syntax is Premiere's own search-bin query language — not
11
+ // validated here, any failure surfaces as either a thrown exception or a
12
+ // silent no-op (count doesn't increase).
13
+
14
+ function ppb_createSmartBin(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 (!args.name || typeof args.name !== "string") {
24
+ return JSON.stringify({ ok: false, error: "name is required" });
25
+ }
26
+ if (!args.query || typeof args.query !== "string") {
27
+ return JSON.stringify({ ok: false, error: "query is required" });
28
+ }
29
+
30
+ if (!app.project) {
31
+ return JSON.stringify({ ok: false, error: "no project open" });
32
+ }
33
+
34
+ var root = app.project.rootItem;
35
+ if (typeof root.createSmartBin !== "function") {
36
+ return JSON.stringify({ ok: false, error: "createSmartBin is not available on this Premiere build" });
37
+ }
38
+
39
+ var countBefore = root.children.numItems;
40
+
41
+ try {
42
+ root.createSmartBin(args.name, args.query);
43
+ } catch (e) {
44
+ return JSON.stringify({ ok: false, error: "createSmartBin() failed: " + e.toString() });
45
+ }
46
+
47
+ var freshChildren = app.project.rootItem.children;
48
+ var countAfter = freshChildren.numItems;
49
+ var created = countAfter > countBefore;
50
+
51
+ var newBin = null;
52
+ if (created) {
53
+ for (var i = freshChildren.numItems - 1; i >= 0; i--) {
54
+ var it = freshChildren[i];
55
+ var itName = null;
56
+ try { itName = it.name; } catch (e) { itName = null; }
57
+ if (itName === args.name) {
58
+ newBin = it;
59
+ break;
60
+ }
61
+ }
62
+ }
63
+
64
+ if (!created || !newBin) {
65
+ return JSON.stringify({
66
+ ok: false,
67
+ error: "createSmartBin() did not throw, but no new bin named \"" + args.name + "\" was found afterward",
68
+ countBefore: countBefore,
69
+ countAfter: countAfter
70
+ });
71
+ }
72
+
73
+ return JSON.stringify({
74
+ ok: true,
75
+ result: { created: true, name: args.name, query: args.query, nodeId: newBin.nodeId }
76
+ });
77
+ } catch (e) {
78
+ return JSON.stringify({ ok: false, error: e.toString() });
79
+ }
80
+ }