@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
@@ -19,20 +19,20 @@ export function getAudioTools(bridgeOptions) {
19
19
  required: ["node_id", "level_db"],
20
20
  },
21
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
- // Find the Volume component via locale-independent helper
28
- var volComp = __findIntrinsic(clip, "volume");
29
- if (!volComp) return __error("Could not find Volume component on clip");
30
-
31
- var levelProp = __findProp(volComp, ["Level", "Volume", "Nivel"]);
32
- if (!levelProp) return __error("Could not find Volume/Level property on clip");
33
-
34
- levelProp.setValue(${args.level_db}, true);
35
- return __result({ adjusted: true, clipName: clip.name, levelDb: ${args.level_db} });
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
+ // Find the Volume component via locale-independent helper
28
+ var volComp = __findIntrinsic(clip, "volume");
29
+ if (!volComp) return __error("Could not find Volume component on clip");
30
+
31
+ var levelProp = __findProp(volComp, ["Level", "Volume", "Nivel"]);
32
+ if (!levelProp) return __error("Could not find Volume/Level property on clip");
33
+
34
+ levelProp.setValue(${args.level_db}, true);
35
+ return __result({ adjusted: true, clipName: clip.name, levelDb: ${args.level_db} });
36
36
  `);
37
37
  return sendCommand(script, bridgeOptions);
38
38
  },
@@ -63,26 +63,26 @@ export function getAudioTools(bridgeOptions) {
63
63
  },
64
64
  handler: async (args) => {
65
65
  const keyframeCode = args.keyframes
66
- .map((kf) => `
67
- var kfT = new Time(); kfT.ticks = String(__secondsToTicks(${kf.time_seconds}));
68
- levelProp.addKey(kfT);
66
+ .map((kf) => `
67
+ var kfT = new Time(); kfT.ticks = String(__secondsToTicks(${kf.time_seconds}));
68
+ levelProp.addKey(kfT);
69
69
  levelProp.setValueAtKey(kfT, ${kf.level_db});`)
70
70
  .join("\n");
71
- const script = buildToolScript(`
72
- var result = __findClip("${escapeForExtendScript(args.node_id)}");
73
- if (!result) return __error("Clip not found");
74
-
75
- var clip = result.clip;
76
- var volComp = __findIntrinsic(clip, "volume");
77
- if (!volComp) return __error("Could not find audio Volume component");
78
- var levelProp = __findProp(volComp, ["Level", "Volume", "Nivel"]);
79
-
80
- if (!levelProp) return __error("Could not find audio Level property");
81
-
82
- levelProp.setTimeVarying(true);
83
- ${keyframeCode}
84
-
85
- return __result({ keyframesAdded: ${args.keyframes.length}, clipName: clip.name });
71
+ const script = buildToolScript(`
72
+ var result = __findClip("${escapeForExtendScript(args.node_id)}");
73
+ if (!result) return __error("Clip not found");
74
+
75
+ var clip = result.clip;
76
+ var volComp = __findIntrinsic(clip, "volume");
77
+ if (!volComp) return __error("Could not find audio Volume component");
78
+ var levelProp = __findProp(volComp, ["Level", "Volume", "Nivel"]);
79
+
80
+ if (!levelProp) return __error("Could not find audio Level property");
81
+
82
+ levelProp.setTimeVarying(true);
83
+ ${keyframeCode}
84
+
85
+ return __result({ keyframesAdded: ${args.keyframes.length}, clipName: clip.name });
86
86
  `);
87
87
  return sendCommand(script, bridgeOptions);
88
88
  },
@@ -104,16 +104,16 @@ export function getAudioTools(bridgeOptions) {
104
104
  required: ["track_index", "muted"],
105
105
  },
106
106
  handler: async (args) => {
107
- const script = buildToolScript(`
108
- var seq = app.project.activeSequence;
109
- if (!seq) return __error("No active sequence");
110
-
111
- if (${args.track_index} >= seq.audioTracks.numTracks) return __error("Track index out of range");
112
-
113
- var track = seq.audioTracks[${args.track_index}];
114
- track.setMute(${args.muted ? 1 : 0});
115
-
116
- return __result({ trackIndex: ${args.track_index}, muted: ${args.muted}, trackName: track.name });
107
+ const script = buildToolScript(`
108
+ var seq = app.project.activeSequence;
109
+ if (!seq) return __error("No active sequence");
110
+
111
+ if (${args.track_index} >= seq.audioTracks.numTracks) return __error("Track index out of range");
112
+
113
+ var track = seq.audioTracks[${args.track_index}];
114
+ track.setMute(${args.muted ? 1 : 0});
115
+
116
+ return __result({ trackIndex: ${args.track_index}, muted: ${args.muted}, trackName: track.name });
117
117
  `);
118
118
  return sendCommand(script, bridgeOptions);
119
119
  },
@@ -37,33 +37,33 @@ export function getCaptionTools(bridgeOptions) {
37
37
  };
38
38
  const formatKey = args.caption_format || "subtitle";
39
39
  const formatEntry = formatMap[formatKey] || formatMap["subtitle"];
40
- const script = buildToolScript(`
41
- var seq = app.project.activeSequence;
42
- if (!seq) return __error("No active sequence");
43
-
44
- var item = __findProjectItem("${escapeForExtendScript(args.item_id)}");
45
- if (!item) return __error("Caption item not found: ${escapeForExtendScript(args.item_id)}");
46
-
47
- // Resolve the caption format constant defensively. The Sequence.CAPTION_FORMAT_*
48
- // constants may be undefined depending on the Premiere version, so fall back to
49
- // the known numeric value if the constant cannot be read.
50
- var captionFormat = ${formatEntry.fallback};
51
- try {
52
- if (typeof Sequence !== "undefined" && Sequence && typeof Sequence.${formatEntry.constant} !== "undefined") {
53
- captionFormat = Sequence.${formatEntry.constant};
54
- }
55
- } catch (eFmt) {
56
- captionFormat = ${formatEntry.fallback};
57
- }
58
-
59
- // createCaptionTrack may return undefined on success, so treat only a thrown
60
- // error as a real failure.
61
- try {
62
- seq.createCaptionTrack(item, ${startSeconds}, captionFormat);
63
- } catch (eCreate) {
64
- return __error("Failed to create caption track: " + eCreate.toString());
65
- }
66
- return __result({ created: true, item: item.name, startSeconds: ${startSeconds}, format: "${formatKey}" });
40
+ const script = buildToolScript(`
41
+ var seq = app.project.activeSequence;
42
+ if (!seq) return __error("No active sequence");
43
+
44
+ var item = __findProjectItem("${escapeForExtendScript(args.item_id)}");
45
+ if (!item) return __error("Caption item not found: ${escapeForExtendScript(args.item_id)}");
46
+
47
+ // Resolve the caption format constant defensively. The Sequence.CAPTION_FORMAT_*
48
+ // constants may be undefined depending on the Premiere version, so fall back to
49
+ // the known numeric value if the constant cannot be read.
50
+ var captionFormat = ${formatEntry.fallback};
51
+ try {
52
+ if (typeof Sequence !== "undefined" && Sequence && typeof Sequence.${formatEntry.constant} !== "undefined") {
53
+ captionFormat = Sequence.${formatEntry.constant};
54
+ }
55
+ } catch (eFmt) {
56
+ captionFormat = ${formatEntry.fallback};
57
+ }
58
+
59
+ // createCaptionTrack may return undefined on success, so treat only a thrown
60
+ // error as a real failure.
61
+ try {
62
+ seq.createCaptionTrack(item, ${startSeconds}, captionFormat);
63
+ } catch (eCreate) {
64
+ return __error("Failed to create caption track: " + eCreate.toString());
65
+ }
66
+ return __result({ created: true, item: item.name, startSeconds: ${startSeconds}, format: "${formatKey}" });
67
67
  `);
68
68
  return sendCommand(script, bridgeOptions);
69
69
  },
@@ -23,57 +23,57 @@ export function getClipboardTools(bridgeOptions) {
23
23
  required: ["source_node_id", "target_node_id"],
24
24
  },
25
25
  handler: async (args) => {
26
- const script = buildToolScript(`
27
- app.enableQE();
28
- var srcResult = __findClip("${escapeForExtendScript(args.source_node_id)}");
29
- if (!srcResult) return __error("Source clip not found");
30
- var tgtResult = __findClip("${escapeForExtendScript(args.target_node_id)}");
31
- if (!tgtResult) return __error("Target clip not found");
32
-
33
- var src = srcResult.clip;
34
- var tgt = tgtResult.clip;
35
- var copied = 0;
36
- var effectFilter = ${args.effect_name ? `"${escapeForExtendScript(args.effect_name)}"` : "null"};
37
- var intrinsic = ["Motion", "Opacity", "Time Remapping", "Volume", "Channel Volume", "Panner"];
38
-
39
- // Use QE to copy effects by name
40
- var qeSeq = qe.project.getActiveSequence();
41
- var tgtTrackType = tgtResult.trackType;
42
- var tgtTrack = tgtTrackType === "video"
43
- ? qeSeq.getVideoTrackAt(tgtResult.trackIndex)
44
- : qeSeq.getAudioTrackAt(tgtResult.trackIndex);
45
- var qeTgtClip = tgtTrack.getItemAt(tgtResult.clipIndex);
46
-
47
- for (var i = 0; i < src.components.numItems; i++) {
48
- var comp = src.components[i];
49
- var name = comp.displayName;
50
-
51
- if (effectFilter && name !== effectFilter) continue;
52
- if (!effectFilter) {
53
- var skip = false;
54
- for (var k = 0; k < intrinsic.length; k++) {
55
- if (name === intrinsic[k]) { skip = true; break; }
56
- }
57
- if (skip) continue;
58
- }
59
-
60
- // Apply effect via QE
61
- try {
62
- var qeEffect = tgtTrackType === "video"
63
- ? qe.project.getVideoEffectByName(name)
64
- : qe.project.getAudioEffectByName(name);
65
- if (qeEffect) {
66
- if (tgtTrackType === "video") {
67
- qeTgtClip.addVideoEffect(qeEffect);
68
- } else {
69
- qeTgtClip.addAudioEffect(qeEffect);
70
- }
71
- copied++;
72
- }
73
- } catch(e) {}
74
- }
75
-
76
- return __result({ copiedEffects: copied, source: src.name, target: tgt.name });
26
+ const script = buildToolScript(`
27
+ app.enableQE();
28
+ var srcResult = __findClip("${escapeForExtendScript(args.source_node_id)}");
29
+ if (!srcResult) return __error("Source clip not found");
30
+ var tgtResult = __findClip("${escapeForExtendScript(args.target_node_id)}");
31
+ if (!tgtResult) return __error("Target clip not found");
32
+
33
+ var src = srcResult.clip;
34
+ var tgt = tgtResult.clip;
35
+ var copied = 0;
36
+ var effectFilter = ${args.effect_name ? `"${escapeForExtendScript(args.effect_name)}"` : "null"};
37
+ var intrinsic = ["Motion", "Opacity", "Time Remapping", "Volume", "Channel Volume", "Panner"];
38
+
39
+ // Use QE to copy effects by name
40
+ var qeSeq = qe.project.getActiveSequence();
41
+ var tgtTrackType = tgtResult.trackType;
42
+ var tgtTrack = tgtTrackType === "video"
43
+ ? qeSeq.getVideoTrackAt(tgtResult.trackIndex)
44
+ : qeSeq.getAudioTrackAt(tgtResult.trackIndex);
45
+ var qeTgtClip = tgtTrack.getItemAt(tgtResult.clipIndex);
46
+
47
+ for (var i = 0; i < src.components.numItems; i++) {
48
+ var comp = src.components[i];
49
+ var name = comp.displayName;
50
+
51
+ if (effectFilter && name !== effectFilter) continue;
52
+ if (!effectFilter) {
53
+ var skip = false;
54
+ for (var k = 0; k < intrinsic.length; k++) {
55
+ if (name === intrinsic[k]) { skip = true; break; }
56
+ }
57
+ if (skip) continue;
58
+ }
59
+
60
+ // Apply effect via QE
61
+ try {
62
+ var qeEffect = tgtTrackType === "video"
63
+ ? qe.project.getVideoEffectByName(name)
64
+ : qe.project.getAudioEffectByName(name);
65
+ if (qeEffect) {
66
+ if (tgtTrackType === "video") {
67
+ qeTgtClip.addVideoEffect(qeEffect);
68
+ } else {
69
+ qeTgtClip.addAudioEffect(qeEffect);
70
+ }
71
+ copied++;
72
+ }
73
+ } catch(e) {}
74
+ }
75
+
76
+ return __result({ copiedEffects: copied, source: src.name, target: tgt.name });
77
77
  `);
78
78
  return sendCommand(script, bridgeOptions);
79
79
  },
@@ -99,46 +99,46 @@ export function getClipboardTools(bridgeOptions) {
99
99
  required: ["source_node_id", "target_node_id", "effect_name"],
100
100
  },
101
101
  handler: async (args) => {
102
- const script = buildToolScript(`
103
- var srcResult = __findClip("${escapeForExtendScript(args.source_node_id)}");
104
- if (!srcResult) return __error("Source clip not found");
105
- var tgtResult = __findClip("${escapeForExtendScript(args.target_node_id)}");
106
- if (!tgtResult) return __error("Target clip not found");
107
-
108
- var srcComp = null;
109
- var tgtComp = null;
110
- for (var i = 0; i < srcResult.clip.components.numItems; i++) {
111
- if (srcResult.clip.components[i].displayName === "${escapeForExtendScript(args.effect_name)}") {
112
- srcComp = srcResult.clip.components[i];
113
- break;
114
- }
115
- }
116
- if (!srcComp) return __error("Effect not found on source clip: ${escapeForExtendScript(args.effect_name)}");
117
-
118
- for (var i = 0; i < tgtResult.clip.components.numItems; i++) {
119
- if (tgtResult.clip.components[i].displayName === "${escapeForExtendScript(args.effect_name)}") {
120
- tgtComp = tgtResult.clip.components[i];
121
- break;
122
- }
123
- }
124
- if (!tgtComp) return __error("Effect not found on target clip: ${escapeForExtendScript(args.effect_name)}");
125
-
126
- var copied = 0;
127
- for (var p = 0; p < srcComp.properties.numItems; p++) {
128
- var srcProp = srcComp.properties[p];
129
- for (var q = 0; q < tgtComp.properties.numItems; q++) {
130
- if (tgtComp.properties[q].displayName === srcProp.displayName) {
131
- try {
132
- var val = srcProp.getValue(0, 0);
133
- tgtComp.properties[q].setValue(val, true);
134
- copied++;
135
- } catch(e) {}
136
- break;
137
- }
138
- }
139
- }
140
-
141
- return __result({ copiedProperties: copied, effect: "${escapeForExtendScript(args.effect_name)}" });
102
+ const script = buildToolScript(`
103
+ var srcResult = __findClip("${escapeForExtendScript(args.source_node_id)}");
104
+ if (!srcResult) return __error("Source clip not found");
105
+ var tgtResult = __findClip("${escapeForExtendScript(args.target_node_id)}");
106
+ if (!tgtResult) return __error("Target clip not found");
107
+
108
+ var srcComp = null;
109
+ var tgtComp = null;
110
+ for (var i = 0; i < srcResult.clip.components.numItems; i++) {
111
+ if (srcResult.clip.components[i].displayName === "${escapeForExtendScript(args.effect_name)}") {
112
+ srcComp = srcResult.clip.components[i];
113
+ break;
114
+ }
115
+ }
116
+ if (!srcComp) return __error("Effect not found on source clip: ${escapeForExtendScript(args.effect_name)}");
117
+
118
+ for (var i = 0; i < tgtResult.clip.components.numItems; i++) {
119
+ if (tgtResult.clip.components[i].displayName === "${escapeForExtendScript(args.effect_name)}") {
120
+ tgtComp = tgtResult.clip.components[i];
121
+ break;
122
+ }
123
+ }
124
+ if (!tgtComp) return __error("Effect not found on target clip: ${escapeForExtendScript(args.effect_name)}");
125
+
126
+ var copied = 0;
127
+ for (var p = 0; p < srcComp.properties.numItems; p++) {
128
+ var srcProp = srcComp.properties[p];
129
+ for (var q = 0; q < tgtComp.properties.numItems; q++) {
130
+ if (tgtComp.properties[q].displayName === srcProp.displayName) {
131
+ try {
132
+ var val = srcProp.getValue(0, 0);
133
+ tgtComp.properties[q].setValue(val, true);
134
+ copied++;
135
+ } catch(e) {}
136
+ break;
137
+ }
138
+ }
139
+ }
140
+
141
+ return __result({ copiedProperties: copied, effect: "${escapeForExtendScript(args.effect_name)}" });
142
142
  `);
143
143
  return sendCommand(script, bridgeOptions);
144
144
  },
@@ -160,26 +160,26 @@ export function getClipboardTools(bridgeOptions) {
160
160
  required: ["clip_node_id", "new_item_id"],
161
161
  },
162
162
  handler: async (args) => {
163
- const script = buildToolScript(`
164
- var clipResult = __findClip("${escapeForExtendScript(args.clip_node_id)}");
165
- if (!clipResult) return __error("Clip not found");
166
- var newItem = __findProjectItem("${escapeForExtendScript(args.new_item_id)}");
167
- if (!newItem) return __error("New project item not found: ${escapeForExtendScript(args.new_item_id)}");
168
-
169
- var clip = clipResult.clip;
170
- var seq = app.project.activeSequence;
171
- var startTicks = clip.start.ticks.toString();
172
- var trackType = clipResult.trackType;
173
- var trackIndex = clipResult.trackIndex;
174
-
175
- // Overwrite at the same position with new media
176
- if (trackType === "video") {
177
- seq.overwriteClip(newItem, startTicks, trackIndex, -1);
178
- } else {
179
- seq.overwriteClip(newItem, startTicks, -1, trackIndex);
180
- }
181
-
182
- return __result({ replaced: true, clipName: clip.name, newSource: newItem.name });
163
+ const script = buildToolScript(`
164
+ var clipResult = __findClip("${escapeForExtendScript(args.clip_node_id)}");
165
+ if (!clipResult) return __error("Clip not found");
166
+ var newItem = __findProjectItem("${escapeForExtendScript(args.new_item_id)}");
167
+ if (!newItem) return __error("New project item not found: ${escapeForExtendScript(args.new_item_id)}");
168
+
169
+ var clip = clipResult.clip;
170
+ var seq = app.project.activeSequence;
171
+ var startTicks = clip.start.ticks.toString();
172
+ var trackType = clipResult.trackType;
173
+ var trackIndex = clipResult.trackIndex;
174
+
175
+ // Overwrite at the same position with new media
176
+ if (trackType === "video") {
177
+ seq.overwriteClip(newItem, startTicks, trackIndex, -1);
178
+ } else {
179
+ seq.overwriteClip(newItem, startTicks, -1, trackIndex);
180
+ }
181
+
182
+ return __result({ replaced: true, clipName: clip.name, newSource: newItem.name });
183
183
  `);
184
184
  return sendCommand(script, bridgeOptions);
185
185
  },
@@ -211,66 +211,66 @@ export function getClipboardTools(bridgeOptions) {
211
211
  required: ["effect_name", "target"],
212
212
  },
213
213
  handler: async (args) => {
214
- const script = buildToolScript(`
215
- app.enableQE();
216
- var seq = app.project.activeSequence;
217
- if (!seq) return __error("No active sequence");
218
- var qeSeq = qe.project.getActiveSequence();
219
-
220
- var effectName = "${escapeForExtendScript(args.effect_name)}";
221
- var qeEffect = qe.project.getVideoEffectByName(effectName);
222
- var isAudio = false;
223
- if (!qeEffect) {
224
- qeEffect = qe.project.getAudioEffectByName(effectName);
225
- isAudio = true;
226
- }
227
- if (!qeEffect) return __error("Effect not found: " + effectName);
228
-
229
- var applied = 0;
230
- var target = "${args.target}";
231
-
232
- function applyToClip(trackIdx, clipIdx, trackType) {
233
- try {
234
- var qeTrack = trackType === "video" ? qeSeq.getVideoTrackAt(trackIdx) : qeSeq.getAudioTrackAt(trackIdx);
235
- var qeClip = qeTrack.getItemAt(clipIdx);
236
- if (isAudio || trackType === "audio") {
237
- qeClip.addAudioEffect(qeEffect);
238
- } else {
239
- qeClip.addVideoEffect(qeEffect);
240
- }
241
- applied++;
242
- } catch(e) {}
243
- }
244
-
245
- if (target === "selected") {
246
- for (var t = 0; t < seq.videoTracks.numTracks; t++) {
247
- for (var c = 0; c < seq.videoTracks[t].clips.numItems; c++) {
248
- if (seq.videoTracks[t].clips[c].isSelected()) applyToClip(t, c, "video");
249
- }
250
- }
251
- for (var t = 0; t < seq.audioTracks.numTracks; t++) {
252
- for (var c = 0; c < seq.audioTracks[t].clips.numItems; c++) {
253
- if (seq.audioTracks[t].clips[c].isSelected()) applyToClip(t, c, "audio");
254
- }
255
- }
256
- } else if (target === "track") {
257
- var tt = "${args.track_type || "video"}";
258
- var ti = ${args.track_index ?? 0};
259
- var tracks = tt === "video" ? seq.videoTracks : seq.audioTracks;
260
- if (ti >= tracks.numTracks) return __error("Track index out of range");
261
- for (var c = 0; c < tracks[ti].clips.numItems; c++) {
262
- applyToClip(ti, c, tt);
263
- }
264
- } else {
265
- for (var t = 0; t < seq.videoTracks.numTracks; t++) {
266
- for (var c = 0; c < seq.videoTracks[t].clips.numItems; c++) applyToClip(t, c, "video");
267
- }
268
- for (var t = 0; t < seq.audioTracks.numTracks; t++) {
269
- for (var c = 0; c < seq.audioTracks[t].clips.numItems; c++) applyToClip(t, c, "audio");
270
- }
271
- }
272
-
273
- return __result({ applied: applied, effect: effectName, target: target });
214
+ const script = buildToolScript(`
215
+ app.enableQE();
216
+ var seq = app.project.activeSequence;
217
+ if (!seq) return __error("No active sequence");
218
+ var qeSeq = qe.project.getActiveSequence();
219
+
220
+ var effectName = "${escapeForExtendScript(args.effect_name)}";
221
+ var qeEffect = qe.project.getVideoEffectByName(effectName);
222
+ var isAudio = false;
223
+ if (!qeEffect) {
224
+ qeEffect = qe.project.getAudioEffectByName(effectName);
225
+ isAudio = true;
226
+ }
227
+ if (!qeEffect) return __error("Effect not found: " + effectName);
228
+
229
+ var applied = 0;
230
+ var target = "${args.target}";
231
+
232
+ function applyToClip(trackIdx, clipIdx, trackType) {
233
+ try {
234
+ var qeTrack = trackType === "video" ? qeSeq.getVideoTrackAt(trackIdx) : qeSeq.getAudioTrackAt(trackIdx);
235
+ var qeClip = qeTrack.getItemAt(clipIdx);
236
+ if (isAudio || trackType === "audio") {
237
+ qeClip.addAudioEffect(qeEffect);
238
+ } else {
239
+ qeClip.addVideoEffect(qeEffect);
240
+ }
241
+ applied++;
242
+ } catch(e) {}
243
+ }
244
+
245
+ if (target === "selected") {
246
+ for (var t = 0; t < seq.videoTracks.numTracks; t++) {
247
+ for (var c = 0; c < seq.videoTracks[t].clips.numItems; c++) {
248
+ if (seq.videoTracks[t].clips[c].isSelected()) applyToClip(t, c, "video");
249
+ }
250
+ }
251
+ for (var t = 0; t < seq.audioTracks.numTracks; t++) {
252
+ for (var c = 0; c < seq.audioTracks[t].clips.numItems; c++) {
253
+ if (seq.audioTracks[t].clips[c].isSelected()) applyToClip(t, c, "audio");
254
+ }
255
+ }
256
+ } else if (target === "track") {
257
+ var tt = "${args.track_type || "video"}";
258
+ var ti = ${args.track_index ?? 0};
259
+ var tracks = tt === "video" ? seq.videoTracks : seq.audioTracks;
260
+ if (ti >= tracks.numTracks) return __error("Track index out of range");
261
+ for (var c = 0; c < tracks[ti].clips.numItems; c++) {
262
+ applyToClip(ti, c, tt);
263
+ }
264
+ } else {
265
+ for (var t = 0; t < seq.videoTracks.numTracks; t++) {
266
+ for (var c = 0; c < seq.videoTracks[t].clips.numItems; c++) applyToClip(t, c, "video");
267
+ }
268
+ for (var t = 0; t < seq.audioTracks.numTracks; t++) {
269
+ for (var c = 0; c < seq.audioTracks[t].clips.numItems; c++) applyToClip(t, c, "audio");
270
+ }
271
+ }
272
+
273
+ return __result({ applied: applied, effect: effectName, target: target });
274
274
  `);
275
275
  return sendCommand(script, bridgeOptions);
276
276
  },
@@ -292,22 +292,22 @@ export function getClipboardTools(bridgeOptions) {
292
292
  required: ["node_id", "effect_name"],
293
293
  },
294
294
  handler: async (args) => {
295
- const script = buildToolScript(`
296
- var result = __findClip("${escapeForExtendScript(args.node_id)}");
297
- if (!result) return __error("Clip not found");
298
-
299
- var clip = result.clip;
300
- var removed = 0;
301
- // Iterate backwards to avoid index issues when removing
302
- for (var i = clip.components.numItems - 1; i >= 0; i--) {
303
- if (clip.components[i].displayName === "${escapeForExtendScript(args.effect_name)}") {
304
- clip.components[i].remove();
305
- removed++;
306
- }
307
- }
308
-
309
- if (removed === 0) return __error("Effect not found: ${escapeForExtendScript(args.effect_name)}");
310
- return __result({ removed: removed, effect: "${escapeForExtendScript(args.effect_name)}", clip: clip.name });
295
+ const script = buildToolScript(`
296
+ var result = __findClip("${escapeForExtendScript(args.node_id)}");
297
+ if (!result) return __error("Clip not found");
298
+
299
+ var clip = result.clip;
300
+ var removed = 0;
301
+ // Iterate backwards to avoid index issues when removing
302
+ for (var i = clip.components.numItems - 1; i >= 0; i--) {
303
+ if (clip.components[i].displayName === "${escapeForExtendScript(args.effect_name)}") {
304
+ clip.components[i].remove();
305
+ removed++;
306
+ }
307
+ }
308
+
309
+ if (removed === 0) return __error("Effect not found: ${escapeForExtendScript(args.effect_name)}");
310
+ return __result({ removed: removed, effect: "${escapeForExtendScript(args.effect_name)}", clip: clip.name });
311
311
  `);
312
312
  return sendCommand(script, bridgeOptions);
313
313
  },
@@ -345,33 +345,33 @@ export function getClipboardTools(bridgeOptions) {
345
345
  "Hue": 24, "Saturation": 25, "Color": 26, "Luminosity": 27
346
346
  };
347
347
  const modeValue = blendModeMap[args.blend_mode] ?? 1;
348
- const script = buildToolScript(`
349
- var result = __findClip("${escapeForExtendScript(args.node_id)}");
350
- if (!result) return __error("Clip not found");
351
-
352
- var clip = result.clip;
353
- var comp = __findIntrinsic(clip, "opacity");
354
- if (!comp) return __error("Could not find Opacity component on clip");
355
-
356
- var prop = __findProp(comp, ["Blend Mode", "Modo de fusión", "Mischmodus"]);
357
- if (!prop) return __error("Could not find Blend Mode property on Opacity component");
358
-
359
- var applied = false;
360
- var note = "Blend mode enum ordering can vary by Premiere version/locale; if the visual result is wrong, the numeric mapping may differ for this version.";
361
- try {
362
- prop.setValue(${modeValue}, true);
363
- applied = true;
364
- } catch(e) {
365
- note = "setValue failed: " + e.toString() + ". " + note;
366
- }
367
-
368
- return __result({
369
- blendMode: "${escapeForExtendScript(args.blend_mode)}",
370
- enumValue: ${modeValue},
371
- applied: applied,
372
- note: note,
373
- clip: clip.name
374
- });
348
+ const script = buildToolScript(`
349
+ var result = __findClip("${escapeForExtendScript(args.node_id)}");
350
+ if (!result) return __error("Clip not found");
351
+
352
+ var clip = result.clip;
353
+ var comp = __findIntrinsic(clip, "opacity");
354
+ if (!comp) return __error("Could not find Opacity component on clip");
355
+
356
+ var prop = __findProp(comp, ["Blend Mode", "Modo de fusión", "Mischmodus"]);
357
+ if (!prop) return __error("Could not find Blend Mode property on Opacity component");
358
+
359
+ var applied = false;
360
+ var note = "Blend mode enum ordering can vary by Premiere version/locale; if the visual result is wrong, the numeric mapping may differ for this version.";
361
+ try {
362
+ prop.setValue(${modeValue}, true);
363
+ applied = true;
364
+ } catch(e) {
365
+ note = "setValue failed: " + e.toString() + ". " + note;
366
+ }
367
+
368
+ return __result({
369
+ blendMode: "${escapeForExtendScript(args.blend_mode)}",
370
+ enumValue: ${modeValue},
371
+ applied: applied,
372
+ note: note,
373
+ clip: clip.name
374
+ });
375
375
  `);
376
376
  return sendCommand(script, bridgeOptions);
377
377
  },