@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.
Files changed (58) hide show
  1. package/CHANGELOG.md +135 -123
  2. package/LICENSE +21 -21
  3. package/README.md +242 -242
  4. package/cep-plugin/.debug +8 -8
  5. package/cep-plugin/CSInterface.js +92 -92
  6. package/cep-plugin/host.jsx +7 -7
  7. package/cep-plugin/index.html +472 -472
  8. package/cep-plugin/main.js +6 -0
  9. package/cep-plugin/premiere.jsx +24947 -24947
  10. package/dist/bridge/script-builder.js +378 -378
  11. package/dist/index.js +103 -4
  12. package/dist/license.js +38 -0
  13. package/dist/server.js +60 -60
  14. package/dist/tools/advanced.js +357 -357
  15. package/dist/tools/audio-advanced.js +1049 -1049
  16. package/dist/tools/audio.js +42 -42
  17. package/dist/tools/captions.js +27 -27
  18. package/dist/tools/clipboard.js +214 -214
  19. package/dist/tools/color-advanced.js +253 -253
  20. package/dist/tools/discovery.js +291 -291
  21. package/dist/tools/effects.js +285 -285
  22. package/dist/tools/export-presets.js +206 -206
  23. package/dist/tools/export.js +255 -255
  24. package/dist/tools/health.js +10 -10
  25. package/dist/tools/inspection.js +885 -885
  26. package/dist/tools/keyframes.js +369 -369
  27. package/dist/tools/markers.js +89 -89
  28. package/dist/tools/media.js +178 -178
  29. package/dist/tools/metadata.js +94 -94
  30. package/dist/tools/multicam.js +50 -50
  31. package/dist/tools/playback.js +15 -15
  32. package/dist/tools/playhead.js +95 -95
  33. package/dist/tools/project-manager.js +21 -21
  34. package/dist/tools/project.js +149 -149
  35. package/dist/tools/scripting.js +319 -319
  36. package/dist/tools/selection.js +202 -202
  37. package/dist/tools/sequence-advanced.js +379 -379
  38. package/dist/tools/sequence-settings.js +564 -564
  39. package/dist/tools/sequence.js +167 -167
  40. package/dist/tools/source-monitor.js +68 -68
  41. package/dist/tools/text.js +58 -58
  42. package/dist/tools/timeline.js +312 -312
  43. package/dist/tools/track-management.js +446 -446
  44. package/dist/tools/track-targeting.js +635 -635
  45. package/dist/tools/tracks.js +87 -87
  46. package/dist/tools/transitions-motion.js +174 -174
  47. package/dist/tools/transitions.js +111 -111
  48. package/dist/tools/utility.js +606 -606
  49. package/dist/tools/workspace.js +29 -29
  50. package/package.json +66 -66
  51. package/scripts/build-chat-dmg.sh +210 -210
  52. package/scripts/build-chat-zip.sh +291 -291
  53. package/scripts/install-cep-windows.ps1 +99 -99
  54. package/scripts/install-cep.sh +66 -66
  55. package/scripts/install-chat-plugin.bat +67 -67
  56. package/scripts/install-chat-plugin.sh +85 -85
  57. package/scripts/install-windows.bat +95 -95
  58. package/scripts/package.mjs +254 -254
@@ -13,13 +13,13 @@ export function getTrackManagementTools(bridgeOptions) {
13
13
  required: ["track_index", "name"],
14
14
  },
15
15
  handler: async (args) => {
16
- const script = buildToolScript(`
17
- var seq = app.project.activeSequence;
18
- if (!seq) return __error("No active sequence");
19
- var track = seq.audioTracks[${args.track_index}];
20
- if (!track) return __error("Audio track not found at index ${args.track_index}");
21
- track.name = "${escapeForExtendScript(args.name)}";
22
- return __result({ renamed: true, trackIndex: ${args.track_index}, newName: "${escapeForExtendScript(args.name)}" });
16
+ const script = buildToolScript(`
17
+ var seq = app.project.activeSequence;
18
+ if (!seq) return __error("No active sequence");
19
+ var track = seq.audioTracks[${args.track_index}];
20
+ if (!track) return __error("Audio track not found at index ${args.track_index}");
21
+ track.name = "${escapeForExtendScript(args.name)}";
22
+ return __result({ renamed: true, trackIndex: ${args.track_index}, newName: "${escapeForExtendScript(args.name)}" });
23
23
  `);
24
24
  return sendCommand(script, bridgeOptions);
25
25
  },
@@ -35,13 +35,13 @@ export function getTrackManagementTools(bridgeOptions) {
35
35
  required: ["track_index", "muted"],
36
36
  },
37
37
  handler: async (args) => {
38
- const script = buildToolScript(`
39
- var seq = app.project.activeSequence;
40
- if (!seq) return __error("No active sequence");
41
- var track = seq.audioTracks[${args.track_index}];
42
- if (!track) return __error("Audio track not found at index ${args.track_index}");
43
- track.setMute(${args.muted ? 1 : 0});
44
- return __result({ muted: ${args.muted}, trackIndex: ${args.track_index}, trackName: track.name });
38
+ const script = buildToolScript(`
39
+ var seq = app.project.activeSequence;
40
+ if (!seq) return __error("No active sequence");
41
+ var track = seq.audioTracks[${args.track_index}];
42
+ if (!track) return __error("Audio track not found at index ${args.track_index}");
43
+ track.setMute(${args.muted ? 1 : 0});
44
+ return __result({ muted: ${args.muted}, trackIndex: ${args.track_index}, trackName: track.name });
45
45
  `);
46
46
  return sendCommand(script, bridgeOptions);
47
47
  },
@@ -57,13 +57,13 @@ export function getTrackManagementTools(bridgeOptions) {
57
57
  required: ["track_index", "locked"],
58
58
  },
59
59
  handler: async (args) => {
60
- const script = buildToolScript(`
61
- var seq = app.project.activeSequence;
62
- if (!seq) return __error("No active sequence");
63
- var track = seq.audioTracks[${args.track_index}];
64
- if (!track) return __error("Audio track not found at index ${args.track_index}");
65
- if (typeof track.setLock === "function") { track.setLock(${args.locked}); } else { track.locked = ${args.locked}; }
66
- return __result({ locked: ${args.locked}, trackIndex: ${args.track_index}, trackName: track.name });
60
+ const script = buildToolScript(`
61
+ var seq = app.project.activeSequence;
62
+ if (!seq) return __error("No active sequence");
63
+ var track = seq.audioTracks[${args.track_index}];
64
+ if (!track) return __error("Audio track not found at index ${args.track_index}");
65
+ if (typeof track.setLock === "function") { track.setLock(${args.locked}); } else { track.locked = ${args.locked}; }
66
+ return __result({ locked: ${args.locked}, trackIndex: ${args.track_index}, trackName: track.name });
67
67
  `);
68
68
  return sendCommand(script, bridgeOptions);
69
69
  },
@@ -79,13 +79,13 @@ export function getTrackManagementTools(bridgeOptions) {
79
79
  required: ["track_index", "targeted"],
80
80
  },
81
81
  handler: async (args) => {
82
- const script = buildToolScript(`
83
- var seq = app.project.activeSequence;
84
- if (!seq) return __error("No active sequence");
85
- var track = seq.audioTracks[${args.track_index}];
86
- if (!track) return __error("Audio track not found at index ${args.track_index}");
87
- if (typeof track.setTargeted === "function") { track.setTargeted(${args.targeted}, false); } else { track.targeted = ${args.targeted}; }
88
- return __result({ targeted: ${args.targeted}, trackIndex: ${args.track_index}, trackName: track.name });
82
+ const script = buildToolScript(`
83
+ var seq = app.project.activeSequence;
84
+ if (!seq) return __error("No active sequence");
85
+ var track = seq.audioTracks[${args.track_index}];
86
+ if (!track) return __error("Audio track not found at index ${args.track_index}");
87
+ if (typeof track.setTargeted === "function") { track.setTargeted(${args.targeted}, false); } else { track.targeted = ${args.targeted}; }
88
+ return __result({ targeted: ${args.targeted}, trackIndex: ${args.track_index}, trackName: track.name });
89
89
  `);
90
90
  return sendCommand(script, bridgeOptions);
91
91
  },
@@ -101,13 +101,13 @@ export function getTrackManagementTools(bridgeOptions) {
101
101
  required: ["track_index", "name"],
102
102
  },
103
103
  handler: async (args) => {
104
- const script = buildToolScript(`
105
- var seq = app.project.activeSequence;
106
- if (!seq) return __error("No active sequence");
107
- var track = seq.videoTracks[${args.track_index}];
108
- if (!track) return __error("Video track not found at index ${args.track_index}");
109
- track.name = "${escapeForExtendScript(args.name)}";
110
- return __result({ renamed: true, trackIndex: ${args.track_index}, newName: "${escapeForExtendScript(args.name)}" });
104
+ const script = buildToolScript(`
105
+ var seq = app.project.activeSequence;
106
+ if (!seq) return __error("No active sequence");
107
+ var track = seq.videoTracks[${args.track_index}];
108
+ if (!track) return __error("Video track not found at index ${args.track_index}");
109
+ track.name = "${escapeForExtendScript(args.name)}";
110
+ return __result({ renamed: true, trackIndex: ${args.track_index}, newName: "${escapeForExtendScript(args.name)}" });
111
111
  `);
112
112
  return sendCommand(script, bridgeOptions);
113
113
  },
@@ -123,13 +123,13 @@ export function getTrackManagementTools(bridgeOptions) {
123
123
  required: ["track_index", "muted"],
124
124
  },
125
125
  handler: async (args) => {
126
- const script = buildToolScript(`
127
- var seq = app.project.activeSequence;
128
- if (!seq) return __error("No active sequence");
129
- var track = seq.videoTracks[${args.track_index}];
130
- if (!track) return __error("Video track not found at index ${args.track_index}");
131
- track.setMute(${args.muted ? 1 : 0});
132
- return __result({ muted: ${args.muted}, trackIndex: ${args.track_index}, trackName: track.name });
126
+ const script = buildToolScript(`
127
+ var seq = app.project.activeSequence;
128
+ if (!seq) return __error("No active sequence");
129
+ var track = seq.videoTracks[${args.track_index}];
130
+ if (!track) return __error("Video track not found at index ${args.track_index}");
131
+ track.setMute(${args.muted ? 1 : 0});
132
+ return __result({ muted: ${args.muted}, trackIndex: ${args.track_index}, trackName: track.name });
133
133
  `);
134
134
  return sendCommand(script, bridgeOptions);
135
135
  },
@@ -145,13 +145,13 @@ export function getTrackManagementTools(bridgeOptions) {
145
145
  required: ["track_index", "locked"],
146
146
  },
147
147
  handler: async (args) => {
148
- const script = buildToolScript(`
149
- var seq = app.project.activeSequence;
150
- if (!seq) return __error("No active sequence");
151
- var track = seq.videoTracks[${args.track_index}];
152
- if (!track) return __error("Video track not found at index ${args.track_index}");
153
- if (typeof track.setLock === "function") { track.setLock(${args.locked}); } else { track.locked = ${args.locked}; }
154
- return __result({ locked: ${args.locked}, trackIndex: ${args.track_index}, trackName: track.name });
148
+ const script = buildToolScript(`
149
+ var seq = app.project.activeSequence;
150
+ if (!seq) return __error("No active sequence");
151
+ var track = seq.videoTracks[${args.track_index}];
152
+ if (!track) return __error("Video track not found at index ${args.track_index}");
153
+ if (typeof track.setLock === "function") { track.setLock(${args.locked}); } else { track.locked = ${args.locked}; }
154
+ return __result({ locked: ${args.locked}, trackIndex: ${args.track_index}, trackName: track.name });
155
155
  `);
156
156
  return sendCommand(script, bridgeOptions);
157
157
  },
@@ -167,13 +167,13 @@ export function getTrackManagementTools(bridgeOptions) {
167
167
  required: ["track_index", "targeted"],
168
168
  },
169
169
  handler: async (args) => {
170
- const script = buildToolScript(`
171
- var seq = app.project.activeSequence;
172
- if (!seq) return __error("No active sequence");
173
- var track = seq.videoTracks[${args.track_index}];
174
- if (!track) return __error("Video track not found at index ${args.track_index}");
175
- if (typeof track.setTargeted === "function") { track.setTargeted(${args.targeted}, false); } else { track.targeted = ${args.targeted}; }
176
- return __result({ targeted: ${args.targeted}, trackIndex: ${args.track_index}, trackName: track.name });
170
+ const script = buildToolScript(`
171
+ var seq = app.project.activeSequence;
172
+ if (!seq) return __error("No active sequence");
173
+ var track = seq.videoTracks[${args.track_index}];
174
+ if (!track) return __error("Video track not found at index ${args.track_index}");
175
+ if (typeof track.setTargeted === "function") { track.setTargeted(${args.targeted}, false); } else { track.targeted = ${args.targeted}; }
176
+ return __result({ targeted: ${args.targeted}, trackIndex: ${args.track_index}, trackName: track.name });
177
177
  `);
178
178
  return sendCommand(script, bridgeOptions);
179
179
  },
@@ -189,25 +189,25 @@ export function getTrackManagementTools(bridgeOptions) {
189
189
  required: ["track_index", "output_channels"],
190
190
  },
191
191
  handler: async (args) => {
192
- const script = buildToolScript(`
193
- var seq = app.project.activeSequence;
194
- if (!seq) return __error("No active sequence");
195
- var track = seq.audioTracks[${args.track_index}];
196
- if (!track) return __error("Audio track not found at index ${args.track_index}");
197
- var outputSet = false;
198
- if (track.components) {
199
- for (var ci = 0; ci < track.components.numItems; ci++) {
200
- var comp = track.components[ci];
201
- if (comp.matchName === "audioOutputRouting" || comp.displayName === "Output Routing") {
202
- for (var pi = 0; pi < comp.properties.numItems; pi++) {
203
- try { comp.properties[pi].setValue(${args.output_channels}, true); outputSet = true; break; } catch(e) {}
204
- }
205
- break;
206
- }
207
- }
208
- }
209
- return __result({ trackIndex: ${args.track_index}, trackName: track.name, requestedOutput: ${args.output_channels},
210
- outputSet: outputSet, note: outputSet ? "Output routing set." : "Output routing component not accessible via ExtendScript in this PP version. Use the Audio Track Mixer to set output routing manually." });
192
+ const script = buildToolScript(`
193
+ var seq = app.project.activeSequence;
194
+ if (!seq) return __error("No active sequence");
195
+ var track = seq.audioTracks[${args.track_index}];
196
+ if (!track) return __error("Audio track not found at index ${args.track_index}");
197
+ var outputSet = false;
198
+ if (track.components) {
199
+ for (var ci = 0; ci < track.components.numItems; ci++) {
200
+ var comp = track.components[ci];
201
+ if (comp.matchName === "audioOutputRouting" || comp.displayName === "Output Routing") {
202
+ for (var pi = 0; pi < comp.properties.numItems; pi++) {
203
+ try { comp.properties[pi].setValue(${args.output_channels}, true); outputSet = true; break; } catch(e) {}
204
+ }
205
+ break;
206
+ }
207
+ }
208
+ }
209
+ return __result({ trackIndex: ${args.track_index}, trackName: track.name, requestedOutput: ${args.output_channels},
210
+ outputSet: outputSet, note: outputSet ? "Output routing set." : "Output routing component not accessible via ExtendScript in this PP version. Use the Audio Track Mixer to set output routing manually." });
211
211
  `);
212
212
  return sendCommand(script, bridgeOptions);
213
213
  },
@@ -222,27 +222,27 @@ export function getTrackManagementTools(bridgeOptions) {
222
222
  required: ["track_index"],
223
223
  },
224
224
  handler: async (args) => {
225
- const script = buildToolScript(`
226
- var seq = app.project.activeSequence;
227
- if (!seq) return __error("No active sequence");
228
- var track = seq.audioTracks[${args.track_index}];
229
- if (!track) return __error("Audio track not found at index ${args.track_index}");
230
- var outputInfo = { trackIndex: ${args.track_index}, trackName: track.name, clipCount: track.clips.numItems };
231
- if (track.components) {
232
- for (var ci = 0; ci < track.components.numItems; ci++) {
233
- var comp = track.components[ci];
234
- if (comp.matchName === "audioOutputRouting" || comp.displayName === "Output Routing") {
235
- outputInfo.outputRoutingComponent = comp.displayName;
236
- var vals = [];
237
- for (var pi = 0; pi < comp.properties.numItems; pi++) {
238
- try { vals.push({ name: comp.properties[pi].displayName, value: comp.properties[pi].getValue() }); } catch(e) {}
239
- }
240
- outputInfo.properties = vals;
241
- break;
242
- }
243
- }
244
- }
245
- return __result(outputInfo);
225
+ const script = buildToolScript(`
226
+ var seq = app.project.activeSequence;
227
+ if (!seq) return __error("No active sequence");
228
+ var track = seq.audioTracks[${args.track_index}];
229
+ if (!track) return __error("Audio track not found at index ${args.track_index}");
230
+ var outputInfo = { trackIndex: ${args.track_index}, trackName: track.name, clipCount: track.clips.numItems };
231
+ if (track.components) {
232
+ for (var ci = 0; ci < track.components.numItems; ci++) {
233
+ var comp = track.components[ci];
234
+ if (comp.matchName === "audioOutputRouting" || comp.displayName === "Output Routing") {
235
+ outputInfo.outputRoutingComponent = comp.displayName;
236
+ var vals = [];
237
+ for (var pi = 0; pi < comp.properties.numItems; pi++) {
238
+ try { vals.push({ name: comp.properties[pi].displayName, value: comp.properties[pi].getValue() }); } catch(e) {}
239
+ }
240
+ outputInfo.properties = vals;
241
+ break;
242
+ }
243
+ }
244
+ }
245
+ return __result(outputInfo);
246
246
  `);
247
247
  return sendCommand(script, bridgeOptions);
248
248
  },
@@ -258,18 +258,18 @@ export function getTrackManagementTools(bridgeOptions) {
258
258
  required: ["track_type", "track_index"],
259
259
  },
260
260
  handler: async (args) => {
261
- const script = buildToolScript(`
262
- var seq = app.project.activeSequence;
263
- if (!seq) return __error("No active sequence");
264
- var trackType = "${escapeForExtendScript(args.track_type)}";
265
- var track = trackType === "video" ? seq.videoTracks[${args.track_index}] : seq.audioTracks[${args.track_index}];
266
- if (!track) return __error("Track not found at index ${args.track_index}");
267
- var count = 0;
268
- for (var i = 0; i < track.clips.numItems; i++) {
269
- track.clips[i].setSelected(true, true);
270
- count++;
271
- }
272
- return __result({ selected: count, trackIndex: ${args.track_index}, trackType: trackType, trackName: track.name });
261
+ const script = buildToolScript(`
262
+ var seq = app.project.activeSequence;
263
+ if (!seq) return __error("No active sequence");
264
+ var trackType = "${escapeForExtendScript(args.track_type)}";
265
+ var track = trackType === "video" ? seq.videoTracks[${args.track_index}] : seq.audioTracks[${args.track_index}];
266
+ if (!track) return __error("Track not found at index ${args.track_index}");
267
+ var count = 0;
268
+ for (var i = 0; i < track.clips.numItems; i++) {
269
+ track.clips[i].setSelected(true, true);
270
+ count++;
271
+ }
272
+ return __result({ selected: count, trackIndex: ${args.track_index}, trackType: trackType, trackName: track.name });
273
273
  `);
274
274
  return sendCommand(script, bridgeOptions);
275
275
  },
@@ -285,34 +285,34 @@ export function getTrackManagementTools(bridgeOptions) {
285
285
  required: ["track_type", "track_index"],
286
286
  },
287
287
  handler: async (args) => {
288
- const script = buildToolScript(`
289
- var seq = app.project.activeSequence;
290
- if (!seq) return __error("No active sequence");
291
- var trackType = "${escapeForExtendScript(args.track_type)}";
292
- var track = trackType === "video" ? seq.videoTracks[${args.track_index}] : seq.audioTracks[${args.track_index}];
293
- if (!track) return __error("Track not found at index ${args.track_index}");
294
- var clips = [];
295
- for (var i = 0; i < track.clips.numItems; i++) {
296
- var c = track.clips[i];
297
- clips.push({
298
- nodeId: c.nodeId,
299
- name: c.name,
300
- clipIndex: i,
301
- startSeconds: __ticksToSeconds(c.start.ticks),
302
- endSeconds: __ticksToSeconds(c.end.ticks),
303
- durationSeconds: __ticksToSeconds(c.duration.ticks),
304
- inPointSeconds: __ticksToSeconds(c.inPoint.ticks),
305
- outPointSeconds: __ticksToSeconds(c.outPoint.ticks),
306
- mediaType: c.mediaType
307
- });
308
- }
309
- return __result({
310
- trackType: trackType,
311
- trackIndex: ${args.track_index},
312
- trackName: track.name,
313
- clipCount: clips.length,
314
- clips: clips
315
- });
288
+ const script = buildToolScript(`
289
+ var seq = app.project.activeSequence;
290
+ if (!seq) return __error("No active sequence");
291
+ var trackType = "${escapeForExtendScript(args.track_type)}";
292
+ var track = trackType === "video" ? seq.videoTracks[${args.track_index}] : seq.audioTracks[${args.track_index}];
293
+ if (!track) return __error("Track not found at index ${args.track_index}");
294
+ var clips = [];
295
+ for (var i = 0; i < track.clips.numItems; i++) {
296
+ var c = track.clips[i];
297
+ clips.push({
298
+ nodeId: c.nodeId,
299
+ name: c.name,
300
+ clipIndex: i,
301
+ startSeconds: __ticksToSeconds(c.start.ticks),
302
+ endSeconds: __ticksToSeconds(c.end.ticks),
303
+ durationSeconds: __ticksToSeconds(c.duration.ticks),
304
+ inPointSeconds: __ticksToSeconds(c.inPoint.ticks),
305
+ outPointSeconds: __ticksToSeconds(c.outPoint.ticks),
306
+ mediaType: c.mediaType
307
+ });
308
+ }
309
+ return __result({
310
+ trackType: trackType,
311
+ trackIndex: ${args.track_index},
312
+ trackName: track.name,
313
+ clipCount: clips.length,
314
+ clips: clips
315
+ });
316
316
  `);
317
317
  return sendCommand(script, bridgeOptions);
318
318
  },
@@ -328,42 +328,42 @@ export function getTrackManagementTools(bridgeOptions) {
328
328
  required: ["track_type", "track_index"],
329
329
  },
330
330
  handler: async (args) => {
331
- const script = buildToolScript(`
332
- var seq = app.project.activeSequence;
333
- if (!seq) return __error("No active sequence");
334
- var trackType = "${escapeForExtendScript(args.track_type)}";
335
- var track = trackType === "video" ? seq.videoTracks[${args.track_index}] : seq.audioTracks[${args.track_index}];
336
- if (!track) return __error("Track not found at index ${args.track_index}");
337
- var clipCount = track.clips.numItems;
338
- var clipData = [];
339
- for (var i = 0; i < clipCount; i++) {
340
- var c = track.clips[i];
341
- clipData.push({ startTicks: parseFloat(c.start.ticks), endTicks: parseFloat(c.end.ticks) });
342
- }
343
- clipData.sort(function(a, b) { return a.startTicks - b.startTicks; });
344
- var gaps = [];
345
- for (var i = 0; i < clipData.length - 1; i++) {
346
- var gapStartTicks = clipData[i].endTicks;
347
- var gapEndTicks = clipData[i + 1].startTicks;
348
- if (gapEndTicks > gapStartTicks + 1000) {
349
- gaps.push({
350
- index: gaps.length,
351
- startSeconds: __ticksToSeconds(gapStartTicks.toString()),
352
- endSeconds: __ticksToSeconds(gapEndTicks.toString()),
353
- durationSeconds: __ticksToSeconds((gapEndTicks - gapStartTicks).toString()),
354
- afterClipIndex: i,
355
- beforeClipIndex: i + 1
356
- });
357
- }
358
- }
359
- return __result({
360
- trackType: trackType,
361
- trackIndex: ${args.track_index},
362
- trackName: track.name,
363
- clipCount: clipCount,
364
- gapCount: gaps.length,
365
- gaps: gaps
366
- });
331
+ const script = buildToolScript(`
332
+ var seq = app.project.activeSequence;
333
+ if (!seq) return __error("No active sequence");
334
+ var trackType = "${escapeForExtendScript(args.track_type)}";
335
+ var track = trackType === "video" ? seq.videoTracks[${args.track_index}] : seq.audioTracks[${args.track_index}];
336
+ if (!track) return __error("Track not found at index ${args.track_index}");
337
+ var clipCount = track.clips.numItems;
338
+ var clipData = [];
339
+ for (var i = 0; i < clipCount; i++) {
340
+ var c = track.clips[i];
341
+ clipData.push({ startTicks: parseFloat(c.start.ticks), endTicks: parseFloat(c.end.ticks) });
342
+ }
343
+ clipData.sort(function(a, b) { return a.startTicks - b.startTicks; });
344
+ var gaps = [];
345
+ for (var i = 0; i < clipData.length - 1; i++) {
346
+ var gapStartTicks = clipData[i].endTicks;
347
+ var gapEndTicks = clipData[i + 1].startTicks;
348
+ if (gapEndTicks > gapStartTicks + 1000) {
349
+ gaps.push({
350
+ index: gaps.length,
351
+ startSeconds: __ticksToSeconds(gapStartTicks.toString()),
352
+ endSeconds: __ticksToSeconds(gapEndTicks.toString()),
353
+ durationSeconds: __ticksToSeconds((gapEndTicks - gapStartTicks).toString()),
354
+ afterClipIndex: i,
355
+ beforeClipIndex: i + 1
356
+ });
357
+ }
358
+ }
359
+ return __result({
360
+ trackType: trackType,
361
+ trackIndex: ${args.track_index},
362
+ trackName: track.name,
363
+ clipCount: clipCount,
364
+ gapCount: gaps.length,
365
+ gaps: gaps
366
+ });
367
367
  `);
368
368
  return sendCommand(script, bridgeOptions);
369
369
  },
@@ -380,44 +380,44 @@ export function getTrackManagementTools(bridgeOptions) {
380
380
  required: ["track_type", "track_index", "gap_index"],
381
381
  },
382
382
  handler: async (args) => {
383
- const script = buildToolScript(`
384
- var seq = app.project.activeSequence;
385
- if (!seq) return __error("No active sequence");
386
- var trackType = "${escapeForExtendScript(args.track_type)}";
387
- var track = trackType === "video" ? seq.videoTracks[${args.track_index}] : seq.audioTracks[${args.track_index}];
388
- if (!track) return __error("Track not found at index ${args.track_index}");
389
- var clipData = [];
390
- for (var i = 0; i < track.clips.numItems; i++) {
391
- var c = track.clips[i];
392
- clipData.push({ startTicks: parseFloat(c.start.ticks), endTicks: parseFloat(c.end.ticks) });
393
- }
394
- clipData.sort(function(a, b) { return a.startTicks - b.startTicks; });
395
- var gaps = [];
396
- for (var i = 0; i < clipData.length - 1; i++) {
397
- var gapStartTicks = clipData[i].endTicks;
398
- var gapEndTicks = clipData[i + 1].startTicks;
399
- if (gapEndTicks > gapStartTicks + 1000) {
400
- gaps.push({ startTicks: gapStartTicks, endTicks: gapEndTicks });
401
- }
402
- }
403
- var gapIdx = ${args.gap_index};
404
- if (gapIdx >= gaps.length) return __error("Gap index " + gapIdx + " out of range (found " + gaps.length + " gaps)");
405
- var gap = gaps[gapIdx];
406
- var gapDuration = gap.endTicks - gap.startTicks;
407
- // Use QE DOM to perform ripple delete (extract) on the gap range
408
- app.enableQE();
409
- var qeSeq = qe.project.getActiveSequence();
410
- if (!qeSeq) return __error("No active sequence (QE)");
411
- seq.setInPoint(gap.startTicks.toString());
412
- seq.setOutPoint(gap.endTicks.toString());
413
- qeSeq.extract();
414
- return __result({
415
- closed: true,
416
- gapIndex: gapIdx,
417
- gapDurationSeconds: __ticksToSeconds(gapDuration.toString()),
418
- startSeconds: __ticksToSeconds(gap.startTicks.toString()),
419
- endSeconds: __ticksToSeconds(gap.endTicks.toString())
420
- });
383
+ const script = buildToolScript(`
384
+ var seq = app.project.activeSequence;
385
+ if (!seq) return __error("No active sequence");
386
+ var trackType = "${escapeForExtendScript(args.track_type)}";
387
+ var track = trackType === "video" ? seq.videoTracks[${args.track_index}] : seq.audioTracks[${args.track_index}];
388
+ if (!track) return __error("Track not found at index ${args.track_index}");
389
+ var clipData = [];
390
+ for (var i = 0; i < track.clips.numItems; i++) {
391
+ var c = track.clips[i];
392
+ clipData.push({ startTicks: parseFloat(c.start.ticks), endTicks: parseFloat(c.end.ticks) });
393
+ }
394
+ clipData.sort(function(a, b) { return a.startTicks - b.startTicks; });
395
+ var gaps = [];
396
+ for (var i = 0; i < clipData.length - 1; i++) {
397
+ var gapStartTicks = clipData[i].endTicks;
398
+ var gapEndTicks = clipData[i + 1].startTicks;
399
+ if (gapEndTicks > gapStartTicks + 1000) {
400
+ gaps.push({ startTicks: gapStartTicks, endTicks: gapEndTicks });
401
+ }
402
+ }
403
+ var gapIdx = ${args.gap_index};
404
+ if (gapIdx >= gaps.length) return __error("Gap index " + gapIdx + " out of range (found " + gaps.length + " gaps)");
405
+ var gap = gaps[gapIdx];
406
+ var gapDuration = gap.endTicks - gap.startTicks;
407
+ // Use QE DOM to perform ripple delete (extract) on the gap range
408
+ app.enableQE();
409
+ var qeSeq = qe.project.getActiveSequence();
410
+ if (!qeSeq) return __error("No active sequence (QE)");
411
+ seq.setInPoint(gap.startTicks.toString());
412
+ seq.setOutPoint(gap.endTicks.toString());
413
+ qeSeq.extract();
414
+ return __result({
415
+ closed: true,
416
+ gapIndex: gapIdx,
417
+ gapDurationSeconds: __ticksToSeconds(gapDuration.toString()),
418
+ startSeconds: __ticksToSeconds(gap.startTicks.toString()),
419
+ endSeconds: __ticksToSeconds(gap.endTicks.toString())
420
+ });
421
421
  `);
422
422
  return sendCommand(script, bridgeOptions);
423
423
  },
@@ -433,40 +433,40 @@ export function getTrackManagementTools(bridgeOptions) {
433
433
  required: ["track_type", "track_index"],
434
434
  },
435
435
  handler: async (args) => {
436
- const script = buildToolScript(`
437
- var seq = app.project.activeSequence;
438
- if (!seq) return __error("No active sequence");
439
- var trackType = "${escapeForExtendScript(args.track_type)}";
440
- var track = trackType === "video" ? seq.videoTracks[${args.track_index}] : seq.audioTracks[${args.track_index}];
441
- if (!track) return __error("Track not found at index ${args.track_index}");
442
- app.enableQE();
443
- var qeSeq = qe.project.getActiveSequence();
444
- if (!qeSeq) return __error("No active sequence (QE)");
445
- var closedCount = 0;
446
- // Find and close gaps from right to left to preserve positions
447
- var found = true;
448
- while (found) {
449
- found = false;
450
- var clipData = [];
451
- for (var i = 0; i < track.clips.numItems; i++) {
452
- var c = track.clips[i];
453
- clipData.push({ startTicks: parseFloat(c.start.ticks), endTicks: parseFloat(c.end.ticks) });
454
- }
455
- clipData.sort(function(a, b) { return a.startTicks - b.startTicks; });
456
- for (var i = clipData.length - 2; i >= 0; i--) {
457
- var gapStartTicks = clipData[i].endTicks;
458
- var gapEndTicks = clipData[i + 1].startTicks;
459
- if (gapEndTicks > gapStartTicks + 1000) {
460
- seq.setInPoint(gapStartTicks.toString());
461
- seq.setOutPoint(gapEndTicks.toString());
462
- qeSeq.extract();
463
- closedCount++;
464
- found = true;
465
- break;
466
- }
467
- }
468
- }
469
- return __result({ closedGaps: closedCount, trackIndex: ${args.track_index}, trackType: trackType });
436
+ const script = buildToolScript(`
437
+ var seq = app.project.activeSequence;
438
+ if (!seq) return __error("No active sequence");
439
+ var trackType = "${escapeForExtendScript(args.track_type)}";
440
+ var track = trackType === "video" ? seq.videoTracks[${args.track_index}] : seq.audioTracks[${args.track_index}];
441
+ if (!track) return __error("Track not found at index ${args.track_index}");
442
+ app.enableQE();
443
+ var qeSeq = qe.project.getActiveSequence();
444
+ if (!qeSeq) return __error("No active sequence (QE)");
445
+ var closedCount = 0;
446
+ // Find and close gaps from right to left to preserve positions
447
+ var found = true;
448
+ while (found) {
449
+ found = false;
450
+ var clipData = [];
451
+ for (var i = 0; i < track.clips.numItems; i++) {
452
+ var c = track.clips[i];
453
+ clipData.push({ startTicks: parseFloat(c.start.ticks), endTicks: parseFloat(c.end.ticks) });
454
+ }
455
+ clipData.sort(function(a, b) { return a.startTicks - b.startTicks; });
456
+ for (var i = clipData.length - 2; i >= 0; i--) {
457
+ var gapStartTicks = clipData[i].endTicks;
458
+ var gapEndTicks = clipData[i + 1].startTicks;
459
+ if (gapEndTicks > gapStartTicks + 1000) {
460
+ seq.setInPoint(gapStartTicks.toString());
461
+ seq.setOutPoint(gapEndTicks.toString());
462
+ qeSeq.extract();
463
+ closedCount++;
464
+ found = true;
465
+ break;
466
+ }
467
+ }
468
+ }
469
+ return __result({ closedGaps: closedCount, trackIndex: ${args.track_index}, trackType: trackType });
470
470
  `);
471
471
  return sendCommand(script, bridgeOptions);
472
472
  },
@@ -484,23 +484,23 @@ export function getTrackManagementTools(bridgeOptions) {
484
484
  required: ["track_type", "track_index", "start_time", "end_time"],
485
485
  },
486
486
  handler: async (args) => {
487
- const script = buildToolScript(`
488
- var seq = app.project.activeSequence;
489
- if (!seq) return __error("No active sequence");
490
- app.enableQE();
491
- var qeSeq = qe.project.getActiveSequence();
492
- if (!qeSeq) return __error("No active sequence (QE)");
493
- var startTicks = __secondsToTicks(${args.start_time}).toString();
494
- var endTicks = __secondsToTicks(${args.end_time}).toString();
495
- seq.setInPoint(startTicks);
496
- seq.setOutPoint(endTicks);
497
- qeSeq.extract();
498
- return __result({
499
- deleted: true,
500
- startSeconds: ${args.start_time},
501
- endSeconds: ${args.end_time},
502
- durationSeconds: ${args.end_time - args.start_time}
503
- });
487
+ const script = buildToolScript(`
488
+ var seq = app.project.activeSequence;
489
+ if (!seq) return __error("No active sequence");
490
+ app.enableQE();
491
+ var qeSeq = qe.project.getActiveSequence();
492
+ if (!qeSeq) return __error("No active sequence (QE)");
493
+ var startTicks = __secondsToTicks(${args.start_time}).toString();
494
+ var endTicks = __secondsToTicks(${args.end_time}).toString();
495
+ seq.setInPoint(startTicks);
496
+ seq.setOutPoint(endTicks);
497
+ qeSeq.extract();
498
+ return __result({
499
+ deleted: true,
500
+ startSeconds: ${args.start_time},
501
+ endSeconds: ${args.end_time},
502
+ durationSeconds: ${args.end_time - args.start_time}
503
+ });
504
504
  `);
505
505
  return sendCommand(script, bridgeOptions);
506
506
  },
@@ -513,40 +513,40 @@ export function getTrackManagementTools(bridgeOptions) {
513
513
  required: [],
514
514
  },
515
515
  handler: async (_args) => {
516
- const script = buildToolScript(`
517
- var seq = app.project.activeSequence;
518
- if (!seq) return __error("No active sequence");
519
- app.enableQE();
520
- var qeSeq = qe.project.getActiveSequence();
521
- if (!qeSeq) return __error("No active sequence (QE)");
522
- var totalClosed = 0;
523
- var found = true;
524
- while (found) {
525
- found = false;
526
- // Collect all clips from all video tracks
527
- var allClipData = [];
528
- for (var t = 0; t < seq.videoTracks.numTracks; t++) {
529
- var track = seq.videoTracks[t];
530
- for (var i = 0; i < track.clips.numItems; i++) {
531
- var c = track.clips[i];
532
- allClipData.push({ startTicks: parseFloat(c.start.ticks), endTicks: parseFloat(c.end.ticks) });
533
- }
534
- }
535
- allClipData.sort(function(a, b) { return a.startTicks - b.startTicks; });
536
- for (var i = allClipData.length - 2; i >= 0; i--) {
537
- var gapStart = allClipData[i].endTicks;
538
- var gapEnd = allClipData[i + 1].startTicks;
539
- if (gapEnd > gapStart + 1000) {
540
- seq.setInPoint(gapStart.toString());
541
- seq.setOutPoint(gapEnd.toString());
542
- qeSeq.extract();
543
- totalClosed++;
544
- found = true;
545
- break;
546
- }
547
- }
548
- }
549
- return __result({ closedGaps: totalClosed });
516
+ const script = buildToolScript(`
517
+ var seq = app.project.activeSequence;
518
+ if (!seq) return __error("No active sequence");
519
+ app.enableQE();
520
+ var qeSeq = qe.project.getActiveSequence();
521
+ if (!qeSeq) return __error("No active sequence (QE)");
522
+ var totalClosed = 0;
523
+ var found = true;
524
+ while (found) {
525
+ found = false;
526
+ // Collect all clips from all video tracks
527
+ var allClipData = [];
528
+ for (var t = 0; t < seq.videoTracks.numTracks; t++) {
529
+ var track = seq.videoTracks[t];
530
+ for (var i = 0; i < track.clips.numItems; i++) {
531
+ var c = track.clips[i];
532
+ allClipData.push({ startTicks: parseFloat(c.start.ticks), endTicks: parseFloat(c.end.ticks) });
533
+ }
534
+ }
535
+ allClipData.sort(function(a, b) { return a.startTicks - b.startTicks; });
536
+ for (var i = allClipData.length - 2; i >= 0; i--) {
537
+ var gapStart = allClipData[i].endTicks;
538
+ var gapEnd = allClipData[i + 1].startTicks;
539
+ if (gapEnd > gapStart + 1000) {
540
+ seq.setInPoint(gapStart.toString());
541
+ seq.setOutPoint(gapEnd.toString());
542
+ qeSeq.extract();
543
+ totalClosed++;
544
+ found = true;
545
+ break;
546
+ }
547
+ }
548
+ }
549
+ return __result({ closedGaps: totalClosed });
550
550
  `);
551
551
  return sendCommand(script, bridgeOptions);
552
552
  },
@@ -559,11 +559,11 @@ export function getTrackManagementTools(bridgeOptions) {
559
559
  required: [],
560
560
  },
561
561
  handler: async (_args) => {
562
- const script = buildToolScript(`
563
- var seq = app.project.activeSequence;
564
- if (!seq) return __error("No active sequence");
565
- var clips = __getAllClips(seq);
566
- return __result({ clipCount: clips.length, clips: clips });
562
+ const script = buildToolScript(`
563
+ var seq = app.project.activeSequence;
564
+ if (!seq) return __error("No active sequence");
565
+ var clips = __getAllClips(seq);
566
+ return __result({ clipCount: clips.length, clips: clips });
567
567
  `);
568
568
  return sendCommand(script, bridgeOptions);
569
569
  },
@@ -582,44 +582,44 @@ export function getTrackManagementTools(bridgeOptions) {
582
582
  },
583
583
  handler: async (args) => {
584
584
  const clipIndicesStr = JSON.stringify(args.clip_indices);
585
- const script = buildToolScript(`
586
- var seq = app.project.activeSequence;
587
- if (!seq) return __error("No active sequence");
588
- app.enableQE();
589
- var qeSeq = qe.project.getActiveSequence();
590
- if (!qeSeq) return __error("No active sequence (QE)");
591
- var trackType = "${escapeForExtendScript(args.track_type)}";
592
- var trackIndex = ${args.track_index};
593
- var effectName = "${escapeForExtendScript(args.effect_name)}";
594
- var clipIndices = ${clipIndicesStr};
595
-
596
- // Find effect in list
597
- var effectList = trackType === "video" ? qe.project.getVideoEffectList() : qe.project.getAudioEffectList();
598
- var effectToApply = null;
599
- for (var i = 0; i < effectList.numItems; i++) {
600
- if (effectList[i].name === effectName) {
601
- effectToApply = effectList[i];
602
- break;
603
- }
604
- }
605
- if (!effectToApply) return __error("Effect not found: " + effectName);
606
-
607
- var qeTrack = trackType === "video" ? qeSeq.getVideoTrackAt(trackIndex) : qeSeq.getAudioTrackAt(trackIndex);
608
- if (!qeTrack) return __error("QE track not found");
609
-
610
- var applied = 0;
611
- for (var j = 0; j < clipIndices.length; j++) {
612
- var qeClip = qeTrack.getItemAt(clipIndices[j]);
613
- if (qeClip) {
614
- if (trackType === "video") {
615
- qeClip.addVideoEffect(effectToApply);
616
- } else {
617
- qeClip.addAudioEffect(effectToApply);
618
- }
619
- applied++;
620
- }
621
- }
622
- return __result({ applied: applied, effect: effectName, trackType: trackType, trackIndex: trackIndex });
585
+ const script = buildToolScript(`
586
+ var seq = app.project.activeSequence;
587
+ if (!seq) return __error("No active sequence");
588
+ app.enableQE();
589
+ var qeSeq = qe.project.getActiveSequence();
590
+ if (!qeSeq) return __error("No active sequence (QE)");
591
+ var trackType = "${escapeForExtendScript(args.track_type)}";
592
+ var trackIndex = ${args.track_index};
593
+ var effectName = "${escapeForExtendScript(args.effect_name)}";
594
+ var clipIndices = ${clipIndicesStr};
595
+
596
+ // Find effect in list
597
+ var effectList = trackType === "video" ? qe.project.getVideoEffectList() : qe.project.getAudioEffectList();
598
+ var effectToApply = null;
599
+ for (var i = 0; i < effectList.numItems; i++) {
600
+ if (effectList[i].name === effectName) {
601
+ effectToApply = effectList[i];
602
+ break;
603
+ }
604
+ }
605
+ if (!effectToApply) return __error("Effect not found: " + effectName);
606
+
607
+ var qeTrack = trackType === "video" ? qeSeq.getVideoTrackAt(trackIndex) : qeSeq.getAudioTrackAt(trackIndex);
608
+ if (!qeTrack) return __error("QE track not found");
609
+
610
+ var applied = 0;
611
+ for (var j = 0; j < clipIndices.length; j++) {
612
+ var qeClip = qeTrack.getItemAt(clipIndices[j]);
613
+ if (qeClip) {
614
+ if (trackType === "video") {
615
+ qeClip.addVideoEffect(effectToApply);
616
+ } else {
617
+ qeClip.addAudioEffect(effectToApply);
618
+ }
619
+ applied++;
620
+ }
621
+ }
622
+ return __result({ applied: applied, effect: effectName, trackType: trackType, trackIndex: trackIndex });
623
623
  `);
624
624
  return sendCommand(script, bridgeOptions);
625
625
  },
@@ -636,44 +636,44 @@ export function getTrackManagementTools(bridgeOptions) {
636
636
  required: ["track_type", "track_index", "clip_index"],
637
637
  },
638
638
  handler: async (args) => {
639
- const script = buildToolScript(`
640
- var seq = app.project.activeSequence;
641
- if (!seq) return __error("No active sequence");
642
- var trackType = "${escapeForExtendScript(args.track_type)}";
643
- var track = trackType === "video" ? seq.videoTracks[${args.track_index}] : seq.audioTracks[${args.track_index}];
644
- if (!track) return __error("Track not found");
645
- var clip = track.clips[${args.clip_index}];
646
- if (!clip) return __error("Clip not found at index ${args.clip_index}");
647
- // Intrinsic components to preserve. matchName is locale-INDEPENDENT, so build the
648
- // preserve set from __INTRINSIC matchNames first; keep English displayNames only
649
- // as a secondary fallback (otherwise non-English installs delete Motion/Opacity).
650
- var intrinsicMatch = [];
651
- for (var key in __INTRINSIC) {
652
- if (__INTRINSIC.hasOwnProperty(key)) {
653
- var m = __INTRINSIC[key].match;
654
- for (var mi = 0; mi < m.length; mi++) { intrinsicMatch.push(m[mi]); }
655
- }
656
- }
657
- var preservedNames = ["Motion", "Opacity", "Volume", "Channel Volume", "Panner", "Time Remapping"];
658
- var removed = [];
659
- // Remove from end to start to avoid index shifting
660
- for (var i = clip.components.numItems - 1; i >= 0; i--) {
661
- var comp = clip.components[i];
662
- var isIntrinsic = false;
663
- for (var j = 0; j < intrinsicMatch.length; j++) {
664
- if (comp.matchName === intrinsicMatch[j]) { isIntrinsic = true; break; }
665
- }
666
- if (!isIntrinsic) {
667
- for (var k = 0; k < preservedNames.length; k++) {
668
- if (comp.displayName === preservedNames[k]) { isIntrinsic = true; break; }
669
- }
670
- }
671
- if (!isIntrinsic) {
672
- removed.push(comp.displayName);
673
- comp.remove();
674
- }
675
- }
676
- return __result({ removed: removed, removedCount: removed.length, clipName: clip.name });
639
+ const script = buildToolScript(`
640
+ var seq = app.project.activeSequence;
641
+ if (!seq) return __error("No active sequence");
642
+ var trackType = "${escapeForExtendScript(args.track_type)}";
643
+ var track = trackType === "video" ? seq.videoTracks[${args.track_index}] : seq.audioTracks[${args.track_index}];
644
+ if (!track) return __error("Track not found");
645
+ var clip = track.clips[${args.clip_index}];
646
+ if (!clip) return __error("Clip not found at index ${args.clip_index}");
647
+ // Intrinsic components to preserve. matchName is locale-INDEPENDENT, so build the
648
+ // preserve set from __INTRINSIC matchNames first; keep English displayNames only
649
+ // as a secondary fallback (otherwise non-English installs delete Motion/Opacity).
650
+ var intrinsicMatch = [];
651
+ for (var key in __INTRINSIC) {
652
+ if (__INTRINSIC.hasOwnProperty(key)) {
653
+ var m = __INTRINSIC[key].match;
654
+ for (var mi = 0; mi < m.length; mi++) { intrinsicMatch.push(m[mi]); }
655
+ }
656
+ }
657
+ var preservedNames = ["Motion", "Opacity", "Volume", "Channel Volume", "Panner", "Time Remapping"];
658
+ var removed = [];
659
+ // Remove from end to start to avoid index shifting
660
+ for (var i = clip.components.numItems - 1; i >= 0; i--) {
661
+ var comp = clip.components[i];
662
+ var isIntrinsic = false;
663
+ for (var j = 0; j < intrinsicMatch.length; j++) {
664
+ if (comp.matchName === intrinsicMatch[j]) { isIntrinsic = true; break; }
665
+ }
666
+ if (!isIntrinsic) {
667
+ for (var k = 0; k < preservedNames.length; k++) {
668
+ if (comp.displayName === preservedNames[k]) { isIntrinsic = true; break; }
669
+ }
670
+ }
671
+ if (!isIntrinsic) {
672
+ removed.push(comp.displayName);
673
+ comp.remove();
674
+ }
675
+ }
676
+ return __result({ removed: removed, removedCount: removed.length, clipName: clip.name });
677
677
  `);
678
678
  return sendCommand(script, bridgeOptions);
679
679
  },
@@ -691,46 +691,46 @@ export function getTrackManagementTools(bridgeOptions) {
691
691
  },
692
692
  handler: async (args) => {
693
693
  const duration = args.duration ?? 1.0;
694
- const script = buildToolScript(`
695
- app.enableQE();
696
- var qeSeq = qe.project.getActiveSequence();
697
- if (!qeSeq) return __error("No active sequence (QE)");
698
- var seq = app.project.activeSequence;
699
- if (!seq) return __error("No active sequence");
700
- var trackIdx = ${args.track_index};
701
- var transitionName = "${escapeForExtendScript(args.transition_name)}";
702
- var durationSecs = ${duration};
703
- var durationTicks = __secondsToTicks(durationSecs).toString();
704
-
705
- var transitions = qe.project.getVideoTransitionList();
706
- var transitionQE = null;
707
- for (var i = 0; i < transitions.numItems; i++) {
708
- if (transitions[i].name === transitionName) {
709
- transitionQE = transitions[i];
710
- break;
711
- }
712
- }
713
- if (!transitionQE) return __error("Transition not found: " + transitionName);
714
-
715
- var track = seq.videoTracks[trackIdx];
716
- if (!track) return __error("Video track not found at index " + trackIdx);
717
- var qeTrack = qeSeq.getVideoTrackAt(trackIdx);
718
- if (!qeTrack) return __error("QE track not found");
719
-
720
- var count = 0;
721
- for (var c = 0; c < track.clips.numItems - 1; c++) {
722
- var cutTicks = track.clips[c].end.ticks;
723
- try {
724
- qeTrack.addTransition(transitionQE, true, cutTicks, durationTicks, "0", false);
725
- count++;
726
- } catch(e) {}
727
- }
728
- return __result({
729
- added: count,
730
- transition: transitionName,
731
- trackIndex: trackIdx,
732
- durationSeconds: durationSecs
733
- });
694
+ const script = buildToolScript(`
695
+ app.enableQE();
696
+ var qeSeq = qe.project.getActiveSequence();
697
+ if (!qeSeq) return __error("No active sequence (QE)");
698
+ var seq = app.project.activeSequence;
699
+ if (!seq) return __error("No active sequence");
700
+ var trackIdx = ${args.track_index};
701
+ var transitionName = "${escapeForExtendScript(args.transition_name)}";
702
+ var durationSecs = ${duration};
703
+ var durationTicks = __secondsToTicks(durationSecs).toString();
704
+
705
+ var transitions = qe.project.getVideoTransitionList();
706
+ var transitionQE = null;
707
+ for (var i = 0; i < transitions.numItems; i++) {
708
+ if (transitions[i].name === transitionName) {
709
+ transitionQE = transitions[i];
710
+ break;
711
+ }
712
+ }
713
+ if (!transitionQE) return __error("Transition not found: " + transitionName);
714
+
715
+ var track = seq.videoTracks[trackIdx];
716
+ if (!track) return __error("Video track not found at index " + trackIdx);
717
+ var qeTrack = qeSeq.getVideoTrackAt(trackIdx);
718
+ if (!qeTrack) return __error("QE track not found");
719
+
720
+ var count = 0;
721
+ for (var c = 0; c < track.clips.numItems - 1; c++) {
722
+ var cutTicks = track.clips[c].end.ticks;
723
+ try {
724
+ qeTrack.addTransition(transitionQE, true, cutTicks, durationTicks, "0", false);
725
+ count++;
726
+ } catch(e) {}
727
+ }
728
+ return __result({
729
+ added: count,
730
+ transition: transitionName,
731
+ trackIndex: trackIdx,
732
+ durationSeconds: durationSecs
733
+ });
734
734
  `);
735
735
  return sendCommand(script, bridgeOptions);
736
736
  },
@@ -748,36 +748,36 @@ export function getTrackManagementTools(bridgeOptions) {
748
748
  required: ["track_type", "track_index", "start_seconds", "end_seconds"],
749
749
  },
750
750
  handler: async (args) => {
751
- const script = buildToolScript(`
752
- var seq = app.project.activeSequence;
753
- if (!seq) return __error("No active sequence");
754
- var trackType = "${escapeForExtendScript(args.track_type)}";
755
- var track = trackType === "video" ? seq.videoTracks[${args.track_index}] : seq.audioTracks[${args.track_index}];
756
- if (!track) return __error("Track not found at index ${args.track_index}");
757
- var startTicks = __secondsToTicks(${args.start_seconds});
758
- var endTicks = __secondsToTicks(${args.end_seconds});
759
- var toRemove = [];
760
- for (var i = 0; i < track.clips.numItems; i++) {
761
- var c = track.clips[i];
762
- var clipStart = parseFloat(c.start.ticks);
763
- var clipEnd = parseFloat(c.end.ticks);
764
- if (clipStart >= startTicks && clipEnd <= endTicks) {
765
- toRemove.push(i);
766
- }
767
- }
768
- var removed = [];
769
- // Remove from end to preserve indices
770
- for (var j = toRemove.length - 1; j >= 0; j--) {
771
- var clip = track.clips[toRemove[j]];
772
- removed.push(clip.name);
773
- clip.remove(false, false);
774
- }
775
- return __result({
776
- removed: removed,
777
- removedCount: removed.length,
778
- startSeconds: ${args.start_seconds},
779
- endSeconds: ${args.end_seconds}
780
- });
751
+ const script = buildToolScript(`
752
+ var seq = app.project.activeSequence;
753
+ if (!seq) return __error("No active sequence");
754
+ var trackType = "${escapeForExtendScript(args.track_type)}";
755
+ var track = trackType === "video" ? seq.videoTracks[${args.track_index}] : seq.audioTracks[${args.track_index}];
756
+ if (!track) return __error("Track not found at index ${args.track_index}");
757
+ var startTicks = __secondsToTicks(${args.start_seconds});
758
+ var endTicks = __secondsToTicks(${args.end_seconds});
759
+ var toRemove = [];
760
+ for (var i = 0; i < track.clips.numItems; i++) {
761
+ var c = track.clips[i];
762
+ var clipStart = parseFloat(c.start.ticks);
763
+ var clipEnd = parseFloat(c.end.ticks);
764
+ if (clipStart >= startTicks && clipEnd <= endTicks) {
765
+ toRemove.push(i);
766
+ }
767
+ }
768
+ var removed = [];
769
+ // Remove from end to preserve indices
770
+ for (var j = toRemove.length - 1; j >= 0; j--) {
771
+ var clip = track.clips[toRemove[j]];
772
+ removed.push(clip.name);
773
+ clip.remove(false, false);
774
+ }
775
+ return __result({
776
+ removed: removed,
777
+ removedCount: removed.length,
778
+ startSeconds: ${args.start_seconds},
779
+ endSeconds: ${args.end_seconds}
780
+ });
781
781
  `);
782
782
  return sendCommand(script, bridgeOptions);
783
783
  },