@synthetic-ai/premiere-mcp 2.1.0 → 2.3.0-dev.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 +135 -123
- package/LICENSE +21 -21
- package/README.md +242 -242
- package/cep-plugin/.debug +8 -8
- package/cep-plugin/CSInterface.js +92 -92
- package/cep-plugin/host.jsx +7 -7
- package/cep-plugin/index.html +472 -472
- package/cep-plugin/main.js +6 -0
- package/cep-plugin/premiere.jsx +24947 -24947
- package/dist/bridge/script-builder.js +378 -378
- package/dist/index.js +103 -4
- package/dist/license.js +38 -0
- package/dist/server.js +60 -60
- package/dist/tools/advanced.js +357 -357
- package/dist/tools/audio-advanced.js +1049 -1049
- package/dist/tools/audio.js +42 -42
- package/dist/tools/captions.js +27 -27
- package/dist/tools/clipboard.js +214 -214
- package/dist/tools/color-advanced.js +253 -253
- package/dist/tools/discovery.js +291 -291
- package/dist/tools/effects.js +285 -285
- package/dist/tools/export-presets.js +206 -206
- package/dist/tools/export.js +255 -255
- package/dist/tools/health.js +10 -10
- package/dist/tools/inspection.js +885 -885
- package/dist/tools/keyframes.js +369 -369
- package/dist/tools/markers.js +89 -89
- package/dist/tools/media.js +178 -178
- package/dist/tools/metadata.js +94 -94
- package/dist/tools/multicam.js +50 -50
- package/dist/tools/playback.js +15 -15
- package/dist/tools/playhead.js +95 -95
- package/dist/tools/project-manager.js +21 -21
- package/dist/tools/project.js +149 -149
- package/dist/tools/scripting.js +319 -319
- package/dist/tools/selection.js +202 -202
- package/dist/tools/sequence-advanced.js +379 -379
- package/dist/tools/sequence-settings.js +564 -564
- package/dist/tools/sequence.js +167 -167
- package/dist/tools/source-monitor.js +68 -68
- package/dist/tools/text.js +58 -58
- package/dist/tools/timeline.js +312 -312
- package/dist/tools/track-management.js +446 -446
- package/dist/tools/track-targeting.js +635 -635
- package/dist/tools/tracks.js +87 -87
- package/dist/tools/transitions-motion.js +174 -174
- package/dist/tools/transitions.js +111 -111
- package/dist/tools/utility.js +606 -606
- package/dist/tools/workspace.js +29 -29
- package/package.json +66 -66
- package/scripts/build-chat-dmg.sh +210 -210
- package/scripts/build-chat-zip.sh +291 -291
- package/scripts/install-cep-windows.ps1 +99 -99
- package/scripts/install-cep.sh +66 -66
- package/scripts/install-chat-plugin.bat +67 -67
- package/scripts/install-chat-plugin.sh +85 -85
- package/scripts/install-windows.bat +95 -95
- package/scripts/package.mjs +254 -254
|
@@ -2,389 +2,389 @@
|
|
|
2
2
|
* Builds ExtendScript strings with helper functions prepended.
|
|
3
3
|
* All generated code must be ES3-compatible (var, no arrow functions, no let/const).
|
|
4
4
|
*/
|
|
5
|
-
const HELPERS = `
|
|
6
|
-
// === MCP Bridge Helpers (auto-prepended) ===
|
|
7
|
-
|
|
8
|
-
var TICKS_PER_SECOND = 254016000000;
|
|
9
|
-
|
|
10
|
-
function __ticksToSeconds(ticks) {
|
|
11
|
-
return parseFloat(ticks) / TICKS_PER_SECOND;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function __secondsToTicks(seconds) {
|
|
15
|
-
return Math.round(parseFloat(seconds) * TICKS_PER_SECOND);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function __ticksToTimecode(ticks, fps) {
|
|
19
|
-
var totalSeconds = __ticksToSeconds(ticks);
|
|
20
|
-
var hours = Math.floor(totalSeconds / 3600);
|
|
21
|
-
var minutes = Math.floor((totalSeconds % 3600) / 60);
|
|
22
|
-
var secs = Math.floor(totalSeconds % 60);
|
|
23
|
-
var frames = Math.floor((totalSeconds % 1) * fps);
|
|
24
|
-
return __pad(hours) + ":" + __pad(minutes) + ":" + __pad(secs) + ":" + __pad(frames);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function __pad(n) {
|
|
28
|
-
return n < 10 ? "0" + n : "" + n;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function __findSequence(idOrName) {
|
|
32
|
-
var project = app.project;
|
|
33
|
-
for (var i = 0; i < project.sequences.numSequences; i++) {
|
|
34
|
-
var seq = project.sequences[i];
|
|
35
|
-
if (seq.sequenceID === idOrName || seq.name === idOrName) {
|
|
36
|
-
return seq;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
return null;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function __findProjectItem(nodeIdOrName, rootItem) {
|
|
43
|
-
if (!rootItem) rootItem = app.project.rootItem;
|
|
44
|
-
for (var i = 0; i < rootItem.children.numItems; i++) {
|
|
45
|
-
var item = rootItem.children[i];
|
|
46
|
-
if (item.nodeId === nodeIdOrName || item.name === nodeIdOrName) {
|
|
47
|
-
return item;
|
|
48
|
-
}
|
|
49
|
-
if (item.type === 2) { // Bin
|
|
50
|
-
var found = __findProjectItem(nodeIdOrName, item);
|
|
51
|
-
if (found) return found;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
return null;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function __findClip(nodeId) {
|
|
58
|
-
var seq = app.project.activeSequence;
|
|
59
|
-
if (!seq) return null;
|
|
60
|
-
|
|
61
|
-
// Search video tracks
|
|
62
|
-
for (var t = 0; t < seq.videoTracks.numTracks; t++) {
|
|
63
|
-
var track = seq.videoTracks[t];
|
|
64
|
-
for (var c = 0; c < track.clips.numItems; c++) {
|
|
65
|
-
var clip = track.clips[c];
|
|
66
|
-
if (clip.nodeId === nodeId) {
|
|
67
|
-
return { clip: clip, trackIndex: t, clipIndex: c, trackType: "video" };
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Search audio tracks
|
|
73
|
-
for (var t = 0; t < seq.audioTracks.numTracks; t++) {
|
|
74
|
-
var track = seq.audioTracks[t];
|
|
75
|
-
for (var c = 0; c < track.clips.numItems; c++) {
|
|
76
|
-
var clip = track.clips[c];
|
|
77
|
-
if (clip.nodeId === nodeId) {
|
|
78
|
-
return { clip: clip, trackIndex: t, clipIndex: c, trackType: "audio" };
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return null;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
function __getAllClips(seq) {
|
|
87
|
-
if (!seq) seq = app.project.activeSequence;
|
|
88
|
-
if (!seq) return [];
|
|
89
|
-
var clips = [];
|
|
90
|
-
|
|
91
|
-
for (var t = 0; t < seq.videoTracks.numTracks; t++) {
|
|
92
|
-
var track = seq.videoTracks[t];
|
|
93
|
-
for (var c = 0; c < track.clips.numItems; c++) {
|
|
94
|
-
var clip = track.clips[c];
|
|
95
|
-
clips.push({
|
|
96
|
-
nodeId: clip.nodeId,
|
|
97
|
-
name: clip.name,
|
|
98
|
-
trackIndex: t,
|
|
99
|
-
trackType: "video",
|
|
100
|
-
inPoint: __ticksToSeconds(clip.inPoint.ticks),
|
|
101
|
-
outPoint: __ticksToSeconds(clip.outPoint.ticks),
|
|
102
|
-
start: __ticksToSeconds(clip.start.ticks),
|
|
103
|
-
end: __ticksToSeconds(clip.end.ticks),
|
|
104
|
-
duration: __ticksToSeconds(clip.duration.ticks),
|
|
105
|
-
mediaType: clip.mediaType
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
for (var t = 0; t < seq.audioTracks.numTracks; t++) {
|
|
111
|
-
var track = seq.audioTracks[t];
|
|
112
|
-
for (var c = 0; c < track.clips.numItems; c++) {
|
|
113
|
-
var clip = track.clips[c];
|
|
114
|
-
clips.push({
|
|
115
|
-
nodeId: clip.nodeId,
|
|
116
|
-
name: clip.name,
|
|
117
|
-
trackIndex: t,
|
|
118
|
-
trackType: "audio",
|
|
119
|
-
inPoint: __ticksToSeconds(clip.inPoint.ticks),
|
|
120
|
-
outPoint: __ticksToSeconds(clip.outPoint.ticks),
|
|
121
|
-
start: __ticksToSeconds(clip.start.ticks),
|
|
122
|
-
end: __ticksToSeconds(clip.end.ticks),
|
|
123
|
-
duration: __ticksToSeconds(clip.duration.ticks),
|
|
124
|
-
mediaType: clip.mediaType
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
return clips;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// Recursively coerce undefined to null in an object tree so that the result
|
|
133
|
-
// can be passed to ExtendScript's JSON.stringify safely. ExtendScript's
|
|
134
|
-
// JSON.stringify (a) does not honor the 'replacer' parameter and (b) emits the
|
|
135
|
-
// bare token 'undefined' for undefined values, which is invalid JSON and
|
|
136
|
-
// crashes the CEP-side JSON.parse. Pre-sanitizing is the only reliable fix.
|
|
137
|
-
function __sanitizeForJSON(obj) {
|
|
138
|
-
if (obj === undefined || obj === null) return null;
|
|
139
|
-
// ExtendScript also emits NaN / Infinity literals which are not valid JSON.
|
|
140
|
-
// Coerce them to null so the CEP-side JSON.parse always succeeds.
|
|
141
|
-
if (typeof obj === "number") {
|
|
142
|
-
if (isNaN(obj) || obj === Infinity || obj === -Infinity) return null;
|
|
143
|
-
return obj;
|
|
144
|
-
}
|
|
145
|
-
if (typeof obj !== "object") return obj;
|
|
146
|
-
if (obj instanceof Array) {
|
|
147
|
-
var arr = [];
|
|
148
|
-
for (var i = 0; i < obj.length; i++) arr.push(__sanitizeForJSON(obj[i]));
|
|
149
|
-
return arr;
|
|
150
|
-
}
|
|
151
|
-
// Don't recurse into Premiere host objects (Time, ProjectItem, etc.) — they
|
|
152
|
-
// serialize as [object Object] and have inaccessible internals. Detect them
|
|
153
|
-
// by lack of plain-object constructor.
|
|
154
|
-
try {
|
|
155
|
-
if (obj.constructor && obj.constructor !== Object && obj.constructor !== Array) {
|
|
156
|
-
// For host objects, fall back to their toString / accessible primitive
|
|
157
|
-
return obj.toString ? String(obj.toString()) : "[object]";
|
|
158
|
-
}
|
|
159
|
-
} catch (e) { /* fall through */ }
|
|
160
|
-
var out = {};
|
|
161
|
-
for (var k in obj) {
|
|
162
|
-
if (obj.hasOwnProperty(k)) out[k] = __sanitizeForJSON(obj[k]);
|
|
163
|
-
}
|
|
164
|
-
return out;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
function __jsonStringify(obj) {
|
|
168
|
-
// ES3-compatible JSON stringify. ExtendScript's JSON.stringify emits the
|
|
169
|
-
// literal token 'undefined' for undefined values (vs Node/Browser which omit
|
|
170
|
-
// them) AND ignores the replacer parameter, making the produced string
|
|
171
|
-
// invalid JSON and crashing the CEP-side JSON.parse. Sanitize first.
|
|
172
|
-
if (typeof JSON !== "undefined" && JSON.stringify) {
|
|
173
|
-
return JSON.stringify(__sanitizeForJSON(obj));
|
|
174
|
-
}
|
|
175
|
-
// Fallback for very old ExtendScript
|
|
176
|
-
if (obj === null) return "null";
|
|
177
|
-
if (obj === undefined) return "undefined";
|
|
178
|
-
if (typeof obj === "string") return '"' + obj.replace(/\\\\/g, "\\\\\\\\").replace(/"/g, '\\\\"').replace(/\\n/g, "\\\\n") + '"';
|
|
179
|
-
if (typeof obj === "number" || typeof obj === "boolean") return String(obj);
|
|
180
|
-
if (obj instanceof Array) {
|
|
181
|
-
var arr = [];
|
|
182
|
-
for (var i = 0; i < obj.length; i++) {
|
|
183
|
-
arr.push(__jsonStringify(obj[i]));
|
|
184
|
-
}
|
|
185
|
-
return "[" + arr.join(",") + "]";
|
|
186
|
-
}
|
|
187
|
-
if (typeof obj === "object") {
|
|
188
|
-
var parts = [];
|
|
189
|
-
for (var k in obj) {
|
|
190
|
-
if (obj.hasOwnProperty(k)) {
|
|
191
|
-
parts.push(__jsonStringify(k) + ":" + __jsonStringify(obj[k]));
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
return "{" + parts.join(",") + "}";
|
|
195
|
-
}
|
|
196
|
-
return String(obj);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
function __result(data) {
|
|
200
|
-
return __jsonStringify({ success: true, data: data });
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
function __error(msg) {
|
|
204
|
-
return __jsonStringify({ success: false, error: String(msg) });
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// --- Locale & version resilient lookups -------------------------------------
|
|
208
|
-
|
|
209
|
-
// Candidate matchNames for Premiere's intrinsic (fixed) effects. matchName is
|
|
210
|
-
// locale-INDEPENDENT, so trying these first makes tools work on any language
|
|
211
|
-
// install. displayName fallbacks are language-specific and only a last resort.
|
|
212
|
-
var __INTRINSIC = {
|
|
213
|
-
motion: { match: ["AE.ADBE Motion", "ADBE Motion", "motionControlEffect"], display: ["Motion", "Movimiento", "Bewegung", "Mouvement"] },
|
|
214
|
-
opacity: { match: ["AE.ADBE Opacity", "ADBE Opacity", "opacityEffect"], display: ["Opacity", "Opacidad", "Deckkraft", "Opacité"] },
|
|
215
|
-
volume: { match: ["AE.ADBE Audio Levels", "audioVolume", "audioGain", "ADBE Audio Levels"], display: ["Volume", "Volumen", "Lautstärke"] },
|
|
216
|
-
panner: { match: ["AE.ADBE Pan", "audioPanner", "ADBE Pan"], display: ["Panner", "Panoramización", "Balance"] },
|
|
217
|
-
lumetri: { match: ["AE.ADBE Lumetri", "ADBE Lumetri", "PR.ADBE Lumetri"], display: ["Lumetri Color", "Color Lumetri", "Lumetri-Farbe"] },
|
|
218
|
-
timeRemap:{ match: ["AE.ADBE Time Remapping", "ADBE Time Remapping"], display: ["Time Remapping", "Reasignación de tiempo"] }
|
|
219
|
-
};
|
|
220
|
-
|
|
221
|
-
// Find a component on a clip by trying matchName candidates then displayName candidates.
|
|
222
|
-
// matchNames/displayNames are arrays of strings. Returns the component or null.
|
|
223
|
-
function __findComponent(clip, matchNames, displayNames) {
|
|
224
|
-
if (!clip || !clip.components) return null;
|
|
225
|
-
var i, c, k;
|
|
226
|
-
if (matchNames) {
|
|
227
|
-
for (i = 0; i < clip.components.numItems; i++) {
|
|
228
|
-
c = clip.components[i];
|
|
229
|
-
for (k = 0; k < matchNames.length; k++) {
|
|
230
|
-
if (c.matchName === matchNames[k]) return c;
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
if (displayNames) {
|
|
235
|
-
for (i = 0; i < clip.components.numItems; i++) {
|
|
236
|
-
c = clip.components[i];
|
|
237
|
-
for (k = 0; k < displayNames.length; k++) {
|
|
238
|
-
if (c.displayName === displayNames[k]) return c;
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
return null;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
// Shorthand: find an intrinsic component by key from the __INTRINSIC table.
|
|
246
|
-
function __findIntrinsic(clip, key) {
|
|
247
|
-
var def = __INTRINSIC[key];
|
|
248
|
-
if (!def) return null;
|
|
249
|
-
return __findComponent(clip, def.match, def.display);
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
// Find a property inside a component by displayName candidates, descending one
|
|
253
|
-
// level into nested property groups (needed for Lumetri's grouped params).
|
|
254
|
-
function __findProp(comp, names) {
|
|
255
|
-
if (!comp || !comp.properties) return null;
|
|
256
|
-
var p, n, prop, sp, sub;
|
|
257
|
-
for (p = 0; p < comp.properties.numItems; p++) {
|
|
258
|
-
prop = comp.properties[p];
|
|
259
|
-
for (n = 0; n < names.length; n++) {
|
|
260
|
-
if (prop.displayName === names[n]) return prop;
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
// second pass: nested groups
|
|
264
|
-
for (p = 0; p < comp.properties.numItems; p++) {
|
|
265
|
-
prop = comp.properties[p];
|
|
266
|
-
if (prop.properties && prop.properties.numItems) {
|
|
267
|
-
for (sp = 0; sp < prop.properties.numItems; sp++) {
|
|
268
|
-
sub = prop.properties[sp];
|
|
269
|
-
for (n = 0; n < names.length; n++) {
|
|
270
|
-
if (sub.displayName === names[n]) return sub;
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
return null;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
// Locale-aware aliases for common Premiere Pro transition names. matchName
|
|
279
|
-
// would be ideal but QE's transition objects don't reliably expose it, so we
|
|
280
|
-
// map the English canonical name to localized displayNames seen in the wild.
|
|
281
|
-
var __TRANSITION_ALIASES = {
|
|
282
|
-
"Cross Dissolve": ["Cross Dissolve", "Disolvencia cruzada", "Fondu enchaîné", "Weiche Blende"],
|
|
283
|
-
"Dip to Black": ["Dip to Black", "Inmersión a negro", "Fondu au noir", "Schwarzblende"],
|
|
284
|
-
"Dip to White": ["Dip to White", "Inmersión a blanco", "Fondu au blanc", "Weißblende"],
|
|
285
|
-
"Film Dissolve": ["Film Dissolve", "Disolvencia de película"],
|
|
286
|
-
"Additive Dissolve": ["Additive Dissolve", "Disolvencia aditiva"],
|
|
287
|
-
"Morph Cut": ["Morph Cut", "Corte por transformación"],
|
|
288
|
-
"Constant Power": ["Constant Power", "Potencia constante"],
|
|
289
|
-
"Constant Gain": ["Constant Gain", "Ganancia constante"],
|
|
290
|
-
"Exponential Fade": ["Exponential Fade", "Fundido exponencial"]
|
|
291
|
-
};
|
|
292
|
-
|
|
293
|
-
// Resolve a transition by name from a QE transition list, locale-resilient.
|
|
294
|
-
// First tries qe.project.getVideoTransitionByName / getAudioTransitionByName,
|
|
295
|
-
// then iterates the list matching the requested name OR any localized alias.
|
|
296
|
-
// kind: "video" | "audio". Returns the QE transition object or null.
|
|
297
|
-
function __findTransition(name, kind) {
|
|
298
|
-
try { app.enableQE(); } catch (e) {}
|
|
299
|
-
if (typeof qe === "undefined" || !qe.project) return null;
|
|
300
|
-
var direct = null;
|
|
301
|
-
try {
|
|
302
|
-
if (kind === "audio" && qe.project.getAudioTransitionByName) {
|
|
303
|
-
direct = qe.project.getAudioTransitionByName(name);
|
|
304
|
-
} else if (qe.project.getVideoTransitionByName) {
|
|
305
|
-
direct = qe.project.getVideoTransitionByName(name);
|
|
306
|
-
}
|
|
307
|
-
} catch (e) {}
|
|
308
|
-
if (direct) return direct;
|
|
309
|
-
var list = null;
|
|
310
|
-
try {
|
|
311
|
-
list = (kind === "audio") ? qe.project.getAudioTransitionList() : qe.project.getVideoTransitionList();
|
|
312
|
-
} catch (e) { list = null; }
|
|
313
|
-
if (!list || !list.numItems) return null;
|
|
314
|
-
// Candidate names: the requested name, plus aliases for it (forward map),
|
|
315
|
-
// plus the canonical name if the request matches any alias (reverse map).
|
|
316
|
-
var candidates = [name];
|
|
317
|
-
var k, j, aliases;
|
|
318
|
-
if (__TRANSITION_ALIASES[name]) {
|
|
319
|
-
aliases = __TRANSITION_ALIASES[name];
|
|
320
|
-
for (j = 0; j < aliases.length; j++) candidates.push(aliases[j]);
|
|
321
|
-
}
|
|
322
|
-
for (k in __TRANSITION_ALIASES) {
|
|
323
|
-
if (__TRANSITION_ALIASES.hasOwnProperty(k)) {
|
|
324
|
-
aliases = __TRANSITION_ALIASES[k];
|
|
325
|
-
for (j = 0; j < aliases.length; j++) {
|
|
326
|
-
if (aliases[j] === name) {
|
|
327
|
-
candidates.push(k);
|
|
328
|
-
for (var jj = 0; jj < aliases.length; jj++) candidates.push(aliases[jj]);
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
for (var i = 0; i < list.numItems; i++) {
|
|
334
|
-
var t = list[i];
|
|
335
|
-
var tName = t.name;
|
|
336
|
-
var tMatch = t.matchName;
|
|
337
|
-
for (var c = 0; c < candidates.length; c++) {
|
|
338
|
-
if (tName === candidates[c]) return t;
|
|
339
|
-
if (tMatch && tMatch === candidates[c]) return t;
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
return null;
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
// Resolve an ID that could be either a project item nodeId/name OR a timeline
|
|
346
|
-
// clip nodeId, to a ProjectItem. Used by tools whose schema says "project item
|
|
347
|
-
// node_id or name" but where callers commonly pass timeline clip nodeIds
|
|
348
|
-
// (which come from get_active_sequence / list_sequence_tracks).
|
|
349
|
-
// Returns the ProjectItem or null.
|
|
350
|
-
function __resolveProjectItemOrTimelineClip(idOrName) {
|
|
351
|
-
if (!idOrName) return null;
|
|
352
|
-
// First try the project tree
|
|
353
|
-
var pi = __findProjectItem(idOrName);
|
|
354
|
-
if (pi) return pi;
|
|
355
|
-
// Fall back: treat as timeline clip nodeId, resolve to its projectItem
|
|
356
|
-
try {
|
|
357
|
-
var r = __findClip(idOrName);
|
|
358
|
-
if (r && r.clip && r.clip.projectItem) return r.clip.projectItem;
|
|
359
|
-
} catch (e) {}
|
|
360
|
-
return null;
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
// Read a member that some Premiere versions expose as a property and others as
|
|
364
|
-
// a zero-arg method (e.g. isMuted, isSelected, isOffline, isSequence). Returns
|
|
365
|
-
// the resolved value, or the provided default if it cannot be read.
|
|
366
|
-
function __readMaybe(obj, name, dflt) {
|
|
367
|
-
try {
|
|
368
|
-
var v = obj[name];
|
|
369
|
-
if (typeof v === "function") return v.call(obj);
|
|
370
|
-
if (v === undefined) return dflt;
|
|
371
|
-
return v;
|
|
372
|
-
} catch(e) { return dflt; }
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
// === End MCP Bridge Helpers ===
|
|
5
|
+
const HELPERS = `
|
|
6
|
+
// === MCP Bridge Helpers (auto-prepended) ===
|
|
7
|
+
|
|
8
|
+
var TICKS_PER_SECOND = 254016000000;
|
|
9
|
+
|
|
10
|
+
function __ticksToSeconds(ticks) {
|
|
11
|
+
return parseFloat(ticks) / TICKS_PER_SECOND;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function __secondsToTicks(seconds) {
|
|
15
|
+
return Math.round(parseFloat(seconds) * TICKS_PER_SECOND);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function __ticksToTimecode(ticks, fps) {
|
|
19
|
+
var totalSeconds = __ticksToSeconds(ticks);
|
|
20
|
+
var hours = Math.floor(totalSeconds / 3600);
|
|
21
|
+
var minutes = Math.floor((totalSeconds % 3600) / 60);
|
|
22
|
+
var secs = Math.floor(totalSeconds % 60);
|
|
23
|
+
var frames = Math.floor((totalSeconds % 1) * fps);
|
|
24
|
+
return __pad(hours) + ":" + __pad(minutes) + ":" + __pad(secs) + ":" + __pad(frames);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function __pad(n) {
|
|
28
|
+
return n < 10 ? "0" + n : "" + n;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function __findSequence(idOrName) {
|
|
32
|
+
var project = app.project;
|
|
33
|
+
for (var i = 0; i < project.sequences.numSequences; i++) {
|
|
34
|
+
var seq = project.sequences[i];
|
|
35
|
+
if (seq.sequenceID === idOrName || seq.name === idOrName) {
|
|
36
|
+
return seq;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function __findProjectItem(nodeIdOrName, rootItem) {
|
|
43
|
+
if (!rootItem) rootItem = app.project.rootItem;
|
|
44
|
+
for (var i = 0; i < rootItem.children.numItems; i++) {
|
|
45
|
+
var item = rootItem.children[i];
|
|
46
|
+
if (item.nodeId === nodeIdOrName || item.name === nodeIdOrName) {
|
|
47
|
+
return item;
|
|
48
|
+
}
|
|
49
|
+
if (item.type === 2) { // Bin
|
|
50
|
+
var found = __findProjectItem(nodeIdOrName, item);
|
|
51
|
+
if (found) return found;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function __findClip(nodeId) {
|
|
58
|
+
var seq = app.project.activeSequence;
|
|
59
|
+
if (!seq) return null;
|
|
60
|
+
|
|
61
|
+
// Search video tracks
|
|
62
|
+
for (var t = 0; t < seq.videoTracks.numTracks; t++) {
|
|
63
|
+
var track = seq.videoTracks[t];
|
|
64
|
+
for (var c = 0; c < track.clips.numItems; c++) {
|
|
65
|
+
var clip = track.clips[c];
|
|
66
|
+
if (clip.nodeId === nodeId) {
|
|
67
|
+
return { clip: clip, trackIndex: t, clipIndex: c, trackType: "video" };
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Search audio tracks
|
|
73
|
+
for (var t = 0; t < seq.audioTracks.numTracks; t++) {
|
|
74
|
+
var track = seq.audioTracks[t];
|
|
75
|
+
for (var c = 0; c < track.clips.numItems; c++) {
|
|
76
|
+
var clip = track.clips[c];
|
|
77
|
+
if (clip.nodeId === nodeId) {
|
|
78
|
+
return { clip: clip, trackIndex: t, clipIndex: c, trackType: "audio" };
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function __getAllClips(seq) {
|
|
87
|
+
if (!seq) seq = app.project.activeSequence;
|
|
88
|
+
if (!seq) return [];
|
|
89
|
+
var clips = [];
|
|
90
|
+
|
|
91
|
+
for (var t = 0; t < seq.videoTracks.numTracks; t++) {
|
|
92
|
+
var track = seq.videoTracks[t];
|
|
93
|
+
for (var c = 0; c < track.clips.numItems; c++) {
|
|
94
|
+
var clip = track.clips[c];
|
|
95
|
+
clips.push({
|
|
96
|
+
nodeId: clip.nodeId,
|
|
97
|
+
name: clip.name,
|
|
98
|
+
trackIndex: t,
|
|
99
|
+
trackType: "video",
|
|
100
|
+
inPoint: __ticksToSeconds(clip.inPoint.ticks),
|
|
101
|
+
outPoint: __ticksToSeconds(clip.outPoint.ticks),
|
|
102
|
+
start: __ticksToSeconds(clip.start.ticks),
|
|
103
|
+
end: __ticksToSeconds(clip.end.ticks),
|
|
104
|
+
duration: __ticksToSeconds(clip.duration.ticks),
|
|
105
|
+
mediaType: clip.mediaType
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
for (var t = 0; t < seq.audioTracks.numTracks; t++) {
|
|
111
|
+
var track = seq.audioTracks[t];
|
|
112
|
+
for (var c = 0; c < track.clips.numItems; c++) {
|
|
113
|
+
var clip = track.clips[c];
|
|
114
|
+
clips.push({
|
|
115
|
+
nodeId: clip.nodeId,
|
|
116
|
+
name: clip.name,
|
|
117
|
+
trackIndex: t,
|
|
118
|
+
trackType: "audio",
|
|
119
|
+
inPoint: __ticksToSeconds(clip.inPoint.ticks),
|
|
120
|
+
outPoint: __ticksToSeconds(clip.outPoint.ticks),
|
|
121
|
+
start: __ticksToSeconds(clip.start.ticks),
|
|
122
|
+
end: __ticksToSeconds(clip.end.ticks),
|
|
123
|
+
duration: __ticksToSeconds(clip.duration.ticks),
|
|
124
|
+
mediaType: clip.mediaType
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return clips;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Recursively coerce undefined to null in an object tree so that the result
|
|
133
|
+
// can be passed to ExtendScript's JSON.stringify safely. ExtendScript's
|
|
134
|
+
// JSON.stringify (a) does not honor the 'replacer' parameter and (b) emits the
|
|
135
|
+
// bare token 'undefined' for undefined values, which is invalid JSON and
|
|
136
|
+
// crashes the CEP-side JSON.parse. Pre-sanitizing is the only reliable fix.
|
|
137
|
+
function __sanitizeForJSON(obj) {
|
|
138
|
+
if (obj === undefined || obj === null) return null;
|
|
139
|
+
// ExtendScript also emits NaN / Infinity literals which are not valid JSON.
|
|
140
|
+
// Coerce them to null so the CEP-side JSON.parse always succeeds.
|
|
141
|
+
if (typeof obj === "number") {
|
|
142
|
+
if (isNaN(obj) || obj === Infinity || obj === -Infinity) return null;
|
|
143
|
+
return obj;
|
|
144
|
+
}
|
|
145
|
+
if (typeof obj !== "object") return obj;
|
|
146
|
+
if (obj instanceof Array) {
|
|
147
|
+
var arr = [];
|
|
148
|
+
for (var i = 0; i < obj.length; i++) arr.push(__sanitizeForJSON(obj[i]));
|
|
149
|
+
return arr;
|
|
150
|
+
}
|
|
151
|
+
// Don't recurse into Premiere host objects (Time, ProjectItem, etc.) — they
|
|
152
|
+
// serialize as [object Object] and have inaccessible internals. Detect them
|
|
153
|
+
// by lack of plain-object constructor.
|
|
154
|
+
try {
|
|
155
|
+
if (obj.constructor && obj.constructor !== Object && obj.constructor !== Array) {
|
|
156
|
+
// For host objects, fall back to their toString / accessible primitive
|
|
157
|
+
return obj.toString ? String(obj.toString()) : "[object]";
|
|
158
|
+
}
|
|
159
|
+
} catch (e) { /* fall through */ }
|
|
160
|
+
var out = {};
|
|
161
|
+
for (var k in obj) {
|
|
162
|
+
if (obj.hasOwnProperty(k)) out[k] = __sanitizeForJSON(obj[k]);
|
|
163
|
+
}
|
|
164
|
+
return out;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function __jsonStringify(obj) {
|
|
168
|
+
// ES3-compatible JSON stringify. ExtendScript's JSON.stringify emits the
|
|
169
|
+
// literal token 'undefined' for undefined values (vs Node/Browser which omit
|
|
170
|
+
// them) AND ignores the replacer parameter, making the produced string
|
|
171
|
+
// invalid JSON and crashing the CEP-side JSON.parse. Sanitize first.
|
|
172
|
+
if (typeof JSON !== "undefined" && JSON.stringify) {
|
|
173
|
+
return JSON.stringify(__sanitizeForJSON(obj));
|
|
174
|
+
}
|
|
175
|
+
// Fallback for very old ExtendScript
|
|
176
|
+
if (obj === null) return "null";
|
|
177
|
+
if (obj === undefined) return "undefined";
|
|
178
|
+
if (typeof obj === "string") return '"' + obj.replace(/\\\\/g, "\\\\\\\\").replace(/"/g, '\\\\"').replace(/\\n/g, "\\\\n") + '"';
|
|
179
|
+
if (typeof obj === "number" || typeof obj === "boolean") return String(obj);
|
|
180
|
+
if (obj instanceof Array) {
|
|
181
|
+
var arr = [];
|
|
182
|
+
for (var i = 0; i < obj.length; i++) {
|
|
183
|
+
arr.push(__jsonStringify(obj[i]));
|
|
184
|
+
}
|
|
185
|
+
return "[" + arr.join(",") + "]";
|
|
186
|
+
}
|
|
187
|
+
if (typeof obj === "object") {
|
|
188
|
+
var parts = [];
|
|
189
|
+
for (var k in obj) {
|
|
190
|
+
if (obj.hasOwnProperty(k)) {
|
|
191
|
+
parts.push(__jsonStringify(k) + ":" + __jsonStringify(obj[k]));
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return "{" + parts.join(",") + "}";
|
|
195
|
+
}
|
|
196
|
+
return String(obj);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function __result(data) {
|
|
200
|
+
return __jsonStringify({ success: true, data: data });
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function __error(msg) {
|
|
204
|
+
return __jsonStringify({ success: false, error: String(msg) });
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// --- Locale & version resilient lookups -------------------------------------
|
|
208
|
+
|
|
209
|
+
// Candidate matchNames for Premiere's intrinsic (fixed) effects. matchName is
|
|
210
|
+
// locale-INDEPENDENT, so trying these first makes tools work on any language
|
|
211
|
+
// install. displayName fallbacks are language-specific and only a last resort.
|
|
212
|
+
var __INTRINSIC = {
|
|
213
|
+
motion: { match: ["AE.ADBE Motion", "ADBE Motion", "motionControlEffect"], display: ["Motion", "Movimiento", "Bewegung", "Mouvement"] },
|
|
214
|
+
opacity: { match: ["AE.ADBE Opacity", "ADBE Opacity", "opacityEffect"], display: ["Opacity", "Opacidad", "Deckkraft", "Opacité"] },
|
|
215
|
+
volume: { match: ["AE.ADBE Audio Levels", "audioVolume", "audioGain", "ADBE Audio Levels"], display: ["Volume", "Volumen", "Lautstärke"] },
|
|
216
|
+
panner: { match: ["AE.ADBE Pan", "audioPanner", "ADBE Pan"], display: ["Panner", "Panoramización", "Balance"] },
|
|
217
|
+
lumetri: { match: ["AE.ADBE Lumetri", "ADBE Lumetri", "PR.ADBE Lumetri"], display: ["Lumetri Color", "Color Lumetri", "Lumetri-Farbe"] },
|
|
218
|
+
timeRemap:{ match: ["AE.ADBE Time Remapping", "ADBE Time Remapping"], display: ["Time Remapping", "Reasignación de tiempo"] }
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
// Find a component on a clip by trying matchName candidates then displayName candidates.
|
|
222
|
+
// matchNames/displayNames are arrays of strings. Returns the component or null.
|
|
223
|
+
function __findComponent(clip, matchNames, displayNames) {
|
|
224
|
+
if (!clip || !clip.components) return null;
|
|
225
|
+
var i, c, k;
|
|
226
|
+
if (matchNames) {
|
|
227
|
+
for (i = 0; i < clip.components.numItems; i++) {
|
|
228
|
+
c = clip.components[i];
|
|
229
|
+
for (k = 0; k < matchNames.length; k++) {
|
|
230
|
+
if (c.matchName === matchNames[k]) return c;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
if (displayNames) {
|
|
235
|
+
for (i = 0; i < clip.components.numItems; i++) {
|
|
236
|
+
c = clip.components[i];
|
|
237
|
+
for (k = 0; k < displayNames.length; k++) {
|
|
238
|
+
if (c.displayName === displayNames[k]) return c;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Shorthand: find an intrinsic component by key from the __INTRINSIC table.
|
|
246
|
+
function __findIntrinsic(clip, key) {
|
|
247
|
+
var def = __INTRINSIC[key];
|
|
248
|
+
if (!def) return null;
|
|
249
|
+
return __findComponent(clip, def.match, def.display);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Find a property inside a component by displayName candidates, descending one
|
|
253
|
+
// level into nested property groups (needed for Lumetri's grouped params).
|
|
254
|
+
function __findProp(comp, names) {
|
|
255
|
+
if (!comp || !comp.properties) return null;
|
|
256
|
+
var p, n, prop, sp, sub;
|
|
257
|
+
for (p = 0; p < comp.properties.numItems; p++) {
|
|
258
|
+
prop = comp.properties[p];
|
|
259
|
+
for (n = 0; n < names.length; n++) {
|
|
260
|
+
if (prop.displayName === names[n]) return prop;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
// second pass: nested groups
|
|
264
|
+
for (p = 0; p < comp.properties.numItems; p++) {
|
|
265
|
+
prop = comp.properties[p];
|
|
266
|
+
if (prop.properties && prop.properties.numItems) {
|
|
267
|
+
for (sp = 0; sp < prop.properties.numItems; sp++) {
|
|
268
|
+
sub = prop.properties[sp];
|
|
269
|
+
for (n = 0; n < names.length; n++) {
|
|
270
|
+
if (sub.displayName === names[n]) return sub;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
return null;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Locale-aware aliases for common Premiere Pro transition names. matchName
|
|
279
|
+
// would be ideal but QE's transition objects don't reliably expose it, so we
|
|
280
|
+
// map the English canonical name to localized displayNames seen in the wild.
|
|
281
|
+
var __TRANSITION_ALIASES = {
|
|
282
|
+
"Cross Dissolve": ["Cross Dissolve", "Disolvencia cruzada", "Fondu enchaîné", "Weiche Blende"],
|
|
283
|
+
"Dip to Black": ["Dip to Black", "Inmersión a negro", "Fondu au noir", "Schwarzblende"],
|
|
284
|
+
"Dip to White": ["Dip to White", "Inmersión a blanco", "Fondu au blanc", "Weißblende"],
|
|
285
|
+
"Film Dissolve": ["Film Dissolve", "Disolvencia de película"],
|
|
286
|
+
"Additive Dissolve": ["Additive Dissolve", "Disolvencia aditiva"],
|
|
287
|
+
"Morph Cut": ["Morph Cut", "Corte por transformación"],
|
|
288
|
+
"Constant Power": ["Constant Power", "Potencia constante"],
|
|
289
|
+
"Constant Gain": ["Constant Gain", "Ganancia constante"],
|
|
290
|
+
"Exponential Fade": ["Exponential Fade", "Fundido exponencial"]
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
// Resolve a transition by name from a QE transition list, locale-resilient.
|
|
294
|
+
// First tries qe.project.getVideoTransitionByName / getAudioTransitionByName,
|
|
295
|
+
// then iterates the list matching the requested name OR any localized alias.
|
|
296
|
+
// kind: "video" | "audio". Returns the QE transition object or null.
|
|
297
|
+
function __findTransition(name, kind) {
|
|
298
|
+
try { app.enableQE(); } catch (e) {}
|
|
299
|
+
if (typeof qe === "undefined" || !qe.project) return null;
|
|
300
|
+
var direct = null;
|
|
301
|
+
try {
|
|
302
|
+
if (kind === "audio" && qe.project.getAudioTransitionByName) {
|
|
303
|
+
direct = qe.project.getAudioTransitionByName(name);
|
|
304
|
+
} else if (qe.project.getVideoTransitionByName) {
|
|
305
|
+
direct = qe.project.getVideoTransitionByName(name);
|
|
306
|
+
}
|
|
307
|
+
} catch (e) {}
|
|
308
|
+
if (direct) return direct;
|
|
309
|
+
var list = null;
|
|
310
|
+
try {
|
|
311
|
+
list = (kind === "audio") ? qe.project.getAudioTransitionList() : qe.project.getVideoTransitionList();
|
|
312
|
+
} catch (e) { list = null; }
|
|
313
|
+
if (!list || !list.numItems) return null;
|
|
314
|
+
// Candidate names: the requested name, plus aliases for it (forward map),
|
|
315
|
+
// plus the canonical name if the request matches any alias (reverse map).
|
|
316
|
+
var candidates = [name];
|
|
317
|
+
var k, j, aliases;
|
|
318
|
+
if (__TRANSITION_ALIASES[name]) {
|
|
319
|
+
aliases = __TRANSITION_ALIASES[name];
|
|
320
|
+
for (j = 0; j < aliases.length; j++) candidates.push(aliases[j]);
|
|
321
|
+
}
|
|
322
|
+
for (k in __TRANSITION_ALIASES) {
|
|
323
|
+
if (__TRANSITION_ALIASES.hasOwnProperty(k)) {
|
|
324
|
+
aliases = __TRANSITION_ALIASES[k];
|
|
325
|
+
for (j = 0; j < aliases.length; j++) {
|
|
326
|
+
if (aliases[j] === name) {
|
|
327
|
+
candidates.push(k);
|
|
328
|
+
for (var jj = 0; jj < aliases.length; jj++) candidates.push(aliases[jj]);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
for (var i = 0; i < list.numItems; i++) {
|
|
334
|
+
var t = list[i];
|
|
335
|
+
var tName = t.name;
|
|
336
|
+
var tMatch = t.matchName;
|
|
337
|
+
for (var c = 0; c < candidates.length; c++) {
|
|
338
|
+
if (tName === candidates[c]) return t;
|
|
339
|
+
if (tMatch && tMatch === candidates[c]) return t;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
return null;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// Resolve an ID that could be either a project item nodeId/name OR a timeline
|
|
346
|
+
// clip nodeId, to a ProjectItem. Used by tools whose schema says "project item
|
|
347
|
+
// node_id or name" but where callers commonly pass timeline clip nodeIds
|
|
348
|
+
// (which come from get_active_sequence / list_sequence_tracks).
|
|
349
|
+
// Returns the ProjectItem or null.
|
|
350
|
+
function __resolveProjectItemOrTimelineClip(idOrName) {
|
|
351
|
+
if (!idOrName) return null;
|
|
352
|
+
// First try the project tree
|
|
353
|
+
var pi = __findProjectItem(idOrName);
|
|
354
|
+
if (pi) return pi;
|
|
355
|
+
// Fall back: treat as timeline clip nodeId, resolve to its projectItem
|
|
356
|
+
try {
|
|
357
|
+
var r = __findClip(idOrName);
|
|
358
|
+
if (r && r.clip && r.clip.projectItem) return r.clip.projectItem;
|
|
359
|
+
} catch (e) {}
|
|
360
|
+
return null;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// Read a member that some Premiere versions expose as a property and others as
|
|
364
|
+
// a zero-arg method (e.g. isMuted, isSelected, isOffline, isSequence). Returns
|
|
365
|
+
// the resolved value, or the provided default if it cannot be read.
|
|
366
|
+
function __readMaybe(obj, name, dflt) {
|
|
367
|
+
try {
|
|
368
|
+
var v = obj[name];
|
|
369
|
+
if (typeof v === "function") return v.call(obj);
|
|
370
|
+
if (v === undefined) return dflt;
|
|
371
|
+
return v;
|
|
372
|
+
} catch(e) { return dflt; }
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// === End MCP Bridge Helpers ===
|
|
376
376
|
`;
|
|
377
377
|
/**
|
|
378
378
|
* Build a complete ExtendScript by wrapping user code in an IIFE with helpers.
|
|
379
379
|
*/
|
|
380
380
|
export function buildScript(code) {
|
|
381
|
-
return `${HELPERS}
|
|
382
|
-
(function() {
|
|
383
|
-
try {
|
|
384
|
-
${code}
|
|
385
|
-
} catch(e) {
|
|
386
|
-
return __error(e.toString());
|
|
387
|
-
}
|
|
381
|
+
return `${HELPERS}
|
|
382
|
+
(function() {
|
|
383
|
+
try {
|
|
384
|
+
${code}
|
|
385
|
+
} catch(e) {
|
|
386
|
+
return __error(e.toString());
|
|
387
|
+
}
|
|
388
388
|
})();`;
|
|
389
389
|
}
|
|
390
390
|
/**
|