@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,611 @@
|
|
|
1
|
+
import { buildToolScript, escapeForExtendScript } from "../bridge/script-builder.js";
|
|
2
|
+
import { sendCommand } from "../bridge/file-bridge.js";
|
|
3
|
+
export function getKeyframeTools(bridgeOptions) {
|
|
4
|
+
return {
|
|
5
|
+
get_effect_properties: {
|
|
6
|
+
description: "List all properties of a specific effect on a clip, including current values",
|
|
7
|
+
parameters: {
|
|
8
|
+
type: "object",
|
|
9
|
+
properties: {
|
|
10
|
+
node_id: {
|
|
11
|
+
type: "string",
|
|
12
|
+
description: "Node ID of the clip",
|
|
13
|
+
},
|
|
14
|
+
effect_name: {
|
|
15
|
+
type: "string",
|
|
16
|
+
description: "Display name of the effect (e.g., 'Motion', 'Opacity', 'Lumetri Color')",
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
required: ["node_id", "effect_name"],
|
|
20
|
+
},
|
|
21
|
+
handler: async (args) => {
|
|
22
|
+
const script = buildToolScript(`
|
|
23
|
+
var result = __findClip("${escapeForExtendScript(args.node_id)}");
|
|
24
|
+
if (!result) return __error("Clip not found");
|
|
25
|
+
|
|
26
|
+
var clip = result.clip;
|
|
27
|
+
var effectName = "${escapeForExtendScript(args.effect_name)}";
|
|
28
|
+
var comp = null;
|
|
29
|
+
|
|
30
|
+
for (var i = 0; i < clip.components.numItems; i++) {
|
|
31
|
+
if (clip.components[i].displayName === effectName || clip.components[i].matchName === effectName) {
|
|
32
|
+
comp = clip.components[i];
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (!comp) return __error("Effect not found: " + effectName);
|
|
38
|
+
|
|
39
|
+
var props = [];
|
|
40
|
+
for (var p = 0; p < comp.properties.numItems; p++) {
|
|
41
|
+
var prop = comp.properties[p];
|
|
42
|
+
var info = {
|
|
43
|
+
index: p,
|
|
44
|
+
displayName: prop.displayName,
|
|
45
|
+
isTimeVarying: false,
|
|
46
|
+
keyframesSupported: false
|
|
47
|
+
};
|
|
48
|
+
try { info.isTimeVarying = prop.isTimeVarying(); } catch(e) {}
|
|
49
|
+
try { info.keyframesSupported = prop.areKeyframesSupported(); } catch(e) {}
|
|
50
|
+
try { info.value = prop.getValue(0, 0); } catch(e) {}
|
|
51
|
+
props.push(info);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return __result({
|
|
55
|
+
effect: comp.displayName,
|
|
56
|
+
matchName: comp.matchName,
|
|
57
|
+
properties: props
|
|
58
|
+
});
|
|
59
|
+
`);
|
|
60
|
+
return sendCommand(script, bridgeOptions);
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
set_effect_property: {
|
|
64
|
+
description: "Set the value of a specific effect property on a clip",
|
|
65
|
+
parameters: {
|
|
66
|
+
type: "object",
|
|
67
|
+
properties: {
|
|
68
|
+
node_id: {
|
|
69
|
+
type: "string",
|
|
70
|
+
description: "Node ID of the clip",
|
|
71
|
+
},
|
|
72
|
+
effect_name: {
|
|
73
|
+
type: "string",
|
|
74
|
+
description: "Display name of the effect (e.g., 'Motion', 'Opacity')",
|
|
75
|
+
},
|
|
76
|
+
property_name: {
|
|
77
|
+
type: "string",
|
|
78
|
+
description: "Display name of the property (e.g., 'Scale', 'Position', 'Opacity')",
|
|
79
|
+
},
|
|
80
|
+
value: {
|
|
81
|
+
type: "number",
|
|
82
|
+
description: "Value to set",
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
required: ["node_id", "effect_name", "property_name", "value"],
|
|
86
|
+
},
|
|
87
|
+
handler: async (args) => {
|
|
88
|
+
const script = buildToolScript(`
|
|
89
|
+
var result = __findClip("${escapeForExtendScript(args.node_id)}");
|
|
90
|
+
if (!result) return __error("Clip not found");
|
|
91
|
+
|
|
92
|
+
var clip = result.clip;
|
|
93
|
+
var comp = null;
|
|
94
|
+
for (var i = 0; i < clip.components.numItems; i++) {
|
|
95
|
+
if (clip.components[i].displayName === "${escapeForExtendScript(args.effect_name)}" || clip.components[i].matchName === "${escapeForExtendScript(args.effect_name)}") {
|
|
96
|
+
comp = clip.components[i];
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (!comp) return __error("Effect not found: ${escapeForExtendScript(args.effect_name)}");
|
|
101
|
+
|
|
102
|
+
var prop = null;
|
|
103
|
+
for (var p = 0; p < comp.properties.numItems; p++) {
|
|
104
|
+
if (comp.properties[p].displayName === "${escapeForExtendScript(args.property_name)}") {
|
|
105
|
+
prop = comp.properties[p];
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (!prop) return __error("Property not found: ${escapeForExtendScript(args.property_name)}");
|
|
110
|
+
|
|
111
|
+
prop.setValue(${args.value}, true);
|
|
112
|
+
return __result({
|
|
113
|
+
set: true,
|
|
114
|
+
effect: "${escapeForExtendScript(args.effect_name)}",
|
|
115
|
+
property: "${escapeForExtendScript(args.property_name)}",
|
|
116
|
+
value: ${args.value}
|
|
117
|
+
});
|
|
118
|
+
`);
|
|
119
|
+
return sendCommand(script, bridgeOptions);
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
get_keyframes: {
|
|
123
|
+
description: "Get all keyframes for a specific effect property on a clip",
|
|
124
|
+
parameters: {
|
|
125
|
+
type: "object",
|
|
126
|
+
properties: {
|
|
127
|
+
node_id: {
|
|
128
|
+
type: "string",
|
|
129
|
+
description: "Node ID of the clip",
|
|
130
|
+
},
|
|
131
|
+
effect_name: {
|
|
132
|
+
type: "string",
|
|
133
|
+
description: "Display name of the effect",
|
|
134
|
+
},
|
|
135
|
+
property_name: {
|
|
136
|
+
type: "string",
|
|
137
|
+
description: "Display name of the property",
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
required: ["node_id", "effect_name", "property_name"],
|
|
141
|
+
},
|
|
142
|
+
handler: async (args) => {
|
|
143
|
+
const script = buildToolScript(`
|
|
144
|
+
var result = __findClip("${escapeForExtendScript(args.node_id)}");
|
|
145
|
+
if (!result) return __error("Clip not found");
|
|
146
|
+
|
|
147
|
+
var clip = result.clip;
|
|
148
|
+
var comp = null;
|
|
149
|
+
for (var i = 0; i < clip.components.numItems; i++) {
|
|
150
|
+
if (clip.components[i].displayName === "${escapeForExtendScript(args.effect_name)}" || clip.components[i].matchName === "${escapeForExtendScript(args.effect_name)}") {
|
|
151
|
+
comp = clip.components[i];
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
if (!comp) return __error("Effect not found");
|
|
156
|
+
|
|
157
|
+
var prop = null;
|
|
158
|
+
for (var p = 0; p < comp.properties.numItems; p++) {
|
|
159
|
+
if (comp.properties[p].displayName === "${escapeForExtendScript(args.property_name)}") {
|
|
160
|
+
prop = comp.properties[p];
|
|
161
|
+
break;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
if (!prop) return __error("Property not found");
|
|
165
|
+
|
|
166
|
+
var isTimeVarying = false;
|
|
167
|
+
try { isTimeVarying = prop.isTimeVarying(); } catch(e) {}
|
|
168
|
+
|
|
169
|
+
if (!isTimeVarying) {
|
|
170
|
+
return __result({ keyframes: [], isTimeVarying: false, message: "Property has no keyframes" });
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
var keys = prop.getKeys();
|
|
174
|
+
var keyframes = [];
|
|
175
|
+
if (keys) {
|
|
176
|
+
for (var k = 0; k < keys.length; k++) {
|
|
177
|
+
var time = keys[k];
|
|
178
|
+
var val = null;
|
|
179
|
+
try { val = prop.getValueAtKey(time); } catch(e) {}
|
|
180
|
+
keyframes.push({
|
|
181
|
+
time: __ticksToSeconds(time.ticks),
|
|
182
|
+
value: val
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return __result({
|
|
188
|
+
effect: "${escapeForExtendScript(args.effect_name)}",
|
|
189
|
+
property: "${escapeForExtendScript(args.property_name)}",
|
|
190
|
+
isTimeVarying: true,
|
|
191
|
+
keyframes: keyframes
|
|
192
|
+
});
|
|
193
|
+
`);
|
|
194
|
+
return sendCommand(script, bridgeOptions);
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
add_keyframe: {
|
|
198
|
+
description: "Add a keyframe to an effect property at a specific time",
|
|
199
|
+
parameters: {
|
|
200
|
+
type: "object",
|
|
201
|
+
properties: {
|
|
202
|
+
node_id: {
|
|
203
|
+
type: "string",
|
|
204
|
+
description: "Node ID of the clip",
|
|
205
|
+
},
|
|
206
|
+
effect_name: {
|
|
207
|
+
type: "string",
|
|
208
|
+
description: "Display name of the effect",
|
|
209
|
+
},
|
|
210
|
+
property_name: {
|
|
211
|
+
type: "string",
|
|
212
|
+
description: "Display name of the property",
|
|
213
|
+
},
|
|
214
|
+
time_seconds: {
|
|
215
|
+
type: "number",
|
|
216
|
+
description: "Time in seconds relative to clip start where to add keyframe",
|
|
217
|
+
},
|
|
218
|
+
value: {
|
|
219
|
+
type: "number",
|
|
220
|
+
description: "Value at the keyframe",
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
required: ["node_id", "effect_name", "property_name", "time_seconds", "value"],
|
|
224
|
+
},
|
|
225
|
+
handler: async (args) => {
|
|
226
|
+
const script = buildToolScript(`
|
|
227
|
+
var result = __findClip("${escapeForExtendScript(args.node_id)}");
|
|
228
|
+
if (!result) return __error("Clip not found");
|
|
229
|
+
|
|
230
|
+
var clip = result.clip;
|
|
231
|
+
var comp = null;
|
|
232
|
+
for (var i = 0; i < clip.components.numItems; i++) {
|
|
233
|
+
if (clip.components[i].displayName === "${escapeForExtendScript(args.effect_name)}" || clip.components[i].matchName === "${escapeForExtendScript(args.effect_name)}") {
|
|
234
|
+
comp = clip.components[i];
|
|
235
|
+
break;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
if (!comp) return __error("Effect not found");
|
|
239
|
+
|
|
240
|
+
var prop = null;
|
|
241
|
+
for (var p = 0; p < comp.properties.numItems; p++) {
|
|
242
|
+
if (comp.properties[p].displayName === "${escapeForExtendScript(args.property_name)}") {
|
|
243
|
+
prop = comp.properties[p];
|
|
244
|
+
break;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
if (!prop) return __error("Property not found");
|
|
248
|
+
|
|
249
|
+
// Enable keyframes if not already
|
|
250
|
+
try {
|
|
251
|
+
if (!prop.isTimeVarying()) {
|
|
252
|
+
prop.setTimeVarying(true);
|
|
253
|
+
}
|
|
254
|
+
} catch(e) {}
|
|
255
|
+
|
|
256
|
+
var time = new Time();
|
|
257
|
+
time.ticks = __secondsToTicks(${args.time_seconds}).toString();
|
|
258
|
+
prop.addKey(time);
|
|
259
|
+
prop.setValueAtKey(time, ${args.value}, true);
|
|
260
|
+
|
|
261
|
+
return __result({
|
|
262
|
+
added: true,
|
|
263
|
+
effect: "${escapeForExtendScript(args.effect_name)}",
|
|
264
|
+
property: "${escapeForExtendScript(args.property_name)}",
|
|
265
|
+
time: ${args.time_seconds},
|
|
266
|
+
value: ${args.value}
|
|
267
|
+
});
|
|
268
|
+
`);
|
|
269
|
+
return sendCommand(script, bridgeOptions);
|
|
270
|
+
},
|
|
271
|
+
},
|
|
272
|
+
remove_keyframe: {
|
|
273
|
+
description: "Remove a keyframe at a specific time from an effect property",
|
|
274
|
+
parameters: {
|
|
275
|
+
type: "object",
|
|
276
|
+
properties: {
|
|
277
|
+
node_id: {
|
|
278
|
+
type: "string",
|
|
279
|
+
description: "Node ID of the clip",
|
|
280
|
+
},
|
|
281
|
+
effect_name: {
|
|
282
|
+
type: "string",
|
|
283
|
+
description: "Display name of the effect",
|
|
284
|
+
},
|
|
285
|
+
property_name: {
|
|
286
|
+
type: "string",
|
|
287
|
+
description: "Display name of the property",
|
|
288
|
+
},
|
|
289
|
+
time_seconds: {
|
|
290
|
+
type: "number",
|
|
291
|
+
description: "Time in seconds of the keyframe to remove",
|
|
292
|
+
},
|
|
293
|
+
},
|
|
294
|
+
required: ["node_id", "effect_name", "property_name", "time_seconds"],
|
|
295
|
+
},
|
|
296
|
+
handler: async (args) => {
|
|
297
|
+
const script = buildToolScript(`
|
|
298
|
+
var result = __findClip("${escapeForExtendScript(args.node_id)}");
|
|
299
|
+
if (!result) return __error("Clip not found");
|
|
300
|
+
|
|
301
|
+
var clip = result.clip;
|
|
302
|
+
var comp = null;
|
|
303
|
+
for (var i = 0; i < clip.components.numItems; i++) {
|
|
304
|
+
if (clip.components[i].displayName === "${escapeForExtendScript(args.effect_name)}" || clip.components[i].matchName === "${escapeForExtendScript(args.effect_name)}") {
|
|
305
|
+
comp = clip.components[i];
|
|
306
|
+
break;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
if (!comp) return __error("Effect not found");
|
|
310
|
+
|
|
311
|
+
var prop = null;
|
|
312
|
+
for (var p = 0; p < comp.properties.numItems; p++) {
|
|
313
|
+
if (comp.properties[p].displayName === "${escapeForExtendScript(args.property_name)}") {
|
|
314
|
+
prop = comp.properties[p];
|
|
315
|
+
break;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
if (!prop) return __error("Property not found");
|
|
319
|
+
|
|
320
|
+
var time = new Time();
|
|
321
|
+
time.ticks = __secondsToTicks(${args.time_seconds}).toString();
|
|
322
|
+
prop.removeKey(time);
|
|
323
|
+
|
|
324
|
+
return __result({
|
|
325
|
+
removed: true,
|
|
326
|
+
effect: "${escapeForExtendScript(args.effect_name)}",
|
|
327
|
+
property: "${escapeForExtendScript(args.property_name)}",
|
|
328
|
+
time: ${args.time_seconds}
|
|
329
|
+
});
|
|
330
|
+
`);
|
|
331
|
+
return sendCommand(script, bridgeOptions);
|
|
332
|
+
},
|
|
333
|
+
},
|
|
334
|
+
remove_keyframe_range: {
|
|
335
|
+
description: "Remove all keyframes in a time range from an effect property",
|
|
336
|
+
parameters: {
|
|
337
|
+
type: "object",
|
|
338
|
+
properties: {
|
|
339
|
+
node_id: {
|
|
340
|
+
type: "string",
|
|
341
|
+
description: "Node ID of the clip",
|
|
342
|
+
},
|
|
343
|
+
effect_name: {
|
|
344
|
+
type: "string",
|
|
345
|
+
description: "Display name of the effect",
|
|
346
|
+
},
|
|
347
|
+
property_name: {
|
|
348
|
+
type: "string",
|
|
349
|
+
description: "Display name of the property",
|
|
350
|
+
},
|
|
351
|
+
start_seconds: {
|
|
352
|
+
type: "number",
|
|
353
|
+
description: "Start of the range in seconds",
|
|
354
|
+
},
|
|
355
|
+
end_seconds: {
|
|
356
|
+
type: "number",
|
|
357
|
+
description: "End of the range in seconds",
|
|
358
|
+
},
|
|
359
|
+
},
|
|
360
|
+
required: ["node_id", "effect_name", "property_name", "start_seconds", "end_seconds"],
|
|
361
|
+
},
|
|
362
|
+
handler: async (args) => {
|
|
363
|
+
const script = buildToolScript(`
|
|
364
|
+
var result = __findClip("${escapeForExtendScript(args.node_id)}");
|
|
365
|
+
if (!result) return __error("Clip not found");
|
|
366
|
+
|
|
367
|
+
var clip = result.clip;
|
|
368
|
+
var comp = null;
|
|
369
|
+
for (var i = 0; i < clip.components.numItems; i++) {
|
|
370
|
+
if (clip.components[i].displayName === "${escapeForExtendScript(args.effect_name)}" || clip.components[i].matchName === "${escapeForExtendScript(args.effect_name)}") {
|
|
371
|
+
comp = clip.components[i];
|
|
372
|
+
break;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
if (!comp) return __error("Effect not found");
|
|
376
|
+
|
|
377
|
+
var prop = null;
|
|
378
|
+
for (var p = 0; p < comp.properties.numItems; p++) {
|
|
379
|
+
if (comp.properties[p].displayName === "${escapeForExtendScript(args.property_name)}") {
|
|
380
|
+
prop = comp.properties[p];
|
|
381
|
+
break;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
if (!prop) return __error("Property not found");
|
|
385
|
+
|
|
386
|
+
var keys = prop.getKeys();
|
|
387
|
+
var startTicks = parseFloat(__secondsToTicks(${args.start_seconds}));
|
|
388
|
+
var endTicks = parseFloat(__secondsToTicks(${args.end_seconds}));
|
|
389
|
+
var removed = 0;
|
|
390
|
+
if (keys) {
|
|
391
|
+
for (var k = keys.length - 1; k >= 0; k--) {
|
|
392
|
+
var kt = parseFloat(keys[k].ticks);
|
|
393
|
+
if (kt >= startTicks && kt <= endTicks) {
|
|
394
|
+
prop.removeKey(keys[k]);
|
|
395
|
+
removed++;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
return __result({
|
|
401
|
+
removed: removed,
|
|
402
|
+
effect: "${escapeForExtendScript(args.effect_name)}",
|
|
403
|
+
property: "${escapeForExtendScript(args.property_name)}",
|
|
404
|
+
range: { start: ${args.start_seconds}, end: ${args.end_seconds} }
|
|
405
|
+
});
|
|
406
|
+
`);
|
|
407
|
+
return sendCommand(script, bridgeOptions);
|
|
408
|
+
},
|
|
409
|
+
},
|
|
410
|
+
set_keyframe_interpolation: {
|
|
411
|
+
description: "Set the interpolation type of a keyframe (Linear, Hold, or Bezier)",
|
|
412
|
+
parameters: {
|
|
413
|
+
type: "object",
|
|
414
|
+
properties: {
|
|
415
|
+
node_id: {
|
|
416
|
+
type: "string",
|
|
417
|
+
description: "Node ID of the clip",
|
|
418
|
+
},
|
|
419
|
+
effect_name: {
|
|
420
|
+
type: "string",
|
|
421
|
+
description: "Display name of the effect",
|
|
422
|
+
},
|
|
423
|
+
property_name: {
|
|
424
|
+
type: "string",
|
|
425
|
+
description: "Display name of the property",
|
|
426
|
+
},
|
|
427
|
+
time_seconds: {
|
|
428
|
+
type: "number",
|
|
429
|
+
description: "Time in seconds of the keyframe",
|
|
430
|
+
},
|
|
431
|
+
interpolation: {
|
|
432
|
+
type: "string",
|
|
433
|
+
enum: ["linear", "hold", "bezier"],
|
|
434
|
+
description: "Interpolation type",
|
|
435
|
+
},
|
|
436
|
+
},
|
|
437
|
+
required: ["node_id", "effect_name", "property_name", "time_seconds", "interpolation"],
|
|
438
|
+
},
|
|
439
|
+
handler: async (args) => {
|
|
440
|
+
const interpMap = { linear: 0, hold: 4, bezier: 5 };
|
|
441
|
+
const interpType = interpMap[args.interpolation] ?? 0;
|
|
442
|
+
const script = buildToolScript(`
|
|
443
|
+
var result = __findClip("${escapeForExtendScript(args.node_id)}");
|
|
444
|
+
if (!result) return __error("Clip not found");
|
|
445
|
+
|
|
446
|
+
var clip = result.clip;
|
|
447
|
+
var comp = null;
|
|
448
|
+
for (var i = 0; i < clip.components.numItems; i++) {
|
|
449
|
+
if (clip.components[i].displayName === "${escapeForExtendScript(args.effect_name)}" || clip.components[i].matchName === "${escapeForExtendScript(args.effect_name)}") {
|
|
450
|
+
comp = clip.components[i];
|
|
451
|
+
break;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
if (!comp) return __error("Effect not found");
|
|
455
|
+
|
|
456
|
+
var prop = null;
|
|
457
|
+
for (var p = 0; p < comp.properties.numItems; p++) {
|
|
458
|
+
if (comp.properties[p].displayName === "${escapeForExtendScript(args.property_name)}") {
|
|
459
|
+
prop = comp.properties[p];
|
|
460
|
+
break;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
if (!prop) return __error("Property not found");
|
|
464
|
+
|
|
465
|
+
var time = new Time();
|
|
466
|
+
time.ticks = __secondsToTicks(${args.time_seconds}).toString();
|
|
467
|
+
prop.setInterpolationTypeAtKey(time, ${interpType}, true);
|
|
468
|
+
|
|
469
|
+
return __result({
|
|
470
|
+
set: true,
|
|
471
|
+
interpolation: "${args.interpolation}",
|
|
472
|
+
time: ${args.time_seconds}
|
|
473
|
+
});
|
|
474
|
+
`);
|
|
475
|
+
return sendCommand(script, bridgeOptions);
|
|
476
|
+
},
|
|
477
|
+
},
|
|
478
|
+
get_value_at_time: {
|
|
479
|
+
description: "Get the interpolated value of an effect property at a specific time",
|
|
480
|
+
parameters: {
|
|
481
|
+
type: "object",
|
|
482
|
+
properties: {
|
|
483
|
+
node_id: {
|
|
484
|
+
type: "string",
|
|
485
|
+
description: "Node ID of the clip",
|
|
486
|
+
},
|
|
487
|
+
effect_name: {
|
|
488
|
+
type: "string",
|
|
489
|
+
description: "Display name of the effect",
|
|
490
|
+
},
|
|
491
|
+
property_name: {
|
|
492
|
+
type: "string",
|
|
493
|
+
description: "Display name of the property",
|
|
494
|
+
},
|
|
495
|
+
time_seconds: {
|
|
496
|
+
type: "number",
|
|
497
|
+
description: "Time in seconds to query the value at",
|
|
498
|
+
},
|
|
499
|
+
},
|
|
500
|
+
required: ["node_id", "effect_name", "property_name", "time_seconds"],
|
|
501
|
+
},
|
|
502
|
+
handler: async (args) => {
|
|
503
|
+
const script = buildToolScript(`
|
|
504
|
+
var result = __findClip("${escapeForExtendScript(args.node_id)}");
|
|
505
|
+
if (!result) return __error("Clip not found");
|
|
506
|
+
|
|
507
|
+
var clip = result.clip;
|
|
508
|
+
var comp = null;
|
|
509
|
+
for (var i = 0; i < clip.components.numItems; i++) {
|
|
510
|
+
if (clip.components[i].displayName === "${escapeForExtendScript(args.effect_name)}" || clip.components[i].matchName === "${escapeForExtendScript(args.effect_name)}") {
|
|
511
|
+
comp = clip.components[i];
|
|
512
|
+
break;
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
if (!comp) return __error("Effect not found");
|
|
516
|
+
|
|
517
|
+
var prop = null;
|
|
518
|
+
for (var p = 0; p < comp.properties.numItems; p++) {
|
|
519
|
+
if (comp.properties[p].displayName === "${escapeForExtendScript(args.property_name)}") {
|
|
520
|
+
prop = comp.properties[p];
|
|
521
|
+
break;
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
if (!prop) return __error("Property not found");
|
|
525
|
+
|
|
526
|
+
var targetTicks = parseFloat(__secondsToTicks(${args.time_seconds}).toString());
|
|
527
|
+
var value = null;
|
|
528
|
+
var source = "static";
|
|
529
|
+
|
|
530
|
+
var isTimeVarying = false;
|
|
531
|
+
try { isTimeVarying = prop.isTimeVarying(); } catch(e) { isTimeVarying = false; }
|
|
532
|
+
|
|
533
|
+
if (isTimeVarying) {
|
|
534
|
+
var keys = prop.getKeys();
|
|
535
|
+
if (keys && keys.length > 0) {
|
|
536
|
+
// Look for an exact key match by tick value.
|
|
537
|
+
var exactKey = null;
|
|
538
|
+
for (var k = 0; k < keys.length; k++) {
|
|
539
|
+
if (parseFloat(keys[k].ticks) === targetTicks) {
|
|
540
|
+
exactKey = keys[k];
|
|
541
|
+
break;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
if (exactKey !== null) {
|
|
545
|
+
value = prop.getValueAtKey(exactKey);
|
|
546
|
+
source = "exact-key";
|
|
547
|
+
} else {
|
|
548
|
+
// Find the bracketing keys (lower = last key before target, upper = first key after target).
|
|
549
|
+
var lowerKey = null;
|
|
550
|
+
var upperKey = null;
|
|
551
|
+
var lowerTicks = 0;
|
|
552
|
+
var upperTicks = 0;
|
|
553
|
+
for (var k2 = 0; k2 < keys.length; k2++) {
|
|
554
|
+
var kt = parseFloat(keys[k2].ticks);
|
|
555
|
+
if (kt <= targetTicks && (lowerKey === null || kt > lowerTicks)) {
|
|
556
|
+
lowerKey = keys[k2];
|
|
557
|
+
lowerTicks = kt;
|
|
558
|
+
}
|
|
559
|
+
if (kt >= targetTicks && (upperKey === null || kt < upperTicks)) {
|
|
560
|
+
upperKey = keys[k2];
|
|
561
|
+
upperTicks = kt;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
if (lowerKey !== null && upperKey !== null) {
|
|
565
|
+
var lowerVal = prop.getValueAtKey(lowerKey);
|
|
566
|
+
var upperVal = prop.getValueAtKey(upperKey);
|
|
567
|
+
var span = upperTicks - lowerTicks;
|
|
568
|
+
var ratio = (span === 0) ? 0 : (targetTicks - lowerTicks) / span;
|
|
569
|
+
if (typeof lowerVal === "number" && typeof upperVal === "number") {
|
|
570
|
+
value = lowerVal + (upperVal - lowerVal) * ratio;
|
|
571
|
+
} else if (lowerVal && upperVal && typeof lowerVal.length === "number" && typeof upperVal.length === "number") {
|
|
572
|
+
var arr = [];
|
|
573
|
+
for (var d = 0; d < lowerVal.length; d++) {
|
|
574
|
+
arr.push(lowerVal[d] + (upperVal[d] - lowerVal[d]) * ratio);
|
|
575
|
+
}
|
|
576
|
+
value = arr;
|
|
577
|
+
} else {
|
|
578
|
+
value = lowerVal;
|
|
579
|
+
}
|
|
580
|
+
source = "interpolated";
|
|
581
|
+
} else if (lowerKey !== null) {
|
|
582
|
+
value = prop.getValueAtKey(lowerKey);
|
|
583
|
+
source = "exact-key";
|
|
584
|
+
} else if (upperKey !== null) {
|
|
585
|
+
value = prop.getValueAtKey(upperKey);
|
|
586
|
+
source = "exact-key";
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
} else {
|
|
590
|
+
try { value = prop.getValue(); } catch(e) { value = null; }
|
|
591
|
+
source = "static";
|
|
592
|
+
}
|
|
593
|
+
} else {
|
|
594
|
+
try { value = prop.getValue(); } catch(e) { value = null; }
|
|
595
|
+
source = "static";
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
return __result({
|
|
599
|
+
effect: "${escapeForExtendScript(args.effect_name)}",
|
|
600
|
+
property: "${escapeForExtendScript(args.property_name)}",
|
|
601
|
+
time: ${args.time_seconds},
|
|
602
|
+
value: value,
|
|
603
|
+
source: source
|
|
604
|
+
});
|
|
605
|
+
`);
|
|
606
|
+
return sendCommand(script, bridgeOptions);
|
|
607
|
+
},
|
|
608
|
+
},
|
|
609
|
+
};
|
|
610
|
+
}
|
|
611
|
+
//# sourceMappingURL=keyframes.js.map
|