@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.
- package/CHANGELOG.md +73 -0
- package/LICENSE +21 -0
- package/README.md +242 -0
- package/cep-plugin/.debug +8 -0
- package/cep-plugin/CSInterface.js +70 -0
- package/cep-plugin/CSXS/manifest.xml +49 -0
- package/cep-plugin/host.jsx +7 -0
- package/cep-plugin/index.html +419 -0
- package/cep-plugin/main.js +327 -0
- package/cep-plugin/premiere.jsx +24947 -0
- package/cep-plugin/synthetic-logo.png +0 -0
- package/dist/bridge/file-bridge.d.ts +29 -0
- package/dist/bridge/file-bridge.js +145 -0
- package/dist/bridge/script-builder.d.ts +19 -0
- package/dist/bridge/script-builder.js +408 -0
- package/dist/http-server.d.ts +21 -0
- package/dist/http-server.js +140 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +70 -0
- package/dist/resources/extendscript-reference.d.ts +2 -0
- package/dist/resources/extendscript-reference.js +219 -0
- package/dist/server.d.ts +4 -0
- package/dist/server.js +276 -0
- package/dist/tools/advanced.d.ts +530 -0
- package/dist/tools/advanced.js +923 -0
- package/dist/tools/audio-advanced.d.ts +847 -0
- package/dist/tools/audio-advanced.js +1653 -0
- package/dist/tools/audio.d.ts +84 -0
- package/dist/tools/audio.js +123 -0
- package/dist/tools/captions.d.ts +30 -0
- package/dist/tools/captions.js +73 -0
- package/dist/tools/clipboard.d.ts +153 -0
- package/dist/tools/clipboard.js +381 -0
- package/dist/tools/color-advanced.d.ts +821 -0
- package/dist/tools/color-advanced.js +772 -0
- package/dist/tools/discovery.d.ts +128 -0
- package/dist/tools/discovery.js +445 -0
- package/dist/tools/effects.d.ts +211 -0
- package/dist/tools/effects.js +554 -0
- package/dist/tools/export-presets.d.ts +333 -0
- package/dist/tools/export-presets.js +490 -0
- package/dist/tools/export.d.ts +320 -0
- package/dist/tools/export.js +618 -0
- package/dist/tools/health.d.ts +9 -0
- package/dist/tools/health.js +25 -0
- package/dist/tools/inspection.d.ts +179 -0
- package/dist/tools/inspection.js +1089 -0
- package/dist/tools/keyframes.d.ts +253 -0
- package/dist/tools/keyframes.js +611 -0
- package/dist/tools/markers.d.ts +117 -0
- package/dist/tools/markers.js +206 -0
- package/dist/tools/media.d.ts +297 -0
- package/dist/tools/media.js +493 -0
- package/dist/tools/metadata.d.ts +178 -0
- package/dist/tools/metadata.js +278 -0
- package/dist/tools/multicam.d.ts +199 -0
- package/dist/tools/multicam.js +212 -0
- package/dist/tools/playback.d.ts +34 -0
- package/dist/tools/playback.js +63 -0
- package/dist/tools/playhead.d.ts +77 -0
- package/dist/tools/playhead.js +185 -0
- package/dist/tools/project-manager.d.ts +65 -0
- package/dist/tools/project-manager.js +81 -0
- package/dist/tools/project.d.ts +390 -0
- package/dist/tools/project.js +596 -0
- package/dist/tools/scripting.d.ts +98 -0
- package/dist/tools/scripting.js +434 -0
- package/dist/tools/selection.d.ts +120 -0
- package/dist/tools/selection.js +334 -0
- package/dist/tools/sequence-advanced.d.ts +84 -0
- package/dist/tools/sequence-advanced.js +471 -0
- package/dist/tools/sequence-settings.d.ts +397 -0
- package/dist/tools/sequence-settings.js +967 -0
- package/dist/tools/sequence.d.ts +211 -0
- package/dist/tools/sequence.js +378 -0
- package/dist/tools/source-monitor.d.ts +95 -0
- package/dist/tools/source-monitor.js +180 -0
- package/dist/tools/text.d.ts +97 -0
- package/dist/tools/text.js +159 -0
- package/dist/tools/timeline.d.ts +265 -0
- package/dist/tools/timeline.js +575 -0
- package/dist/tools/track-management.d.ts +482 -0
- package/dist/tools/track-management.js +787 -0
- package/dist/tools/track-targeting.d.ts +666 -0
- package/dist/tools/track-targeting.js +1318 -0
- package/dist/tools/tracks.d.ts +90 -0
- package/dist/tools/tracks.js +183 -0
- package/dist/tools/transitions-motion.d.ts +788 -0
- package/dist/tools/transitions-motion.js +680 -0
- package/dist/tools/transitions.d.ts +124 -0
- package/dist/tools/transitions.js +267 -0
- package/dist/tools/utility.d.ts +469 -0
- package/dist/tools/utility.js +1118 -0
- package/dist/tools/workspace.d.ts +25 -0
- package/dist/tools/workspace.js +61 -0
- package/package.json +66 -0
- package/scripts/build-chat-dmg.sh +210 -0
- package/scripts/build-chat-zip.sh +291 -0
- package/scripts/install-cep-windows.ps1 +99 -0
- package/scripts/install-cep.sh +66 -0
- package/scripts/install-chat-plugin.bat +67 -0
- package/scripts/install-chat-plugin.sh +85 -0
- package/scripts/install-windows.bat +95 -0
- 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
|
+
})();
|