@synthetic-ai/premiere-mcp 2.2.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 (57) hide show
  1. package/CHANGELOG.md +135 -135
  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/license.js +38 -0
  12. package/dist/server.js +60 -60
  13. package/dist/tools/advanced.js +357 -357
  14. package/dist/tools/audio-advanced.js +1049 -1049
  15. package/dist/tools/audio.js +42 -42
  16. package/dist/tools/captions.js +27 -27
  17. package/dist/tools/clipboard.js +214 -214
  18. package/dist/tools/color-advanced.js +253 -253
  19. package/dist/tools/discovery.js +291 -291
  20. package/dist/tools/effects.js +285 -285
  21. package/dist/tools/export-presets.js +206 -206
  22. package/dist/tools/export.js +255 -255
  23. package/dist/tools/health.js +10 -10
  24. package/dist/tools/inspection.js +885 -885
  25. package/dist/tools/keyframes.js +369 -369
  26. package/dist/tools/markers.js +89 -89
  27. package/dist/tools/media.js +178 -178
  28. package/dist/tools/metadata.js +94 -94
  29. package/dist/tools/multicam.js +50 -50
  30. package/dist/tools/playback.js +15 -15
  31. package/dist/tools/playhead.js +95 -95
  32. package/dist/tools/project-manager.js +21 -21
  33. package/dist/tools/project.js +149 -149
  34. package/dist/tools/scripting.js +319 -319
  35. package/dist/tools/selection.js +202 -202
  36. package/dist/tools/sequence-advanced.js +379 -379
  37. package/dist/tools/sequence-settings.js +564 -564
  38. package/dist/tools/sequence.js +167 -167
  39. package/dist/tools/source-monitor.js +68 -68
  40. package/dist/tools/text.js +58 -58
  41. package/dist/tools/timeline.js +312 -312
  42. package/dist/tools/track-management.js +446 -446
  43. package/dist/tools/track-targeting.js +635 -635
  44. package/dist/tools/tracks.js +87 -87
  45. package/dist/tools/transitions-motion.js +174 -174
  46. package/dist/tools/transitions.js +111 -111
  47. package/dist/tools/utility.js +606 -606
  48. package/dist/tools/workspace.js +29 -29
  49. package/package.json +66 -66
  50. package/scripts/build-chat-dmg.sh +210 -210
  51. package/scripts/build-chat-zip.sh +291 -291
  52. package/scripts/install-cep-windows.ps1 +99 -99
  53. package/scripts/install-cep.sh +66 -66
  54. package/scripts/install-chat-plugin.bat +67 -67
  55. package/scripts/install-chat-plugin.sh +85 -85
  56. package/scripts/install-windows.bat +95 -95
  57. package/scripts/package.mjs +254 -254
@@ -3,15 +3,15 @@ import { sendCommand } from "../bridge/file-bridge.js";
3
3
  export function getEffectsTools(bridgeOptions) {
4
4
  return {
5
5
  apply_effect: {
6
- description: `Apply a video effect to a clip by name. Uses QE DOM for effect lookup.
7
-
8
- Preconditions: Active sequence required. The clip must exist on the timeline. Use list_available_effects to find exact effect names before calling this tool.
9
-
10
- Common effect names: "Gaussian Blur", "Lumetri Color", "Warp Stabilizer", "Unsharp Mask", "Fast Color Corrector", "Brightness & Contrast", "Hue/Saturation", "Sharpen", "Video Limiter", "Timecode".
11
-
12
- After applying, use get_effect_properties to see the effect's parameters, then set_effect_property or add_keyframe to adjust values.
13
-
14
- Example: apply_effect({ node_id: "abc123", effect_name: "Gaussian Blur" })
6
+ description: `Apply a video effect to a clip by name. Uses QE DOM for effect lookup.
7
+
8
+ Preconditions: Active sequence required. The clip must exist on the timeline. Use list_available_effects to find exact effect names before calling this tool.
9
+
10
+ Common effect names: "Gaussian Blur", "Lumetri Color", "Warp Stabilizer", "Unsharp Mask", "Fast Color Corrector", "Brightness & Contrast", "Hue/Saturation", "Sharpen", "Video Limiter", "Timecode".
11
+
12
+ After applying, use get_effect_properties to see the effect's parameters, then set_effect_property or add_keyframe to adjust values.
13
+
14
+ Example: apply_effect({ node_id: "abc123", effect_name: "Gaussian Blur" })
15
15
  Example: apply_effect({ node_id: "abc123", effect_name: "Lumetri Color" })`,
16
16
  parameters: {
17
17
  type: "object",
@@ -28,38 +28,38 @@ Example: apply_effect({ node_id: "abc123", effect_name: "Lumetri Color" })`,
28
28
  required: ["node_id", "effect_name"],
29
29
  },
30
30
  handler: async (args) => {
31
- const script = buildToolScript(`
32
- app.enableQE();
33
- var qeSeq = qe.project.getActiveSequence();
34
- if (!qeSeq) return __error("No active sequence (QE)");
35
-
36
- var result = __findClip("${escapeForExtendScript(args.node_id)}");
37
- if (!result) return __error("Clip not found: ${escapeForExtendScript(args.node_id)}");
38
-
39
- // Find the effect in QE
40
- var effectName = "${escapeForExtendScript(args.effect_name)}";
41
- var qeTrack = result.trackType === "video"
42
- ? qeSeq.getVideoTrackAt(result.trackIndex)
43
- : qeSeq.getAudioTrackAt(result.trackIndex);
44
-
45
- if (!qeTrack) return __error("QE track not found");
46
-
47
- var qeClip = qeTrack.getItemAt(result.clipIndex);
48
- if (!qeClip) return __error("QE clip not found");
49
-
50
- // Search for the effect
51
- var effects = qe.project.getVideoEffectList();
52
- var found = false;
53
- for (var i = 0; i < effects.numItems; i++) {
54
- if (effects[i].name === effectName) {
55
- qeClip.addVideoEffect(effects[i]);
56
- found = true;
57
- break;
58
- }
59
- }
60
-
61
- if (!found) return __error("Effect not found: " + effectName);
62
- return __result({ applied: true, effect: effectName, clipName: result.clip.name });
31
+ const script = buildToolScript(`
32
+ app.enableQE();
33
+ var qeSeq = qe.project.getActiveSequence();
34
+ if (!qeSeq) return __error("No active sequence (QE)");
35
+
36
+ var result = __findClip("${escapeForExtendScript(args.node_id)}");
37
+ if (!result) return __error("Clip not found: ${escapeForExtendScript(args.node_id)}");
38
+
39
+ // Find the effect in QE
40
+ var effectName = "${escapeForExtendScript(args.effect_name)}";
41
+ var qeTrack = result.trackType === "video"
42
+ ? qeSeq.getVideoTrackAt(result.trackIndex)
43
+ : qeSeq.getAudioTrackAt(result.trackIndex);
44
+
45
+ if (!qeTrack) return __error("QE track not found");
46
+
47
+ var qeClip = qeTrack.getItemAt(result.clipIndex);
48
+ if (!qeClip) return __error("QE clip not found");
49
+
50
+ // Search for the effect
51
+ var effects = qe.project.getVideoEffectList();
52
+ var found = false;
53
+ for (var i = 0; i < effects.numItems; i++) {
54
+ if (effects[i].name === effectName) {
55
+ qeClip.addVideoEffect(effects[i]);
56
+ found = true;
57
+ break;
58
+ }
59
+ }
60
+
61
+ if (!found) return __error("Effect not found: " + effectName);
62
+ return __result({ applied: true, effect: effectName, clipName: result.clip.name });
63
63
  `);
64
64
  return sendCommand(script, bridgeOptions);
65
65
  },
@@ -81,34 +81,34 @@ Example: apply_effect({ node_id: "abc123", effect_name: "Lumetri Color" })`,
81
81
  required: ["node_id", "effect_name"],
82
82
  },
83
83
  handler: async (args) => {
84
- const script = buildToolScript(`
85
- app.enableQE();
86
- var qeSeq = qe.project.getActiveSequence();
87
- if (!qeSeq) return __error("No active sequence (QE)");
88
-
89
- var result = __findClip("${escapeForExtendScript(args.node_id)}");
90
- if (!result) return __error("Clip not found: ${escapeForExtendScript(args.node_id)}");
91
- if (result.trackType !== "audio") return __error("node_id is not an audio clip");
92
-
93
- var effectName = "${escapeForExtendScript(args.effect_name)}";
94
- var qeTrack = qeSeq.getAudioTrackAt(result.trackIndex);
95
- if (!qeTrack) return __error("QE audio track not found");
96
-
97
- var qeClip = qeTrack.getItemAt(result.clipIndex);
98
- if (!qeClip) return __error("QE clip not found");
99
-
100
- var effects = qe.project.getAudioEffectList();
101
- var found = false;
102
- for (var i = 0; i < effects.numItems; i++) {
103
- if (effects[i].name === effectName) {
104
- qeClip.addAudioEffect(effects[i]);
105
- found = true;
106
- break;
107
- }
108
- }
109
-
110
- if (!found) return __error("Audio effect not found: " + effectName);
111
- return __result({ applied: true, effect: effectName });
84
+ const script = buildToolScript(`
85
+ app.enableQE();
86
+ var qeSeq = qe.project.getActiveSequence();
87
+ if (!qeSeq) return __error("No active sequence (QE)");
88
+
89
+ var result = __findClip("${escapeForExtendScript(args.node_id)}");
90
+ if (!result) return __error("Clip not found: ${escapeForExtendScript(args.node_id)}");
91
+ if (result.trackType !== "audio") return __error("node_id is not an audio clip");
92
+
93
+ var effectName = "${escapeForExtendScript(args.effect_name)}";
94
+ var qeTrack = qeSeq.getAudioTrackAt(result.trackIndex);
95
+ if (!qeTrack) return __error("QE audio track not found");
96
+
97
+ var qeClip = qeTrack.getItemAt(result.clipIndex);
98
+ if (!qeClip) return __error("QE clip not found");
99
+
100
+ var effects = qe.project.getAudioEffectList();
101
+ var found = false;
102
+ for (var i = 0; i < effects.numItems; i++) {
103
+ if (effects[i].name === effectName) {
104
+ qeClip.addAudioEffect(effects[i]);
105
+ found = true;
106
+ break;
107
+ }
108
+ }
109
+
110
+ if (!found) return __error("Audio effect not found: " + effectName);
111
+ return __result({ applied: true, effect: effectName });
112
112
  `);
113
113
  return sendCommand(script, bridgeOptions);
114
114
  },
@@ -134,29 +134,29 @@ Example: apply_effect({ node_id: "abc123", effect_name: "Lumetri Color" })`,
134
134
  required: ["node_id"],
135
135
  },
136
136
  handler: async (args) => {
137
- const script = buildToolScript(`
138
- var result = __findClip("${escapeForExtendScript(args.node_id)}");
139
- if (!result) return __error("Clip not found");
140
-
141
- var clip = result.clip;
142
- ${args.effect_index !== undefined ? `
143
- if (${args.effect_index} >= clip.components.numItems) return __error("Effect index out of range");
144
- var effectName = clip.components[${args.effect_index}].displayName;
145
- clip.components[${args.effect_index}].remove();
146
- return __result({ removed: true, effect: effectName });
147
- ` : `
148
- var effectName = "${escapeForExtendScript(args.effect_name || "")}";
149
- var found = false;
150
- for (var i = clip.components.numItems - 1; i >= 0; i--) {
151
- if (clip.components[i].displayName === effectName) {
152
- clip.components[i].remove();
153
- found = true;
154
- break;
155
- }
156
- }
157
- if (!found) return __error("Effect not found: " + effectName);
158
- return __result({ removed: true, effect: effectName });
159
- `}
137
+ const script = buildToolScript(`
138
+ var result = __findClip("${escapeForExtendScript(args.node_id)}");
139
+ if (!result) return __error("Clip not found");
140
+
141
+ var clip = result.clip;
142
+ ${args.effect_index !== undefined ? `
143
+ if (${args.effect_index} >= clip.components.numItems) return __error("Effect index out of range");
144
+ var effectName = clip.components[${args.effect_index}].displayName;
145
+ clip.components[${args.effect_index}].remove();
146
+ return __result({ removed: true, effect: effectName });
147
+ ` : `
148
+ var effectName = "${escapeForExtendScript(args.effect_name || "")}";
149
+ var found = false;
150
+ for (var i = clip.components.numItems - 1; i >= 0; i--) {
151
+ if (clip.components[i].displayName === effectName) {
152
+ clip.components[i].remove();
153
+ found = true;
154
+ break;
155
+ }
156
+ }
157
+ if (!found) return __error("Effect not found: " + effectName);
158
+ return __result({ removed: true, effect: effectName });
159
+ `}
160
160
  `);
161
161
  return sendCommand(script, bridgeOptions);
162
162
  },
@@ -283,24 +283,24 @@ Example: apply_effect({ node_id: "abc123", effect_name: "Lumetri Color" })`,
283
283
  },
284
284
  },
285
285
  color_correct: {
286
- description: `Apply color correction to a clip using Lumetri Color. Automatically applies the Lumetri Color effect if not already present, then sets the specified parameters.
287
-
288
- Preconditions: Active sequence required. Clip must exist on the timeline.
289
-
290
- All parameters are optional — only the ones you provide will be changed. Values outside their valid range are clamped by Premiere Pro.
291
-
292
- Parameter ranges:
293
- - exposure: -5.0 to +5.0 (EV stops; 0 = no change)
294
- - contrast: -100 to +100 (0 = no change)
295
- - highlights: -100 to +100
296
- - shadows: -100 to +100
297
- - whites: -100 to +100
298
- - blacks: -100 to +100
299
- - temperature: -100 to +100 (negative = cooler/blue, positive = warmer/orange)
300
- - tint: -100 to +100 (negative = green, positive = magenta)
301
- - saturation: 0 to 200 (100 = no change, 0 = grayscale)
302
-
303
- Example: color_correct({ node_id: "abc123", exposure: 0.5, contrast: 10, saturation: 110 })
286
+ description: `Apply color correction to a clip using Lumetri Color. Automatically applies the Lumetri Color effect if not already present, then sets the specified parameters.
287
+
288
+ Preconditions: Active sequence required. Clip must exist on the timeline.
289
+
290
+ All parameters are optional — only the ones you provide will be changed. Values outside their valid range are clamped by Premiere Pro.
291
+
292
+ Parameter ranges:
293
+ - exposure: -5.0 to +5.0 (EV stops; 0 = no change)
294
+ - contrast: -100 to +100 (0 = no change)
295
+ - highlights: -100 to +100
296
+ - shadows: -100 to +100
297
+ - whites: -100 to +100
298
+ - blacks: -100 to +100
299
+ - temperature: -100 to +100 (negative = cooler/blue, positive = warmer/orange)
300
+ - tint: -100 to +100 (negative = green, positive = magenta)
301
+ - saturation: 0 to 200 (100 = no change, 0 = grayscale)
302
+
303
+ Example: color_correct({ node_id: "abc123", exposure: 0.5, contrast: 10, saturation: 110 })
304
304
  Example: color_correct({ node_id: "abc123", temperature: -20, highlights: -30, shadows: 15 })`,
305
305
  parameters: {
306
306
  type: "object",
@@ -323,98 +323,98 @@ Example: color_correct({ node_id: "abc123", temperature: -20, highlights: -30, s
323
323
  },
324
324
  handler: async (args) => {
325
325
  // First apply Lumetri Color effect, then set its properties
326
- const script = buildToolScript(`
327
- app.enableQE();
328
- var qeSeq = qe.project.getActiveSequence();
329
- if (!qeSeq) return __error("No active sequence (QE)");
330
-
331
- var result = __findClip("${escapeForExtendScript(args.node_id)}");
332
- if (!result) return __error("Clip not found");
333
-
334
- // Apply Lumetri Color if not already present
335
- var clip = result.clip;
336
- var lumetri = __findIntrinsic(clip, "lumetri");
337
-
338
- if (!lumetri) {
339
- var qeTrack = qeSeq.getVideoTrackAt(result.trackIndex);
340
- if (!qeTrack) return __error("QE video track not found");
341
- var qeClip = qeTrack.getItemAt(result.clipIndex);
342
- if (!qeClip) return __error("QE clip not found");
343
- var effects = qe.project.getVideoEffectList();
344
- // qe.project.getVideoEffectList() returns LOCALIZED displayNames in
345
- // non-English Premiere installs (e.g. "Color Lumetri" in es-ES).
346
- // Match against a list of candidate names across locales.
347
- var lumetriNames = ["Lumetri Color", "Color Lumetri", "Lumetri-Farbe", "Couleur Lumetri", "Lumetri カラー", "色彩Lumetri"];
348
- var lumetriEffect = null;
349
- for (var i = 0; i < effects.numItems; i++) {
350
- for (var n = 0; n < lumetriNames.length; n++) {
351
- if (effects[i].name === lumetriNames[n]) {
352
- lumetriEffect = effects[i];
353
- break;
354
- }
355
- }
356
- if (lumetriEffect) break;
357
- }
358
- // Fallback: any effect whose name contains "Lumetri"
359
- if (!lumetriEffect) {
360
- for (var j = 0; j < effects.numItems; j++) {
361
- if (effects[j].name && effects[j].name.indexOf("Lumetri") !== -1) {
362
- lumetriEffect = effects[j];
363
- break;
364
- }
365
- }
366
- }
367
- if (!lumetriEffect) return __error("Lumetri Color effect not found in available video effects");
368
- qeClip.addVideoEffect(lumetriEffect);
369
- // Re-find the component after applying
370
- lumetri = __findIntrinsic(clip, "lumetri");
371
- }
372
-
373
- if (!lumetri) return __error("Could not apply or find Lumetri Color effect");
374
-
375
- // Set Lumetri properties. Params are nested in sub-groups; __findProp
376
- // descends one level to locate them. Each setValue is guarded.
377
- var changes = {};
378
- var failed = {};
379
-
380
- function __setLumetri(displayNames, value, key) {
381
- var prop = __findProp(lumetri, displayNames);
382
- if (!prop) { failed[key] = "property not found"; return; }
383
- try {
384
- prop.setValue(value, true);
385
- changes[key] = value;
386
- } catch (e) {
387
- failed[key] = e.toString();
388
- }
389
- }
390
-
391
- ${args.exposure !== undefined ? `__setLumetri(["Exposure", "Exposición", "Belichtung", "Exposition"], ${args.exposure}, "exposure");` : ""}
392
- ${args.contrast !== undefined ? `__setLumetri(["Contrast", "Contraste", "Kontrast", "Contraste"], ${args.contrast}, "contrast");` : ""}
393
- ${args.highlights !== undefined ? `__setLumetri(["Highlights", "Iluminaciones", "Lichter", "Hautes lumières"], ${args.highlights}, "highlights");` : ""}
394
- ${args.shadows !== undefined ? `__setLumetri(["Shadows", "Sombras", "Schatten", "Ombres"], ${args.shadows}, "shadows");` : ""}
395
- ${args.whites !== undefined ? `__setLumetri(["Whites", "Blancos", "Weißwerte", "Blancs"], ${args.whites}, "whites");` : ""}
396
- ${args.blacks !== undefined ? `__setLumetri(["Blacks", "Negros", "Schwarzwerte", "Noirs"], ${args.blacks}, "blacks");` : ""}
397
- ${args.temperature !== undefined ? `__setLumetri(["Temperature", "Temperatura", "Temperatur", "Température"], ${args.temperature}, "temperature");` : ""}
398
- ${args.tint !== undefined ? `__setLumetri(["Tint", "Matiz", "Farbton", "Teinte"], ${args.tint}, "tint");` : ""}
399
- ${args.saturation !== undefined ? `__setLumetri(["Saturation", "Saturación", "Sättigung", "Saturation"], ${args.saturation}, "saturation");` : ""}
400
-
401
- return __result({ colorCorrected: true, clipName: clip.name, changes: changes, failed: failed });
326
+ const script = buildToolScript(`
327
+ app.enableQE();
328
+ var qeSeq = qe.project.getActiveSequence();
329
+ if (!qeSeq) return __error("No active sequence (QE)");
330
+
331
+ var result = __findClip("${escapeForExtendScript(args.node_id)}");
332
+ if (!result) return __error("Clip not found");
333
+
334
+ // Apply Lumetri Color if not already present
335
+ var clip = result.clip;
336
+ var lumetri = __findIntrinsic(clip, "lumetri");
337
+
338
+ if (!lumetri) {
339
+ var qeTrack = qeSeq.getVideoTrackAt(result.trackIndex);
340
+ if (!qeTrack) return __error("QE video track not found");
341
+ var qeClip = qeTrack.getItemAt(result.clipIndex);
342
+ if (!qeClip) return __error("QE clip not found");
343
+ var effects = qe.project.getVideoEffectList();
344
+ // qe.project.getVideoEffectList() returns LOCALIZED displayNames in
345
+ // non-English Premiere installs (e.g. "Color Lumetri" in es-ES).
346
+ // Match against a list of candidate names across locales.
347
+ var lumetriNames = ["Lumetri Color", "Color Lumetri", "Lumetri-Farbe", "Couleur Lumetri", "Lumetri カラー", "色彩Lumetri"];
348
+ var lumetriEffect = null;
349
+ for (var i = 0; i < effects.numItems; i++) {
350
+ for (var n = 0; n < lumetriNames.length; n++) {
351
+ if (effects[i].name === lumetriNames[n]) {
352
+ lumetriEffect = effects[i];
353
+ break;
354
+ }
355
+ }
356
+ if (lumetriEffect) break;
357
+ }
358
+ // Fallback: any effect whose name contains "Lumetri"
359
+ if (!lumetriEffect) {
360
+ for (var j = 0; j < effects.numItems; j++) {
361
+ if (effects[j].name && effects[j].name.indexOf("Lumetri") !== -1) {
362
+ lumetriEffect = effects[j];
363
+ break;
364
+ }
365
+ }
366
+ }
367
+ if (!lumetriEffect) return __error("Lumetri Color effect not found in available video effects");
368
+ qeClip.addVideoEffect(lumetriEffect);
369
+ // Re-find the component after applying
370
+ lumetri = __findIntrinsic(clip, "lumetri");
371
+ }
372
+
373
+ if (!lumetri) return __error("Could not apply or find Lumetri Color effect");
374
+
375
+ // Set Lumetri properties. Params are nested in sub-groups; __findProp
376
+ // descends one level to locate them. Each setValue is guarded.
377
+ var changes = {};
378
+ var failed = {};
379
+
380
+ function __setLumetri(displayNames, value, key) {
381
+ var prop = __findProp(lumetri, displayNames);
382
+ if (!prop) { failed[key] = "property not found"; return; }
383
+ try {
384
+ prop.setValue(value, true);
385
+ changes[key] = value;
386
+ } catch (e) {
387
+ failed[key] = e.toString();
388
+ }
389
+ }
390
+
391
+ ${args.exposure !== undefined ? `__setLumetri(["Exposure", "Exposición", "Belichtung", "Exposition"], ${args.exposure}, "exposure");` : ""}
392
+ ${args.contrast !== undefined ? `__setLumetri(["Contrast", "Contraste", "Kontrast", "Contraste"], ${args.contrast}, "contrast");` : ""}
393
+ ${args.highlights !== undefined ? `__setLumetri(["Highlights", "Iluminaciones", "Lichter", "Hautes lumières"], ${args.highlights}, "highlights");` : ""}
394
+ ${args.shadows !== undefined ? `__setLumetri(["Shadows", "Sombras", "Schatten", "Ombres"], ${args.shadows}, "shadows");` : ""}
395
+ ${args.whites !== undefined ? `__setLumetri(["Whites", "Blancos", "Weißwerte", "Blancs"], ${args.whites}, "whites");` : ""}
396
+ ${args.blacks !== undefined ? `__setLumetri(["Blacks", "Negros", "Schwarzwerte", "Noirs"], ${args.blacks}, "blacks");` : ""}
397
+ ${args.temperature !== undefined ? `__setLumetri(["Temperature", "Temperatura", "Temperatur", "Température"], ${args.temperature}, "temperature");` : ""}
398
+ ${args.tint !== undefined ? `__setLumetri(["Tint", "Matiz", "Farbton", "Teinte"], ${args.tint}, "tint");` : ""}
399
+ ${args.saturation !== undefined ? `__setLumetri(["Saturation", "Saturación", "Sättigung", "Saturation"], ${args.saturation}, "saturation");` : ""}
400
+
401
+ return __result({ colorCorrected: true, clipName: clip.name, changes: changes, failed: failed });
402
402
  `);
403
403
  return sendCommand(script, bridgeOptions);
404
404
  },
405
405
  },
406
406
  apply_lut: {
407
- description: `Apply a LUT (Look-Up Table) file to a clip via Lumetri Color's Creative or Input LUT slot.
408
-
409
- Preconditions: Active sequence required. The LUT file must be accessible on disk at the specified path. Lumetri Color effect is automatically applied to the clip if not already present.
410
-
411
- Supported LUT formats: .cube, .look, .lut, .3dl, .mga.
412
-
413
- Parameters:
414
- - node_id: Clip to apply the LUT to.
415
- - lut_path: Absolute path to the LUT file (e.g., "C:\\LUTs\\rec709.cube" or "/Users/you/LUTs/film.cube").
416
- - lut_strength: Intensity of the LUT effect (0.0 = no effect, 1.0 = full strength, default: 1.0).
417
-
407
+ description: `Apply a LUT (Look-Up Table) file to a clip via Lumetri Color's Creative or Input LUT slot.
408
+
409
+ Preconditions: Active sequence required. The LUT file must be accessible on disk at the specified path. Lumetri Color effect is automatically applied to the clip if not already present.
410
+
411
+ Supported LUT formats: .cube, .look, .lut, .3dl, .mga.
412
+
413
+ Parameters:
414
+ - node_id: Clip to apply the LUT to.
415
+ - lut_path: Absolute path to the LUT file (e.g., "C:\\LUTs\\rec709.cube" or "/Users/you/LUTs/film.cube").
416
+ - lut_strength: Intensity of the LUT effect (0.0 = no effect, 1.0 = full strength, default: 1.0).
417
+
418
418
  Example: apply_lut({ node_id: "abc123", lut_path: "/Users/you/LUTs/orange-teal.cube", lut_strength: 0.8 })`,
419
419
  parameters: {
420
420
  type: "object",
@@ -431,39 +431,39 @@ Example: apply_lut({ node_id: "abc123", lut_path: "/Users/you/LUTs/orange-teal.c
431
431
  required: ["node_id", "lut_path"],
432
432
  },
433
433
  handler: async (args) => {
434
- const script = buildToolScript(`
435
- app.enableQE();
436
- var result = __findClip("${escapeForExtendScript(args.node_id)}");
437
- if (!result) return __error("Clip not found");
438
-
439
- var clip = result.clip;
440
-
441
- // Find or apply Lumetri Color
442
- var lumetriComp = __findIntrinsic(clip, "lumetri");
443
-
444
- if (!lumetriComp) {
445
- var qeSeq = qe.project.getActiveSequence();
446
- var qeTrack = qeSeq.getVideoTrackAt(result.trackIndex);
447
- var qeClip = qeTrack.getItemAt(result.clipIndex);
448
- var effects = qe.project.getVideoEffectList();
449
- for (var i = 0; i < effects.numItems; i++) {
450
- if (effects[i].name === "Lumetri Color") {
451
- qeClip.addVideoEffect(effects[i]);
452
- break;
453
- }
454
- }
455
- // Re-find the component
456
- lumetriComp = __findIntrinsic(clip, "lumetri");
457
- }
458
-
459
- if (!lumetriComp) return __error("Could not apply Lumetri Color effect");
460
-
461
- // Set the LUT path (Input LUT is nested within Lumetri groups)
462
- var lutProp = __findProp(lumetriComp, ["Input LUT", "Creative Look", "LUT de entrada"]);
463
- if (!lutProp) return __error("Input LUT property not found on Lumetri Color");
464
- lutProp.setValue("${escapeForExtendScript(args.lut_path)}", true);
465
-
466
- return __result({ lutApplied: true, clipName: clip.name, lutPath: "${escapeForExtendScript(args.lut_path)}" });
434
+ const script = buildToolScript(`
435
+ app.enableQE();
436
+ var result = __findClip("${escapeForExtendScript(args.node_id)}");
437
+ if (!result) return __error("Clip not found");
438
+
439
+ var clip = result.clip;
440
+
441
+ // Find or apply Lumetri Color
442
+ var lumetriComp = __findIntrinsic(clip, "lumetri");
443
+
444
+ if (!lumetriComp) {
445
+ var qeSeq = qe.project.getActiveSequence();
446
+ var qeTrack = qeSeq.getVideoTrackAt(result.trackIndex);
447
+ var qeClip = qeTrack.getItemAt(result.clipIndex);
448
+ var effects = qe.project.getVideoEffectList();
449
+ for (var i = 0; i < effects.numItems; i++) {
450
+ if (effects[i].name === "Lumetri Color") {
451
+ qeClip.addVideoEffect(effects[i]);
452
+ break;
453
+ }
454
+ }
455
+ // Re-find the component
456
+ lumetriComp = __findIntrinsic(clip, "lumetri");
457
+ }
458
+
459
+ if (!lumetriComp) return __error("Could not apply Lumetri Color effect");
460
+
461
+ // Set the LUT path (Input LUT is nested within Lumetri groups)
462
+ var lutProp = __findProp(lumetriComp, ["Input LUT", "Creative Look", "LUT de entrada"]);
463
+ if (!lutProp) return __error("Input LUT property not found on Lumetri Color");
464
+ lutProp.setValue("${escapeForExtendScript(args.lut_path)}", true);
465
+
466
+ return __result({ lutApplied: true, clipName: clip.name, lutPath: "${escapeForExtendScript(args.lut_path)}" });
467
467
  `);
468
468
  return sendCommand(script, bridgeOptions);
469
469
  },
@@ -490,61 +490,61 @@ Example: apply_lut({ node_id: "abc123", lut_path: "/Users/you/LUTs/orange-teal.c
490
490
  required: ["node_id"],
491
491
  },
492
492
  handler: async (args) => {
493
- const script = buildToolScript(`
494
- app.enableQE();
495
- var qeSeq = qe.project.getActiveSequence();
496
- if (!qeSeq) return __error("No active sequence (QE)");
497
-
498
- var result = __findClip("${escapeForExtendScript(args.node_id)}");
499
- if (!result) return __error("Clip not found: ${escapeForExtendScript(args.node_id)}");
500
-
501
- var qeTrack = result.trackType === "video"
502
- ? qeSeq.getVideoTrackAt(result.trackIndex)
503
- : null;
504
- if (!qeTrack) return __error("Warp Stabilizer can only be applied to video clips");
505
-
506
- var qeClip = qeTrack.getItemAt(result.clipIndex);
507
- if (!qeClip) return __error("QE clip not found");
508
-
509
- // Find and apply Warp Stabilizer
510
- var effects = qe.project.getVideoEffectList();
511
- var found = false;
512
- for (var i = 0; i < effects.numItems; i++) {
513
- if (effects[i].name === "Warp Stabilizer") {
514
- qeClip.addVideoEffect(effects[i]);
515
- found = true;
516
- break;
517
- }
518
- }
519
-
520
- if (!found) return __error("Warp Stabilizer effect not found");
521
-
522
- // Set properties if specified
523
- var clip = result.clip;
524
- var changes = { stabilized: true };
525
- ${args.smoothness !== undefined || args.method !== undefined ? `
526
- for (var i = 0; i < clip.components.numItems; i++) {
527
- var comp = clip.components[i];
528
- if (comp.displayName === "Warp Stabilizer") {
529
- for (var p = 0; p < comp.properties.numItems; p++) {
530
- var prop = comp.properties[p];
531
- ${args.smoothness !== undefined ? `
532
- if (prop.displayName === "Smoothness") {
533
- prop.setValue(${args.smoothness}, true);
534
- changes.smoothness = ${args.smoothness};
535
- }` : ""}
536
- ${args.method !== undefined ? `
537
- if (prop.displayName === "Method") {
538
- prop.setValue("${escapeForExtendScript(args.method)}", true);
539
- changes.method = "${escapeForExtendScript(args.method)}";
540
- }` : ""}
541
- }
542
- break;
543
- }
544
- }
545
- ` : ""}
546
-
547
- return __result({ clipName: clip.name, info: "Warp Stabilizer applied. Analysis will begin automatically.", changes: changes });
493
+ const script = buildToolScript(`
494
+ app.enableQE();
495
+ var qeSeq = qe.project.getActiveSequence();
496
+ if (!qeSeq) return __error("No active sequence (QE)");
497
+
498
+ var result = __findClip("${escapeForExtendScript(args.node_id)}");
499
+ if (!result) return __error("Clip not found: ${escapeForExtendScript(args.node_id)}");
500
+
501
+ var qeTrack = result.trackType === "video"
502
+ ? qeSeq.getVideoTrackAt(result.trackIndex)
503
+ : null;
504
+ if (!qeTrack) return __error("Warp Stabilizer can only be applied to video clips");
505
+
506
+ var qeClip = qeTrack.getItemAt(result.clipIndex);
507
+ if (!qeClip) return __error("QE clip not found");
508
+
509
+ // Find and apply Warp Stabilizer
510
+ var effects = qe.project.getVideoEffectList();
511
+ var found = false;
512
+ for (var i = 0; i < effects.numItems; i++) {
513
+ if (effects[i].name === "Warp Stabilizer") {
514
+ qeClip.addVideoEffect(effects[i]);
515
+ found = true;
516
+ break;
517
+ }
518
+ }
519
+
520
+ if (!found) return __error("Warp Stabilizer effect not found");
521
+
522
+ // Set properties if specified
523
+ var clip = result.clip;
524
+ var changes = { stabilized: true };
525
+ ${args.smoothness !== undefined || args.method !== undefined ? `
526
+ for (var i = 0; i < clip.components.numItems; i++) {
527
+ var comp = clip.components[i];
528
+ if (comp.displayName === "Warp Stabilizer") {
529
+ for (var p = 0; p < comp.properties.numItems; p++) {
530
+ var prop = comp.properties[p];
531
+ ${args.smoothness !== undefined ? `
532
+ if (prop.displayName === "Smoothness") {
533
+ prop.setValue(${args.smoothness}, true);
534
+ changes.smoothness = ${args.smoothness};
535
+ }` : ""}
536
+ ${args.method !== undefined ? `
537
+ if (prop.displayName === "Method") {
538
+ prop.setValue("${escapeForExtendScript(args.method)}", true);
539
+ changes.method = "${escapeForExtendScript(args.method)}";
540
+ }` : ""}
541
+ }
542
+ break;
543
+ }
544
+ }
545
+ ` : ""}
546
+
547
+ return __result({ clipName: clip.name, info: "Warp Stabilizer applied. Analysis will begin automatically.", changes: changes });
548
548
  `);
549
549
  return sendCommand(script, bridgeOptions);
550
550
  },