@synthetic-ai/premiere-mcp 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/CHANGELOG.md +73 -0
  2. package/LICENSE +21 -0
  3. package/README.md +242 -0
  4. package/cep-plugin/.debug +8 -0
  5. package/cep-plugin/CSInterface.js +70 -0
  6. package/cep-plugin/CSXS/manifest.xml +49 -0
  7. package/cep-plugin/host.jsx +7 -0
  8. package/cep-plugin/index.html +419 -0
  9. package/cep-plugin/main.js +327 -0
  10. package/cep-plugin/premiere.jsx +24947 -0
  11. package/cep-plugin/synthetic-logo.png +0 -0
  12. package/dist/bridge/file-bridge.d.ts +29 -0
  13. package/dist/bridge/file-bridge.js +145 -0
  14. package/dist/bridge/script-builder.d.ts +19 -0
  15. package/dist/bridge/script-builder.js +408 -0
  16. package/dist/http-server.d.ts +21 -0
  17. package/dist/http-server.js +140 -0
  18. package/dist/index.d.ts +3 -0
  19. package/dist/index.js +70 -0
  20. package/dist/resources/extendscript-reference.d.ts +2 -0
  21. package/dist/resources/extendscript-reference.js +219 -0
  22. package/dist/server.d.ts +4 -0
  23. package/dist/server.js +276 -0
  24. package/dist/tools/advanced.d.ts +530 -0
  25. package/dist/tools/advanced.js +923 -0
  26. package/dist/tools/audio-advanced.d.ts +847 -0
  27. package/dist/tools/audio-advanced.js +1653 -0
  28. package/dist/tools/audio.d.ts +84 -0
  29. package/dist/tools/audio.js +123 -0
  30. package/dist/tools/captions.d.ts +30 -0
  31. package/dist/tools/captions.js +73 -0
  32. package/dist/tools/clipboard.d.ts +153 -0
  33. package/dist/tools/clipboard.js +381 -0
  34. package/dist/tools/color-advanced.d.ts +821 -0
  35. package/dist/tools/color-advanced.js +772 -0
  36. package/dist/tools/discovery.d.ts +128 -0
  37. package/dist/tools/discovery.js +445 -0
  38. package/dist/tools/effects.d.ts +211 -0
  39. package/dist/tools/effects.js +554 -0
  40. package/dist/tools/export-presets.d.ts +333 -0
  41. package/dist/tools/export-presets.js +490 -0
  42. package/dist/tools/export.d.ts +320 -0
  43. package/dist/tools/export.js +618 -0
  44. package/dist/tools/health.d.ts +9 -0
  45. package/dist/tools/health.js +25 -0
  46. package/dist/tools/inspection.d.ts +179 -0
  47. package/dist/tools/inspection.js +1089 -0
  48. package/dist/tools/keyframes.d.ts +253 -0
  49. package/dist/tools/keyframes.js +611 -0
  50. package/dist/tools/markers.d.ts +117 -0
  51. package/dist/tools/markers.js +206 -0
  52. package/dist/tools/media.d.ts +297 -0
  53. package/dist/tools/media.js +493 -0
  54. package/dist/tools/metadata.d.ts +178 -0
  55. package/dist/tools/metadata.js +278 -0
  56. package/dist/tools/multicam.d.ts +199 -0
  57. package/dist/tools/multicam.js +212 -0
  58. package/dist/tools/playback.d.ts +34 -0
  59. package/dist/tools/playback.js +63 -0
  60. package/dist/tools/playhead.d.ts +77 -0
  61. package/dist/tools/playhead.js +185 -0
  62. package/dist/tools/project-manager.d.ts +65 -0
  63. package/dist/tools/project-manager.js +81 -0
  64. package/dist/tools/project.d.ts +390 -0
  65. package/dist/tools/project.js +596 -0
  66. package/dist/tools/scripting.d.ts +98 -0
  67. package/dist/tools/scripting.js +434 -0
  68. package/dist/tools/selection.d.ts +120 -0
  69. package/dist/tools/selection.js +334 -0
  70. package/dist/tools/sequence-advanced.d.ts +84 -0
  71. package/dist/tools/sequence-advanced.js +471 -0
  72. package/dist/tools/sequence-settings.d.ts +397 -0
  73. package/dist/tools/sequence-settings.js +967 -0
  74. package/dist/tools/sequence.d.ts +211 -0
  75. package/dist/tools/sequence.js +378 -0
  76. package/dist/tools/source-monitor.d.ts +95 -0
  77. package/dist/tools/source-monitor.js +180 -0
  78. package/dist/tools/text.d.ts +97 -0
  79. package/dist/tools/text.js +159 -0
  80. package/dist/tools/timeline.d.ts +265 -0
  81. package/dist/tools/timeline.js +575 -0
  82. package/dist/tools/track-management.d.ts +482 -0
  83. package/dist/tools/track-management.js +787 -0
  84. package/dist/tools/track-targeting.d.ts +666 -0
  85. package/dist/tools/track-targeting.js +1318 -0
  86. package/dist/tools/tracks.d.ts +90 -0
  87. package/dist/tools/tracks.js +183 -0
  88. package/dist/tools/transitions-motion.d.ts +788 -0
  89. package/dist/tools/transitions-motion.js +680 -0
  90. package/dist/tools/transitions.d.ts +124 -0
  91. package/dist/tools/transitions.js +267 -0
  92. package/dist/tools/utility.d.ts +469 -0
  93. package/dist/tools/utility.js +1118 -0
  94. package/dist/tools/workspace.d.ts +25 -0
  95. package/dist/tools/workspace.js +61 -0
  96. package/package.json +66 -0
  97. package/scripts/build-chat-dmg.sh +210 -0
  98. package/scripts/build-chat-zip.sh +291 -0
  99. package/scripts/install-cep-windows.ps1 +99 -0
  100. package/scripts/install-cep.sh +66 -0
  101. package/scripts/install-chat-plugin.bat +67 -0
  102. package/scripts/install-chat-plugin.sh +85 -0
  103. package/scripts/install-windows.bat +95 -0
  104. package/scripts/package.mjs +218 -0
@@ -0,0 +1,327 @@
1
+ /* MCP Bridge - CEP Plugin Main Script
2
+ * Polls a temp directory for command files (.jsx), executes them
3
+ * in Premiere Pro's ExtendScript engine, and writes results back.
4
+ *
5
+ * Improvements over the original:
6
+ * - isRunning flag guards the poll loop so concurrent executions can't pile up.
7
+ * - executeScript wraps cs.evalScript with a 25-second timeout to prevent hangs.
8
+ * - Status indicator updates on every state change (idle / running / error).
9
+ * - Reconnect logic: if Premiere Pro is unresponsive, the bridge backs off and retries. */
10
+
11
+ var cs = new CSInterface();
12
+ var bridgeRunning = false;
13
+ var isRunning = false; // true while a script is actively executing (prevents concurrent runs)
14
+ var pollInterval = null;
15
+ var commandCount = 0;
16
+ var errorCount = 0;
17
+ var consecutiveErrors = 0;
18
+ var tempDir = "";
19
+ var POLL_MS = 200;
20
+ var EXEC_TIMEOUT_MS = 25000; // 25-second hard timeout for ExtendScript execution
21
+
22
+ // ---- Logging ----
23
+ function log(msg, cls) {
24
+ var el = document.getElementById("log");
25
+ var entry = document.createElement("div");
26
+ entry.className = "log-entry " + (cls || "");
27
+ var ts = new Date().toLocaleTimeString();
28
+ entry.textContent = "[" + ts + "] " + msg;
29
+ el.appendChild(entry);
30
+ el.scrollTop = el.scrollHeight;
31
+ // Keep max 100 entries
32
+ while (el.children.length > 100) el.removeChild(el.firstChild);
33
+ }
34
+
35
+ // ---- Status ----
36
+ function setStatus(state, text) {
37
+ var dot = document.getElementById("statusDot");
38
+ dot.className = "status-dot " + state;
39
+ document.getElementById("statusText").textContent = text;
40
+
41
+ // Mirror the same state onto the small dot inside the product block, so the
42
+ // "is the panel OK?" cue is visible up-top without scanning the status row.
43
+ // Map: connected -> ok, warning -> warn, error -> err, waiting/* -> grey.
44
+ var ps = document.getElementById("productStatus");
45
+ if (ps) {
46
+ ps.className = "product-status" +
47
+ (state === "connected" ? " ok" :
48
+ state === "warning" ? " warn" :
49
+ state === "error" ? " err" : "");
50
+ ps.title = text || "Panel ready";
51
+ }
52
+ }
53
+
54
+ // ---- File I/O via Node.js (CEP has access to Node) ----
55
+ var fs = require("fs");
56
+ var path = require("path");
57
+ var os = require("os");
58
+ tempDir = path.join(os.tmpdir(), "premiere-mcp-bridge");
59
+
60
+ function ensureDir(dir) {
61
+ try {
62
+ if (!fs.existsSync(dir)) {
63
+ fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
64
+ }
65
+ } catch (e) {
66
+ log("Error creating dir: " + e.message, "err");
67
+ }
68
+ }
69
+
70
+ function listCommandFiles() {
71
+ try {
72
+ if (!fs.existsSync(tempDir)) return [];
73
+ var files = fs.readdirSync(tempDir);
74
+ return files
75
+ .filter(function (f) { return f.indexOf("cmd_") === 0 && f.indexOf(".jsx") > 0; })
76
+ .sort(); // process in order
77
+ } catch (e) {
78
+ return [];
79
+ }
80
+ }
81
+
82
+ function readFile(filePath) {
83
+ try {
84
+ return fs.readFileSync(filePath, "utf-8");
85
+ } catch (e) {
86
+ return null;
87
+ }
88
+ }
89
+
90
+ function writeFile(filePath, content) {
91
+ try {
92
+ // Atomic write: write to a temp file the MCP server's poller ignores
93
+ // (it waits for the exact res_<id>.json name), then rename into place.
94
+ // Prevents the server from reading a half-written response file.
95
+ var tmpPath = filePath + ".part";
96
+ fs.writeFileSync(tmpPath, content, "utf-8");
97
+ fs.renameSync(tmpPath, filePath);
98
+ return true;
99
+ } catch (e) {
100
+ log("Error writing " + filePath + ": " + e.message, "err");
101
+ return false;
102
+ }
103
+ }
104
+
105
+ function deleteFile(filePath) {
106
+ try {
107
+ if (fs.existsSync(filePath)) fs.unlinkSync(filePath);
108
+ } catch (e) {}
109
+ }
110
+
111
+ // ---- Script Execution with timeout ----
112
+ function executeScript(script, callback) {
113
+ // Script is already wrapped in an IIFE by the MCP server's buildScript(),
114
+ // so we pass it directly to avoid double-wrapping.
115
+ var timedOut = false;
116
+ var settled = false;
117
+
118
+ var timeoutHandle = setTimeout(function () {
119
+ if (settled) return;
120
+ timedOut = true;
121
+ settled = true;
122
+ log("Execution timed out after " + (EXEC_TIMEOUT_MS / 1000) + "s", "err");
123
+ consecutiveErrors++;
124
+ updateStatusFromErrors();
125
+ callback(JSON.stringify({ success: false, error: "ExtendScript execution timed out after " + (EXEC_TIMEOUT_MS / 1000) + " seconds. Premiere Pro may be busy or unresponsive." }));
126
+ }, EXEC_TIMEOUT_MS);
127
+
128
+ cs.evalScript(script, function (result) {
129
+ if (settled) return; // already timed out
130
+ settled = true;
131
+ clearTimeout(timeoutHandle);
132
+ consecutiveErrors = 0; // reset on success
133
+ callback(result);
134
+ });
135
+ }
136
+
137
+ // ---- Update status dot based on error state ----
138
+ function updateStatusFromErrors() {
139
+ if (!bridgeRunning) return;
140
+ if (consecutiveErrors >= 3) {
141
+ setStatus("error", "Running — Premiere Pro unresponsive (" + consecutiveErrors + " errors)");
142
+ } else if (consecutiveErrors > 0) {
143
+ setStatus("warning", "Running — " + consecutiveErrors + " consecutive error(s)");
144
+ } else {
145
+ setStatus("connected", "Running — " + commandCount + " commands processed");
146
+ }
147
+ }
148
+
149
+ // ---- Command Processing ----
150
+ function processCommands() {
151
+ // Prevent concurrent executions: if a script is still running, skip this poll tick.
152
+ if (isRunning) return;
153
+
154
+ var cmdFiles = listCommandFiles();
155
+ if (cmdFiles.length === 0) return;
156
+
157
+ // Process one command at a time (FIFO). The next tick picks up the next file.
158
+ isRunning = true;
159
+ processOneCommand(cmdFiles[0]);
160
+ }
161
+
162
+ function processOneCommand(cmdFileName) {
163
+ var cmdFilePath = path.join(tempDir, cmdFileName);
164
+ var script = readFile(cmdFilePath);
165
+ if (!script) {
166
+ log("Failed to read: " + cmdFileName, "err");
167
+ deleteFile(cmdFilePath);
168
+ return;
169
+ }
170
+
171
+ // Derive response filename: cmd_12345.jsx -> res_12345.json
172
+ var id = cmdFileName.replace("cmd_", "").replace(".jsx", "");
173
+ var resFilePath = path.join(tempDir, "res_" + id + ".json");
174
+
175
+ log("Executing: " + cmdFileName + " (" + script.length + " chars)", "cmd");
176
+
177
+ // Delete the command file immediately to avoid re-processing
178
+ deleteFile(cmdFilePath);
179
+
180
+ executeScript(script, function (result) {
181
+ isRunning = false; // release the lock so the next command can be processed
182
+ commandCount++;
183
+ document.getElementById("cmdCount").textContent = commandCount;
184
+
185
+ var response;
186
+ try {
187
+ // ExtendScript returns a string; try to parse it as JSON
188
+ if (result && result !== "undefined" && result !== "null") {
189
+ // Check if it's already valid JSON
190
+ var parsed = JSON.parse(result);
191
+ response = JSON.stringify(parsed);
192
+ // If the parsed result contains an error field, count it
193
+ if (parsed && parsed.success === false) {
194
+ errorCount++;
195
+ consecutiveErrors++;
196
+ log("Result: Error — " + (parsed.error || "unknown"), "err");
197
+ } else {
198
+ consecutiveErrors = 0;
199
+ log("Result: OK", "ok");
200
+ }
201
+ } else {
202
+ response = JSON.stringify({ success: true, data: result || null });
203
+ consecutiveErrors = 0;
204
+ log("Result: OK (null/undefined)", "ok");
205
+ }
206
+ } catch (e) {
207
+ // If result isn't JSON, wrap it
208
+ if (result && result.indexOf("Error") === 0) {
209
+ response = JSON.stringify({ success: false, error: result });
210
+ errorCount++;
211
+ consecutiveErrors++;
212
+ log("Result: " + result, "err");
213
+ } else {
214
+ response = JSON.stringify({ success: true, data: result });
215
+ consecutiveErrors = 0;
216
+ log("Result: OK (raw)", "ok");
217
+ }
218
+ }
219
+
220
+ updateStatusFromErrors();
221
+ writeFile(resFilePath, response);
222
+ });
223
+ }
224
+
225
+ // ---- Bridge Control ----
226
+ function startBridge() {
227
+ tempDir = document.getElementById("tempDir").value.trim();
228
+ if (!tempDir) {
229
+ log("Please set a temp directory", "err");
230
+ return;
231
+ }
232
+
233
+ ensureDir(tempDir);
234
+ bridgeRunning = true;
235
+ setStatus("connected", "Running — polling " + tempDir);
236
+ log("Bridge started. Temp dir: " + tempDir);
237
+
238
+ document.getElementById("btnStart").disabled = true;
239
+ document.getElementById("btnStop").disabled = false;
240
+
241
+ // Verify Premiere Pro connection
242
+ cs.evalScript("app.version", function (version) {
243
+ if (version && version !== "undefined") {
244
+ log("Premiere Pro: " + version, "ok");
245
+ } else {
246
+ log("Warning: Could not detect Premiere Pro version", "err");
247
+ }
248
+ });
249
+
250
+ pollInterval = setInterval(function () {
251
+ if (bridgeRunning) processCommands();
252
+ }, POLL_MS);
253
+ }
254
+
255
+ function stopBridge() {
256
+ bridgeRunning = false;
257
+ if (pollInterval) clearInterval(pollInterval);
258
+ pollInterval = null;
259
+
260
+ setStatus("", "Stopped");
261
+ log("Bridge stopped");
262
+
263
+ document.getElementById("btnStart").disabled = false;
264
+ document.getElementById("btnStop").disabled = true;
265
+ }
266
+
267
+ function saveTempDir() {
268
+ tempDir = document.getElementById("tempDir").value.trim();
269
+ log("Temp directory saved: " + tempDir);
270
+ // Persist via localStorage
271
+ try {
272
+ localStorage.setItem("mcp_bridge_temp_dir", tempDir);
273
+ } catch (e) {}
274
+ }
275
+
276
+ // ---- Init ----
277
+ (function init() {
278
+ // Set the default temp dir in the input field
279
+ document.getElementById("tempDir").value = tempDir;
280
+
281
+ // Load premiere.jsx library (extended function library from AdobePremiereProMCP)
282
+ cs.evalScript('$.evalFile("' + cs.getSystemPath(SystemPath.EXTENSION) + '/premiere.jsx")', function(result) {
283
+ log("premiere.jsx loaded: " + (result === "true" || result === "undefined" ? "OK" : result));
284
+ });
285
+
286
+ // Restore saved temp dir
287
+ try {
288
+ var saved = localStorage.getItem("mcp_bridge_temp_dir");
289
+ if (saved) {
290
+ tempDir = saved;
291
+ document.getElementById("tempDir").value = tempDir;
292
+ }
293
+ } catch (e) {}
294
+
295
+ // Wire any element with [data-href] to open the URL in the user's default
296
+ // browser (CEP intercepts <a href> navigation; we have to bridge through
297
+ // cep.util.openURLInDefaultBrowser to escape the WebView).
298
+ function openExternal(url) {
299
+ try {
300
+ if (window.cep && cep.util && cep.util.openURLInDefaultBrowser) {
301
+ cep.util.openURLInDefaultBrowser(url);
302
+ } else if (window.__adobe_cep__ && window.__adobe_cep__.openURLInDefaultBrowser) {
303
+ window.__adobe_cep__.openURLInDefaultBrowser(url);
304
+ } else {
305
+ window.open(url, "_blank");
306
+ }
307
+ } catch (e) { /* swallow — non-fatal */ }
308
+ }
309
+ var links = document.querySelectorAll("[data-href]");
310
+ for (var i = 0; i < links.length; i++) {
311
+ (function (el) {
312
+ el.addEventListener("click", function (ev) {
313
+ ev.preventDefault();
314
+ openExternal(el.getAttribute("data-href"));
315
+ });
316
+ })(links[i]);
317
+ }
318
+
319
+ log("Synthetic Premiere MCP loaded");
320
+ setStatus("waiting", "Ready — click Start Bridge");
321
+
322
+ // Auto-start if temp dir exists
323
+ if (fs.existsSync(tempDir)) {
324
+ log("Temp directory exists, auto-starting...");
325
+ setTimeout(startBridge, 500);
326
+ }
327
+ })();