@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
@@ -10,43 +10,43 @@ export function getAudioAdvancedTools(bridgeOptions) {
10
10
  required: [],
11
11
  },
12
12
  handler: async (_args) => {
13
- const script = buildToolScript(`
14
- var seq = app.project.activeSequence;
15
- if (!seq) return __error("No active sequence");
16
- var tracks = [];
17
- for (var t = 0; t < seq.audioTracks.numTracks; t++) {
18
- var track = seq.audioTracks[t];
19
- var trackInfo = {
20
- index: t,
21
- name: track.name || ("Audio " + (t + 1)),
22
- isMuted: __readMaybe(track, "isMuted", false),
23
- numClips: track.clips ? track.clips.numItems : 0,
24
- level: 0,
25
- panning: 0
26
- };
27
- var volComp = __findComponent(track, ["AE.ADBE Audio Levels", "audioVolume", "audioGain"], ["Volume", "Volumen"]);
28
- if (volComp) {
29
- for (var pi = 0; pi < volComp.properties.numItems; pi++) {
30
- var prop = volComp.properties[pi];
31
- if (prop.displayName === "Level") {
32
- try { trackInfo.level = prop.getValue(); } catch(e) {}
33
- break;
34
- }
35
- }
36
- }
37
- var panComp = __findComponent(track, ["AE.ADBE Pan", "audioPanner"], ["Panner", "Balance"]);
38
- if (panComp) {
39
- for (var pi2 = 0; pi2 < panComp.properties.numItems; pi2++) {
40
- var prop2 = panComp.properties[pi2];
41
- if (prop2.displayName === "Balance" || prop2.displayName === "Panner" || prop2.displayName === "Left/Right Balance") {
42
- try { trackInfo.panning = prop2.getValue(); } catch(e2) {}
43
- break;
44
- }
45
- }
46
- }
47
- tracks.push(trackInfo);
48
- }
49
- return __result({ tracks: tracks, numTracks: tracks.length });
13
+ const script = buildToolScript(`
14
+ var seq = app.project.activeSequence;
15
+ if (!seq) return __error("No active sequence");
16
+ var tracks = [];
17
+ for (var t = 0; t < seq.audioTracks.numTracks; t++) {
18
+ var track = seq.audioTracks[t];
19
+ var trackInfo = {
20
+ index: t,
21
+ name: track.name || ("Audio " + (t + 1)),
22
+ isMuted: __readMaybe(track, "isMuted", false),
23
+ numClips: track.clips ? track.clips.numItems : 0,
24
+ level: 0,
25
+ panning: 0
26
+ };
27
+ var volComp = __findComponent(track, ["AE.ADBE Audio Levels", "audioVolume", "audioGain"], ["Volume", "Volumen"]);
28
+ if (volComp) {
29
+ for (var pi = 0; pi < volComp.properties.numItems; pi++) {
30
+ var prop = volComp.properties[pi];
31
+ if (prop.displayName === "Level") {
32
+ try { trackInfo.level = prop.getValue(); } catch(e) {}
33
+ break;
34
+ }
35
+ }
36
+ }
37
+ var panComp = __findComponent(track, ["AE.ADBE Pan", "audioPanner"], ["Panner", "Balance"]);
38
+ if (panComp) {
39
+ for (var pi2 = 0; pi2 < panComp.properties.numItems; pi2++) {
40
+ var prop2 = panComp.properties[pi2];
41
+ if (prop2.displayName === "Balance" || prop2.displayName === "Panner" || prop2.displayName === "Left/Right Balance") {
42
+ try { trackInfo.panning = prop2.getValue(); } catch(e2) {}
43
+ break;
44
+ }
45
+ }
46
+ }
47
+ tracks.push(trackInfo);
48
+ }
49
+ return __result({ tracks: tracks, numTracks: tracks.length });
50
50
  `);
51
51
  return sendCommand(script, bridgeOptions);
52
52
  },
@@ -63,33 +63,33 @@ export function getAudioAdvancedTools(bridgeOptions) {
63
63
  required: ["track_index", "clip_index", "level_db"],
64
64
  },
65
65
  handler: async (args) => {
66
- const script = buildToolScript(`
67
- var seq = app.project.activeSequence;
68
- if (!seq) return __error("No active sequence");
69
- var trackIdx = ${args.track_index};
70
- var clipIdx = ${args.clip_index};
71
- var levelDb = ${args.level_db};
72
- if (trackIdx >= seq.audioTracks.numTracks) return __error("Track index " + trackIdx + " out of range");
73
- var track = seq.audioTracks[trackIdx];
74
- if (clipIdx >= track.clips.numItems) return __error("Clip index " + clipIdx + " out of range");
75
- var clip = track.clips[clipIdx];
76
- var found = false;
77
- for (var i = 0; i < clip.components.numItems; i++) {
78
- var comp = clip.components[i];
79
- if (comp.matchName === "audioVolume" || comp.displayName === "Volume") {
80
- for (var p = 0; p < comp.properties.numItems; p++) {
81
- var prop = comp.properties[p];
82
- if (prop.displayName === "Level") {
83
- prop.setValue(levelDb, true);
84
- found = true;
85
- break;
86
- }
87
- }
88
- }
89
- if (found) break;
90
- }
91
- if (!found) return __error("Could not find Volume/Level component on clip");
92
- return __result({ trackIndex: trackIdx, clipIndex: clipIdx, levelDb: levelDb, clip: clip.name });
66
+ const script = buildToolScript(`
67
+ var seq = app.project.activeSequence;
68
+ if (!seq) return __error("No active sequence");
69
+ var trackIdx = ${args.track_index};
70
+ var clipIdx = ${args.clip_index};
71
+ var levelDb = ${args.level_db};
72
+ if (trackIdx >= seq.audioTracks.numTracks) return __error("Track index " + trackIdx + " out of range");
73
+ var track = seq.audioTracks[trackIdx];
74
+ if (clipIdx >= track.clips.numItems) return __error("Clip index " + clipIdx + " out of range");
75
+ var clip = track.clips[clipIdx];
76
+ var found = false;
77
+ for (var i = 0; i < clip.components.numItems; i++) {
78
+ var comp = clip.components[i];
79
+ if (comp.matchName === "audioVolume" || comp.displayName === "Volume") {
80
+ for (var p = 0; p < comp.properties.numItems; p++) {
81
+ var prop = comp.properties[p];
82
+ if (prop.displayName === "Level") {
83
+ prop.setValue(levelDb, true);
84
+ found = true;
85
+ break;
86
+ }
87
+ }
88
+ }
89
+ if (found) break;
90
+ }
91
+ if (!found) return __error("Could not find Volume/Level component on clip");
92
+ return __result({ trackIndex: trackIdx, clipIndex: clipIdx, levelDb: levelDb, clip: clip.name });
93
93
  `);
94
94
  return sendCommand(script, bridgeOptions);
95
95
  },
@@ -105,32 +105,32 @@ export function getAudioAdvancedTools(bridgeOptions) {
105
105
  required: ["track_index", "pan_value"],
106
106
  },
107
107
  handler: async (args) => {
108
- const script = buildToolScript(`
109
- var seq = app.project.activeSequence;
110
- if (!seq) return __error("No active sequence");
111
- var trackIdx = ${args.track_index};
112
- var panValue = ${args.pan_value};
113
- if (trackIdx >= seq.audioTracks.numTracks) return __error("Track index " + trackIdx + " out of range");
114
- var track = seq.audioTracks[trackIdx];
115
- var found = false;
116
- if (track.components) {
117
- for (var ci = 0; ci < track.components.numItems; ci++) {
118
- var comp = track.components[ci];
119
- if (comp.matchName === "audioPanner" || comp.displayName === "Panner") {
120
- for (var pi = 0; pi < comp.properties.numItems; pi++) {
121
- var prop = comp.properties[pi];
122
- if (prop.displayName === "Balance" || prop.displayName === "Panner" || prop.displayName === "Left/Right Balance") {
123
- prop.setValue(panValue, true);
124
- found = true;
125
- break;
126
- }
127
- }
128
- }
129
- if (found) break;
130
- }
131
- }
132
- if (!found) return __result({ note: "Panner component not found on track " + trackIdx + ". Track-level panning may not be available for this track type.", trackIndex: trackIdx, requestedPan: panValue });
133
- return __result({ trackIndex: trackIdx, panValue: panValue });
108
+ const script = buildToolScript(`
109
+ var seq = app.project.activeSequence;
110
+ if (!seq) return __error("No active sequence");
111
+ var trackIdx = ${args.track_index};
112
+ var panValue = ${args.pan_value};
113
+ if (trackIdx >= seq.audioTracks.numTracks) return __error("Track index " + trackIdx + " out of range");
114
+ var track = seq.audioTracks[trackIdx];
115
+ var found = false;
116
+ if (track.components) {
117
+ for (var ci = 0; ci < track.components.numItems; ci++) {
118
+ var comp = track.components[ci];
119
+ if (comp.matchName === "audioPanner" || comp.displayName === "Panner") {
120
+ for (var pi = 0; pi < comp.properties.numItems; pi++) {
121
+ var prop = comp.properties[pi];
122
+ if (prop.displayName === "Balance" || prop.displayName === "Panner" || prop.displayName === "Left/Right Balance") {
123
+ prop.setValue(panValue, true);
124
+ found = true;
125
+ break;
126
+ }
127
+ }
128
+ }
129
+ if (found) break;
130
+ }
131
+ }
132
+ if (!found) return __result({ note: "Panner component not found on track " + trackIdx + ". Track-level panning may not be available for this track type.", trackIndex: trackIdx, requestedPan: panValue });
133
+ return __result({ trackIndex: trackIdx, panValue: panValue });
134
134
  `);
135
135
  return sendCommand(script, bridgeOptions);
136
136
  },
@@ -147,33 +147,33 @@ export function getAudioAdvancedTools(bridgeOptions) {
147
147
  required: ["track_index", "clip_index", "pan_value"],
148
148
  },
149
149
  handler: async (args) => {
150
- const script = buildToolScript(`
151
- var seq = app.project.activeSequence;
152
- if (!seq) return __error("No active sequence");
153
- var trackIdx = ${args.track_index};
154
- var clipIdx = ${args.clip_index};
155
- var panValue = ${args.pan_value};
156
- if (trackIdx >= seq.audioTracks.numTracks) return __error("Track index " + trackIdx + " out of range");
157
- var track = seq.audioTracks[trackIdx];
158
- if (clipIdx >= track.clips.numItems) return __error("Clip index " + clipIdx + " out of range");
159
- var clip = track.clips[clipIdx];
160
- var found = false;
161
- for (var i = 0; i < clip.components.numItems; i++) {
162
- var comp = clip.components[i];
163
- if (comp.matchName === "audioPanner" || comp.displayName === "Panner") {
164
- for (var p = 0; p < comp.properties.numItems; p++) {
165
- var prop = comp.properties[p];
166
- if (prop.displayName === "Balance" || prop.displayName === "Panner" || prop.displayName === "Left/Right Balance") {
167
- prop.setValue(panValue, true);
168
- found = true;
169
- break;
170
- }
171
- }
172
- }
173
- if (found) break;
174
- }
175
- if (!found) return __result({ note: "Panner component not found on clip", trackIndex: trackIdx, clipIndex: clipIdx, requestedPan: panValue });
176
- return __result({ trackIndex: trackIdx, clipIndex: clipIdx, panValue: panValue, clip: clip.name });
150
+ const script = buildToolScript(`
151
+ var seq = app.project.activeSequence;
152
+ if (!seq) return __error("No active sequence");
153
+ var trackIdx = ${args.track_index};
154
+ var clipIdx = ${args.clip_index};
155
+ var panValue = ${args.pan_value};
156
+ if (trackIdx >= seq.audioTracks.numTracks) return __error("Track index " + trackIdx + " out of range");
157
+ var track = seq.audioTracks[trackIdx];
158
+ if (clipIdx >= track.clips.numItems) return __error("Clip index " + clipIdx + " out of range");
159
+ var clip = track.clips[clipIdx];
160
+ var found = false;
161
+ for (var i = 0; i < clip.components.numItems; i++) {
162
+ var comp = clip.components[i];
163
+ if (comp.matchName === "audioPanner" || comp.displayName === "Panner") {
164
+ for (var p = 0; p < comp.properties.numItems; p++) {
165
+ var prop = comp.properties[p];
166
+ if (prop.displayName === "Balance" || prop.displayName === "Panner" || prop.displayName === "Left/Right Balance") {
167
+ prop.setValue(panValue, true);
168
+ found = true;
169
+ break;
170
+ }
171
+ }
172
+ }
173
+ if (found) break;
174
+ }
175
+ if (!found) return __result({ note: "Panner component not found on clip", trackIndex: trackIdx, clipIndex: clipIdx, requestedPan: panValue });
176
+ return __result({ trackIndex: trackIdx, clipIndex: clipIdx, panValue: panValue, clip: clip.name });
177
177
  `);
178
178
  return sendCommand(script, bridgeOptions);
179
179
  },
@@ -189,31 +189,31 @@ export function getAudioAdvancedTools(bridgeOptions) {
189
189
  required: ["track_index", "clip_index"],
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 trackIdx = ${args.track_index};
196
- var clipIdx = ${args.clip_index};
197
- if (trackIdx >= seq.audioTracks.numTracks) return __error("Track index " + trackIdx + " out of range");
198
- var track = seq.audioTracks[trackIdx];
199
- if (clipIdx >= track.clips.numItems) return __error("Clip index " + clipIdx + " out of range");
200
- var clip = track.clips[clipIdx];
201
- var panValue = 0;
202
- var found = false;
203
- for (var i = 0; i < clip.components.numItems; i++) {
204
- var comp = clip.components[i];
205
- if (comp.matchName === "audioPanner" || comp.displayName === "Panner") {
206
- for (var p = 0; p < comp.properties.numItems; p++) {
207
- var prop = comp.properties[p];
208
- if (prop.displayName === "Balance" || prop.displayName === "Panner" || prop.displayName === "Left/Right Balance") {
209
- try { panValue = prop.getValue(); found = true; } catch(e) {}
210
- break;
211
- }
212
- }
213
- }
214
- if (found) break;
215
- }
216
- return __result({ trackIndex: trackIdx, clipIndex: clipIdx, panValue: panValue, clip: clip.name, found: found });
192
+ const script = buildToolScript(`
193
+ var seq = app.project.activeSequence;
194
+ if (!seq) return __error("No active sequence");
195
+ var trackIdx = ${args.track_index};
196
+ var clipIdx = ${args.clip_index};
197
+ if (trackIdx >= seq.audioTracks.numTracks) return __error("Track index " + trackIdx + " out of range");
198
+ var track = seq.audioTracks[trackIdx];
199
+ if (clipIdx >= track.clips.numItems) return __error("Clip index " + clipIdx + " out of range");
200
+ var clip = track.clips[clipIdx];
201
+ var panValue = 0;
202
+ var found = false;
203
+ for (var i = 0; i < clip.components.numItems; i++) {
204
+ var comp = clip.components[i];
205
+ if (comp.matchName === "audioPanner" || comp.displayName === "Panner") {
206
+ for (var p = 0; p < comp.properties.numItems; p++) {
207
+ var prop = comp.properties[p];
208
+ if (prop.displayName === "Balance" || prop.displayName === "Panner" || prop.displayName === "Left/Right Balance") {
209
+ try { panValue = prop.getValue(); found = true; } catch(e) {}
210
+ break;
211
+ }
212
+ }
213
+ }
214
+ if (found) break;
215
+ }
216
+ return __result({ trackIndex: trackIdx, clipIndex: clipIdx, panValue: panValue, clip: clip.name, found: found });
217
217
  `);
218
218
  return sendCommand(script, bridgeOptions);
219
219
  },
@@ -231,38 +231,38 @@ export function getAudioAdvancedTools(bridgeOptions) {
231
231
  required: ["track_index", "clip_index", "time", "level_db"],
232
232
  },
233
233
  handler: async (args) => {
234
- const script = buildToolScript(`
235
- var seq = app.project.activeSequence;
236
- if (!seq) return __error("No active sequence");
237
- var trackIdx = ${args.track_index};
238
- var clipIdx = ${args.clip_index};
239
- var timeSecs = ${args.time};
240
- var levelDb = ${args.level_db};
241
- if (trackIdx >= seq.audioTracks.numTracks) return __error("Track index " + trackIdx + " out of range");
242
- var track = seq.audioTracks[trackIdx];
243
- if (clipIdx >= track.clips.numItems) return __error("Clip index " + clipIdx + " out of range");
244
- var clip = track.clips[clipIdx];
245
- var levelProp = null;
246
- for (var i = 0; i < clip.components.numItems; i++) {
247
- var comp = clip.components[i];
248
- if (comp.matchName === "audioVolume" || comp.displayName === "Volume") {
249
- for (var p = 0; p < comp.properties.numItems; p++) {
250
- if (comp.properties[p].displayName === "Level") {
251
- levelProp = comp.properties[p];
252
- break;
253
- }
254
- }
255
- }
256
- if (levelProp) break;
257
- }
258
- if (!levelProp) return __error("Could not find Volume Level property on clip");
259
- levelProp.setTimeVarying(true);
260
- var timeTicks = __secondsToTicks(timeSecs);
261
- var t = new Time();
262
- t.ticks = String(timeTicks);
263
- levelProp.addKey(t);
264
- levelProp.setValueAtKey(t, levelDb);
265
- return __result({ trackIndex: trackIdx, clipIndex: clipIdx, time: timeSecs, levelDb: levelDb, clip: clip.name });
234
+ const script = buildToolScript(`
235
+ var seq = app.project.activeSequence;
236
+ if (!seq) return __error("No active sequence");
237
+ var trackIdx = ${args.track_index};
238
+ var clipIdx = ${args.clip_index};
239
+ var timeSecs = ${args.time};
240
+ var levelDb = ${args.level_db};
241
+ if (trackIdx >= seq.audioTracks.numTracks) return __error("Track index " + trackIdx + " out of range");
242
+ var track = seq.audioTracks[trackIdx];
243
+ if (clipIdx >= track.clips.numItems) return __error("Clip index " + clipIdx + " out of range");
244
+ var clip = track.clips[clipIdx];
245
+ var levelProp = null;
246
+ for (var i = 0; i < clip.components.numItems; i++) {
247
+ var comp = clip.components[i];
248
+ if (comp.matchName === "audioVolume" || comp.displayName === "Volume") {
249
+ for (var p = 0; p < comp.properties.numItems; p++) {
250
+ if (comp.properties[p].displayName === "Level") {
251
+ levelProp = comp.properties[p];
252
+ break;
253
+ }
254
+ }
255
+ }
256
+ if (levelProp) break;
257
+ }
258
+ if (!levelProp) return __error("Could not find Volume Level property on clip");
259
+ levelProp.setTimeVarying(true);
260
+ var timeTicks = __secondsToTicks(timeSecs);
261
+ var t = new Time();
262
+ t.ticks = String(timeTicks);
263
+ levelProp.addKey(t);
264
+ levelProp.setValueAtKey(t, levelDb);
265
+ return __result({ trackIndex: trackIdx, clipIndex: clipIdx, time: timeSecs, levelDb: levelDb, clip: clip.name });
266
266
  `);
267
267
  return sendCommand(script, bridgeOptions);
268
268
  },
@@ -280,28 +280,28 @@ export function getAudioAdvancedTools(bridgeOptions) {
280
280
  required: ["track_index", "clip_index", "time", "pan_value"],
281
281
  },
282
282
  handler: async (args) => {
283
- const script = buildToolScript(`
284
- var seq = app.project.activeSequence;
285
- if (!seq) return __error("No active sequence");
286
- var trackIdx = ${args.track_index};
287
- var clipIdx = ${args.clip_index};
288
- var timeSecs = ${args.time};
289
- var panValue = ${args.pan_value};
290
- if (trackIdx >= seq.audioTracks.numTracks) return __error("Track index " + trackIdx + " out of range");
291
- var track = seq.audioTracks[trackIdx];
292
- if (clipIdx >= track.clips.numItems) return __error("Clip index " + clipIdx + " out of range");
293
- var clip = track.clips[clipIdx];
294
- var panComp = __findIntrinsic(clip, "panner");
295
- if (!panComp) return __error("Panner component not found on clip");
296
- var panProp = __findProp(panComp, ["Balance", "Panner", "Left/Right Balance", "Panoramización"]);
297
- if (!panProp) return __error("Panner property not found on clip");
298
- panProp.setTimeVarying(true);
299
- var timeTicks = __secondsToTicks(timeSecs);
300
- var t = new Time();
301
- t.ticks = String(timeTicks);
302
- panProp.addKey(t);
303
- panProp.setValueAtKey(t, panValue);
304
- return __result({ trackIndex: trackIdx, clipIndex: clipIdx, time: timeSecs, panValue: panValue, clip: clip.name });
283
+ const script = buildToolScript(`
284
+ var seq = app.project.activeSequence;
285
+ if (!seq) return __error("No active sequence");
286
+ var trackIdx = ${args.track_index};
287
+ var clipIdx = ${args.clip_index};
288
+ var timeSecs = ${args.time};
289
+ var panValue = ${args.pan_value};
290
+ if (trackIdx >= seq.audioTracks.numTracks) return __error("Track index " + trackIdx + " out of range");
291
+ var track = seq.audioTracks[trackIdx];
292
+ if (clipIdx >= track.clips.numItems) return __error("Clip index " + clipIdx + " out of range");
293
+ var clip = track.clips[clipIdx];
294
+ var panComp = __findIntrinsic(clip, "panner");
295
+ if (!panComp) return __error("Panner component not found on clip");
296
+ var panProp = __findProp(panComp, ["Balance", "Panner", "Left/Right Balance", "Panoramización"]);
297
+ if (!panProp) return __error("Panner property not found on clip");
298
+ panProp.setTimeVarying(true);
299
+ var timeTicks = __secondsToTicks(timeSecs);
300
+ var t = new Time();
301
+ t.ticks = String(timeTicks);
302
+ panProp.addKey(t);
303
+ panProp.setValueAtKey(t, panValue);
304
+ return __result({ trackIndex: trackIdx, clipIndex: clipIdx, time: timeSecs, panValue: panValue, clip: clip.name });
305
305
  `);
306
306
  return sendCommand(script, bridgeOptions);
307
307
  },
@@ -317,39 +317,39 @@ export function getAudioAdvancedTools(bridgeOptions) {
317
317
  required: ["track_index", "clip_index"],
318
318
  },
319
319
  handler: async (args) => {
320
- const script = buildToolScript(`
321
- var seq = app.project.activeSequence;
322
- if (!seq) return __error("No active sequence");
323
- var trackIdx = ${args.track_index};
324
- var clipIdx = ${args.clip_index};
325
- if (trackIdx >= seq.audioTracks.numTracks) return __error("Track index " + trackIdx + " out of range");
326
- var track = seq.audioTracks[trackIdx];
327
- if (clipIdx >= track.clips.numItems) return __error("Clip index " + clipIdx + " out of range");
328
- var clip = track.clips[clipIdx];
329
- var volComp = __findIntrinsic(clip, "volume");
330
- if (!volComp) return __error("Volume component not found on clip");
331
- var levelProp = __findProp(volComp, ["Level", "Nivel", "Pegel", "Volume"]);
332
- if (!levelProp) return __error("Volume Level property not found on clip");
333
- var keyframes = [];
334
- try {
335
- var keys = levelProp.getKeys ? levelProp.getKeys() : null;
336
- if (keys) {
337
- var numKeys = (keys.numKeys != null) ? keys.numKeys : (keys.length != null ? keys.length : 0);
338
- for (var k = 0; k < numKeys; k++) {
339
- var keyTime = keys[k];
340
- if (keyTime == null && levelProp.getKey) {
341
- try { keyTime = levelProp.getKey(k); } catch(eGetKey) {}
342
- }
343
- if (keyTime == null) continue;
344
- var keyVal = 0;
345
- try { keyVal = levelProp.getValueAtKey(keyTime); } catch(e) {}
346
- keyframes.push({ time: __ticksToSeconds(keyTime.ticks), levelDb: keyVal });
347
- }
348
- }
349
- } catch(eKeys) {
350
- keyframes = [];
351
- }
352
- return __result({ trackIndex: trackIdx, clipIndex: clipIdx, clip: clip.name, keyframes: keyframes, timeVarying: levelProp.getTimeVarying ? levelProp.getTimeVarying() : false });
320
+ const script = buildToolScript(`
321
+ var seq = app.project.activeSequence;
322
+ if (!seq) return __error("No active sequence");
323
+ var trackIdx = ${args.track_index};
324
+ var clipIdx = ${args.clip_index};
325
+ if (trackIdx >= seq.audioTracks.numTracks) return __error("Track index " + trackIdx + " out of range");
326
+ var track = seq.audioTracks[trackIdx];
327
+ if (clipIdx >= track.clips.numItems) return __error("Clip index " + clipIdx + " out of range");
328
+ var clip = track.clips[clipIdx];
329
+ var volComp = __findIntrinsic(clip, "volume");
330
+ if (!volComp) return __error("Volume component not found on clip");
331
+ var levelProp = __findProp(volComp, ["Level", "Nivel", "Pegel", "Volume"]);
332
+ if (!levelProp) return __error("Volume Level property not found on clip");
333
+ var keyframes = [];
334
+ try {
335
+ var keys = levelProp.getKeys ? levelProp.getKeys() : null;
336
+ if (keys) {
337
+ var numKeys = (keys.numKeys != null) ? keys.numKeys : (keys.length != null ? keys.length : 0);
338
+ for (var k = 0; k < numKeys; k++) {
339
+ var keyTime = keys[k];
340
+ if (keyTime == null && levelProp.getKey) {
341
+ try { keyTime = levelProp.getKey(k); } catch(eGetKey) {}
342
+ }
343
+ if (keyTime == null) continue;
344
+ var keyVal = 0;
345
+ try { keyVal = levelProp.getValueAtKey(keyTime); } catch(e) {}
346
+ keyframes.push({ time: __ticksToSeconds(keyTime.ticks), levelDb: keyVal });
347
+ }
348
+ }
349
+ } catch(eKeys) {
350
+ keyframes = [];
351
+ }
352
+ return __result({ trackIndex: trackIdx, clipIndex: clipIdx, clip: clip.name, keyframes: keyframes, timeVarying: levelProp.getTimeVarying ? levelProp.getTimeVarying() : false });
353
353
  `);
354
354
  return sendCommand(script, bridgeOptions);
355
355
  },
@@ -365,39 +365,39 @@ export function getAudioAdvancedTools(bridgeOptions) {
365
365
  required: ["track_index", "clip_index"],
366
366
  },
367
367
  handler: async (args) => {
368
- const script = buildToolScript(`
369
- var seq = app.project.activeSequence;
370
- if (!seq) return __error("No active sequence");
371
- var trackIdx = ${args.track_index};
372
- var clipIdx = ${args.clip_index};
373
- if (trackIdx >= seq.audioTracks.numTracks) return __error("Track index " + trackIdx + " out of range");
374
- var track = seq.audioTracks[trackIdx];
375
- if (clipIdx >= track.clips.numItems) return __error("Clip index " + clipIdx + " out of range");
376
- var clip = track.clips[clipIdx];
377
- var panComp = __findIntrinsic(clip, "panner");
378
- if (!panComp) return __error("Panner component not found on clip");
379
- var panProp = __findProp(panComp, ["Balance", "Panner", "Left/Right Balance", "Panoramización"]);
380
- if (!panProp) return __error("Panner property not found on clip");
381
- var keyframes = [];
382
- try {
383
- var keys = panProp.getKeys ? panProp.getKeys() : null;
384
- if (keys) {
385
- var numKeys = (keys.numKeys != null) ? keys.numKeys : (keys.length != null ? keys.length : 0);
386
- for (var k = 0; k < numKeys; k++) {
387
- var keyTime = keys[k];
388
- if (keyTime == null && panProp.getKey) {
389
- try { keyTime = panProp.getKey(k); } catch(eGetKey) {}
390
- }
391
- if (keyTime == null) continue;
392
- var keyVal = 0;
393
- try { keyVal = panProp.getValueAtKey(keyTime); } catch(e) {}
394
- keyframes.push({ time: __ticksToSeconds(keyTime.ticks), panValue: keyVal });
395
- }
396
- }
397
- } catch(eKeys) {
398
- keyframes = [];
399
- }
400
- return __result({ trackIndex: trackIdx, clipIndex: clipIdx, clip: clip.name, keyframes: keyframes, timeVarying: panProp.getTimeVarying ? panProp.getTimeVarying() : false });
368
+ const script = buildToolScript(`
369
+ var seq = app.project.activeSequence;
370
+ if (!seq) return __error("No active sequence");
371
+ var trackIdx = ${args.track_index};
372
+ var clipIdx = ${args.clip_index};
373
+ if (trackIdx >= seq.audioTracks.numTracks) return __error("Track index " + trackIdx + " out of range");
374
+ var track = seq.audioTracks[trackIdx];
375
+ if (clipIdx >= track.clips.numItems) return __error("Clip index " + clipIdx + " out of range");
376
+ var clip = track.clips[clipIdx];
377
+ var panComp = __findIntrinsic(clip, "panner");
378
+ if (!panComp) return __error("Panner component not found on clip");
379
+ var panProp = __findProp(panComp, ["Balance", "Panner", "Left/Right Balance", "Panoramización"]);
380
+ if (!panProp) return __error("Panner property not found on clip");
381
+ var keyframes = [];
382
+ try {
383
+ var keys = panProp.getKeys ? panProp.getKeys() : null;
384
+ if (keys) {
385
+ var numKeys = (keys.numKeys != null) ? keys.numKeys : (keys.length != null ? keys.length : 0);
386
+ for (var k = 0; k < numKeys; k++) {
387
+ var keyTime = keys[k];
388
+ if (keyTime == null && panProp.getKey) {
389
+ try { keyTime = panProp.getKey(k); } catch(eGetKey) {}
390
+ }
391
+ if (keyTime == null) continue;
392
+ var keyVal = 0;
393
+ try { keyVal = panProp.getValueAtKey(keyTime); } catch(e) {}
394
+ keyframes.push({ time: __ticksToSeconds(keyTime.ticks), panValue: keyVal });
395
+ }
396
+ }
397
+ } catch(eKeys) {
398
+ keyframes = [];
399
+ }
400
+ return __result({ trackIndex: trackIdx, clipIndex: clipIdx, clip: clip.name, keyframes: keyframes, timeVarying: panProp.getTimeVarying ? panProp.getTimeVarying() : false });
401
401
  `);
402
402
  return sendCommand(script, bridgeOptions);
403
403
  },
@@ -413,37 +413,37 @@ export function getAudioAdvancedTools(bridgeOptions) {
413
413
  required: ["track_index", "clip_index"],
414
414
  },
415
415
  handler: async (args) => {
416
- const script = buildToolScript(`
417
- var seq = app.project.activeSequence;
418
- if (!seq) return __error("No active sequence");
419
- var trackIdx = ${args.track_index};
420
- var clipIdx = ${args.clip_index};
421
- if (trackIdx >= seq.audioTracks.numTracks) return __error("Track index " + trackIdx + " out of range");
422
- var track = seq.audioTracks[trackIdx];
423
- if (clipIdx >= track.clips.numItems) return __error("Clip index " + clipIdx + " out of range");
424
- var clip = track.clips[clipIdx];
425
- var removed = [];
426
- for (var i = 0; i < clip.components.numItems; i++) {
427
- var comp = clip.components[i];
428
- if (comp.matchName === "audioVolume" || comp.displayName === "Volume") {
429
- for (var p = 0; p < comp.properties.numItems; p++) {
430
- if (comp.properties[p].displayName === "Level") {
431
- try { comp.properties[p].setTimeVarying(false); removed.push("Volume Level"); } catch(e) {}
432
- break;
433
- }
434
- }
435
- }
436
- if (comp.matchName === "audioPanner" || comp.displayName === "Panner") {
437
- for (var p2 = 0; p2 < comp.properties.numItems; p2++) {
438
- var prop2 = comp.properties[p2];
439
- if (prop2.displayName === "Balance" || prop2.displayName === "Panner" || prop2.displayName === "Left/Right Balance") {
440
- try { prop2.setTimeVarying(false); removed.push("Panner"); } catch(e2) {}
441
- break;
442
- }
443
- }
444
- }
445
- }
446
- return __result({ trackIndex: trackIdx, clipIndex: clipIdx, clip: clip.name, removedFrom: removed });
416
+ const script = buildToolScript(`
417
+ var seq = app.project.activeSequence;
418
+ if (!seq) return __error("No active sequence");
419
+ var trackIdx = ${args.track_index};
420
+ var clipIdx = ${args.clip_index};
421
+ if (trackIdx >= seq.audioTracks.numTracks) return __error("Track index " + trackIdx + " out of range");
422
+ var track = seq.audioTracks[trackIdx];
423
+ if (clipIdx >= track.clips.numItems) return __error("Clip index " + clipIdx + " out of range");
424
+ var clip = track.clips[clipIdx];
425
+ var removed = [];
426
+ for (var i = 0; i < clip.components.numItems; i++) {
427
+ var comp = clip.components[i];
428
+ if (comp.matchName === "audioVolume" || comp.displayName === "Volume") {
429
+ for (var p = 0; p < comp.properties.numItems; p++) {
430
+ if (comp.properties[p].displayName === "Level") {
431
+ try { comp.properties[p].setTimeVarying(false); removed.push("Volume Level"); } catch(e) {}
432
+ break;
433
+ }
434
+ }
435
+ }
436
+ if (comp.matchName === "audioPanner" || comp.displayName === "Panner") {
437
+ for (var p2 = 0; p2 < comp.properties.numItems; p2++) {
438
+ var prop2 = comp.properties[p2];
439
+ if (prop2.displayName === "Balance" || prop2.displayName === "Panner" || prop2.displayName === "Left/Right Balance") {
440
+ try { prop2.setTimeVarying(false); removed.push("Panner"); } catch(e2) {}
441
+ break;
442
+ }
443
+ }
444
+ }
445
+ }
446
+ return __result({ trackIndex: trackIdx, clipIndex: clipIdx, clip: clip.name, removedFrom: removed });
447
447
  `);
448
448
  return sendCommand(script, bridgeOptions);
449
449
  },
@@ -460,31 +460,31 @@ export function getAudioAdvancedTools(bridgeOptions) {
460
460
  required: ["track_index", "clip_index"],
461
461
  },
462
462
  handler: async (args) => {
463
- const script = buildToolScript(`
464
- var seq = app.project.activeSequence;
465
- if (!seq) return __error("No active sequence");
466
- var trackIdx = ${args.track_index};
467
- var clipIdx = ${args.clip_index};
468
- if (trackIdx >= seq.audioTracks.numTracks) return __error("Track index " + trackIdx + " out of range");
469
- var track = seq.audioTracks[trackIdx];
470
- if (clipIdx >= track.clips.numItems) return __error("Clip index " + clipIdx + " out of range");
471
- app.enableQE();
472
- if (!qe || !qe.project) return __error("QE DOM not available");
473
- var qeSeq = qe.project.getActiveSequence();
474
- if (!qeSeq) return __error("No active sequence in QE DOM");
475
- var qeTrack = qeSeq.getAudioTrackAt(trackIdx);
476
- if (!qeTrack) return __error("Track not found in QE");
477
- var qeClip = qeTrack.getItemAt(clipIdx);
478
- if (!qeClip) return __error("Clip not found in QE");
479
- var effectNames = ["Parametric Equalizer", "Studio Equalizer", "Graphic Equalizer (10 Band)", "Graphic Equalizer (20 Band)"];
480
- var effect = null;
481
- for (var i = 0; i < effectNames.length; i++) {
482
- try { effect = qe.project.getAudioEffectByName(effectNames[i]); } catch(e) {}
483
- if (effect) { break; }
484
- }
485
- if (!effect) return __error("Parametric Equalizer effect not found. Ensure it is installed.");
486
- qeClip.addAudioEffect(effect);
487
- return __result({ trackIndex: trackIdx, clipIndex: clipIdx, effect: "Parametric Equalizer", applied: true });
463
+ const script = buildToolScript(`
464
+ var seq = app.project.activeSequence;
465
+ if (!seq) return __error("No active sequence");
466
+ var trackIdx = ${args.track_index};
467
+ var clipIdx = ${args.clip_index};
468
+ if (trackIdx >= seq.audioTracks.numTracks) return __error("Track index " + trackIdx + " out of range");
469
+ var track = seq.audioTracks[trackIdx];
470
+ if (clipIdx >= track.clips.numItems) return __error("Clip index " + clipIdx + " out of range");
471
+ app.enableQE();
472
+ if (!qe || !qe.project) return __error("QE DOM not available");
473
+ var qeSeq = qe.project.getActiveSequence();
474
+ if (!qeSeq) return __error("No active sequence in QE DOM");
475
+ var qeTrack = qeSeq.getAudioTrackAt(trackIdx);
476
+ if (!qeTrack) return __error("Track not found in QE");
477
+ var qeClip = qeTrack.getItemAt(clipIdx);
478
+ if (!qeClip) return __error("Clip not found in QE");
479
+ var effectNames = ["Parametric Equalizer", "Studio Equalizer", "Graphic Equalizer (10 Band)", "Graphic Equalizer (20 Band)"];
480
+ var effect = null;
481
+ for (var i = 0; i < effectNames.length; i++) {
482
+ try { effect = qe.project.getAudioEffectByName(effectNames[i]); } catch(e) {}
483
+ if (effect) { break; }
484
+ }
485
+ if (!effect) return __error("Parametric Equalizer effect not found. Ensure it is installed.");
486
+ qeClip.addAudioEffect(effect);
487
+ return __result({ trackIndex: trackIdx, clipIndex: clipIdx, effect: "Parametric Equalizer", applied: true });
488
488
  `);
489
489
  return sendCommand(script, bridgeOptions);
490
490
  },
@@ -508,31 +508,31 @@ export function getAudioAdvancedTools(bridgeOptions) {
508
508
  const ratio = args.ratio ?? 4;
509
509
  const attack = args.attack ?? 10;
510
510
  const release = args.release ?? 100;
511
- const script = buildToolScript(`
512
- var seq = app.project.activeSequence;
513
- if (!seq) return __error("No active sequence");
514
- var trackIdx = ${args.track_index};
515
- var clipIdx = ${args.clip_index};
516
- if (trackIdx >= seq.audioTracks.numTracks) return __error("Track index " + trackIdx + " out of range");
517
- var track = seq.audioTracks[trackIdx];
518
- if (clipIdx >= track.clips.numItems) return __error("Clip index " + clipIdx + " out of range");
519
- app.enableQE();
520
- if (!qe || !qe.project) return __error("QE DOM not available");
521
- var qeSeq = qe.project.getActiveSequence();
522
- if (!qeSeq) return __error("No active sequence in QE DOM");
523
- var qeTrack = qeSeq.getAudioTrackAt(trackIdx);
524
- if (!qeTrack) return __error("Track not found in QE");
525
- var qeClip = qeTrack.getItemAt(clipIdx);
526
- if (!qeClip) return __error("Clip not found in QE");
527
- var effectNames = ["Dynamics", "Compressor", "Multiband Compressor", "Single-band Compressor"];
528
- var effect = null;
529
- for (var i = 0; i < effectNames.length; i++) {
530
- try { effect = qe.project.getAudioEffectByName(effectNames[i]); } catch(e) {}
531
- if (effect) break;
532
- }
533
- if (!effect) return __error("Dynamics/Compressor effect not found.");
534
- qeClip.addAudioEffect(effect);
535
- return __result({ trackIndex: trackIdx, clipIndex: clipIdx, effect: "Dynamics", threshold: ${threshold}, ratio: ${ratio}, attack: ${attack}, release: ${release}, applied: true, note: "Effect added. Set parameters manually in Effect Controls panel." });
511
+ const script = buildToolScript(`
512
+ var seq = app.project.activeSequence;
513
+ if (!seq) return __error("No active sequence");
514
+ var trackIdx = ${args.track_index};
515
+ var clipIdx = ${args.clip_index};
516
+ if (trackIdx >= seq.audioTracks.numTracks) return __error("Track index " + trackIdx + " out of range");
517
+ var track = seq.audioTracks[trackIdx];
518
+ if (clipIdx >= track.clips.numItems) return __error("Clip index " + clipIdx + " out of range");
519
+ app.enableQE();
520
+ if (!qe || !qe.project) return __error("QE DOM not available");
521
+ var qeSeq = qe.project.getActiveSequence();
522
+ if (!qeSeq) return __error("No active sequence in QE DOM");
523
+ var qeTrack = qeSeq.getAudioTrackAt(trackIdx);
524
+ if (!qeTrack) return __error("Track not found in QE");
525
+ var qeClip = qeTrack.getItemAt(clipIdx);
526
+ if (!qeClip) return __error("Clip not found in QE");
527
+ var effectNames = ["Dynamics", "Compressor", "Multiband Compressor", "Single-band Compressor"];
528
+ var effect = null;
529
+ for (var i = 0; i < effectNames.length; i++) {
530
+ try { effect = qe.project.getAudioEffectByName(effectNames[i]); } catch(e) {}
531
+ if (effect) break;
532
+ }
533
+ if (!effect) return __error("Dynamics/Compressor effect not found.");
534
+ qeClip.addAudioEffect(effect);
535
+ return __result({ trackIndex: trackIdx, clipIndex: clipIdx, effect: "Dynamics", threshold: ${threshold}, ratio: ${ratio}, attack: ${attack}, release: ${release}, applied: true, note: "Effect added. Set parameters manually in Effect Controls panel." });
536
536
  `);
537
537
  return sendCommand(script, bridgeOptions);
538
538
  },
@@ -550,31 +550,31 @@ export function getAudioAdvancedTools(bridgeOptions) {
550
550
  },
551
551
  handler: async (args) => {
552
552
  const ceiling = args.ceiling ?? -0.1;
553
- const script = buildToolScript(`
554
- var seq = app.project.activeSequence;
555
- if (!seq) return __error("No active sequence");
556
- var trackIdx = ${args.track_index};
557
- var clipIdx = ${args.clip_index};
558
- if (trackIdx >= seq.audioTracks.numTracks) return __error("Track index " + trackIdx + " out of range");
559
- var track = seq.audioTracks[trackIdx];
560
- if (clipIdx >= track.clips.numItems) return __error("Clip index " + clipIdx + " out of range");
561
- app.enableQE();
562
- if (!qe || !qe.project) return __error("QE DOM not available");
563
- var qeSeq = qe.project.getActiveSequence();
564
- if (!qeSeq) return __error("No active sequence in QE DOM");
565
- var qeTrack = qeSeq.getAudioTrackAt(trackIdx);
566
- if (!qeTrack) return __error("Track not found in QE");
567
- var qeClip = qeTrack.getItemAt(clipIdx);
568
- if (!qeClip) return __error("Clip not found in QE");
569
- var effectNames = ["Hard Limiter", "Limiter", "Loudness Maximizer"];
570
- var effect = null;
571
- for (var i = 0; i < effectNames.length; i++) {
572
- try { effect = qe.project.getAudioEffectByName(effectNames[i]); } catch(e) {}
573
- if (effect) break;
574
- }
575
- if (!effect) return __error("Hard Limiter effect not found.");
576
- qeClip.addAudioEffect(effect);
577
- return __result({ trackIndex: trackIdx, clipIndex: clipIdx, effect: "Hard Limiter", ceiling: ${ceiling}, applied: true, note: "Effect added. Adjust ceiling parameter in Effect Controls panel." });
553
+ const script = buildToolScript(`
554
+ var seq = app.project.activeSequence;
555
+ if (!seq) return __error("No active sequence");
556
+ var trackIdx = ${args.track_index};
557
+ var clipIdx = ${args.clip_index};
558
+ if (trackIdx >= seq.audioTracks.numTracks) return __error("Track index " + trackIdx + " out of range");
559
+ var track = seq.audioTracks[trackIdx];
560
+ if (clipIdx >= track.clips.numItems) return __error("Clip index " + clipIdx + " out of range");
561
+ app.enableQE();
562
+ if (!qe || !qe.project) return __error("QE DOM not available");
563
+ var qeSeq = qe.project.getActiveSequence();
564
+ if (!qeSeq) return __error("No active sequence in QE DOM");
565
+ var qeTrack = qeSeq.getAudioTrackAt(trackIdx);
566
+ if (!qeTrack) return __error("Track not found in QE");
567
+ var qeClip = qeTrack.getItemAt(clipIdx);
568
+ if (!qeClip) return __error("Clip not found in QE");
569
+ var effectNames = ["Hard Limiter", "Limiter", "Loudness Maximizer"];
570
+ var effect = null;
571
+ for (var i = 0; i < effectNames.length; i++) {
572
+ try { effect = qe.project.getAudioEffectByName(effectNames[i]); } catch(e) {}
573
+ if (effect) break;
574
+ }
575
+ if (!effect) return __error("Hard Limiter effect not found.");
576
+ qeClip.addAudioEffect(effect);
577
+ return __result({ trackIndex: trackIdx, clipIndex: clipIdx, effect: "Hard Limiter", ceiling: ${ceiling}, applied: true, note: "Effect added. Adjust ceiling parameter in Effect Controls panel." });
578
578
  `);
579
579
  return sendCommand(script, bridgeOptions);
580
580
  },
@@ -594,31 +594,31 @@ export function getAudioAdvancedTools(bridgeOptions) {
594
594
  handler: async (args) => {
595
595
  const frequency = args.frequency ?? 6000;
596
596
  const reduction = args.reduction ?? -10;
597
- const script = buildToolScript(`
598
- var seq = app.project.activeSequence;
599
- if (!seq) return __error("No active sequence");
600
- var trackIdx = ${args.track_index};
601
- var clipIdx = ${args.clip_index};
602
- if (trackIdx >= seq.audioTracks.numTracks) return __error("Track index " + trackIdx + " out of range");
603
- var track = seq.audioTracks[trackIdx];
604
- if (clipIdx >= track.clips.numItems) return __error("Clip index " + clipIdx + " out of range");
605
- app.enableQE();
606
- if (!qe || !qe.project) return __error("QE DOM not available");
607
- var qeSeq = qe.project.getActiveSequence();
608
- if (!qeSeq) return __error("No active sequence in QE DOM");
609
- var qeTrack = qeSeq.getAudioTrackAt(trackIdx);
610
- if (!qeTrack) return __error("Track not found in QE");
611
- var qeClip = qeTrack.getItemAt(clipIdx);
612
- if (!qeClip) return __error("Clip not found in QE");
613
- var effectNames = ["DeEsser", "De-Esser", "Spectral DeEsser"];
614
- var effect = null;
615
- for (var i = 0; i < effectNames.length; i++) {
616
- try { effect = qe.project.getAudioEffectByName(effectNames[i]); } catch(e) {}
617
- if (effect) break;
618
- }
619
- if (!effect) return __error("DeEsser effect not found.");
620
- qeClip.addAudioEffect(effect);
621
- return __result({ trackIndex: trackIdx, clipIndex: clipIdx, effect: "DeEsser", frequency: ${frequency}, reduction: ${reduction}, applied: true, note: "Effect added. Adjust parameters in Effect Controls panel." });
597
+ const script = buildToolScript(`
598
+ var seq = app.project.activeSequence;
599
+ if (!seq) return __error("No active sequence");
600
+ var trackIdx = ${args.track_index};
601
+ var clipIdx = ${args.clip_index};
602
+ if (trackIdx >= seq.audioTracks.numTracks) return __error("Track index " + trackIdx + " out of range");
603
+ var track = seq.audioTracks[trackIdx];
604
+ if (clipIdx >= track.clips.numItems) return __error("Clip index " + clipIdx + " out of range");
605
+ app.enableQE();
606
+ if (!qe || !qe.project) return __error("QE DOM not available");
607
+ var qeSeq = qe.project.getActiveSequence();
608
+ if (!qeSeq) return __error("No active sequence in QE DOM");
609
+ var qeTrack = qeSeq.getAudioTrackAt(trackIdx);
610
+ if (!qeTrack) return __error("Track not found in QE");
611
+ var qeClip = qeTrack.getItemAt(clipIdx);
612
+ if (!qeClip) return __error("Clip not found in QE");
613
+ var effectNames = ["DeEsser", "De-Esser", "Spectral DeEsser"];
614
+ var effect = null;
615
+ for (var i = 0; i < effectNames.length; i++) {
616
+ try { effect = qe.project.getAudioEffectByName(effectNames[i]); } catch(e) {}
617
+ if (effect) break;
618
+ }
619
+ if (!effect) return __error("DeEsser effect not found.");
620
+ qeClip.addAudioEffect(effect);
621
+ return __result({ trackIndex: trackIdx, clipIndex: clipIdx, effect: "DeEsser", frequency: ${frequency}, reduction: ${reduction}, applied: true, note: "Effect added. Adjust parameters in Effect Controls panel." });
622
622
  `);
623
623
  return sendCommand(script, bridgeOptions);
624
624
  },
@@ -636,31 +636,31 @@ export function getAudioAdvancedTools(bridgeOptions) {
636
636
  },
637
637
  handler: async (args) => {
638
638
  const amount = args.amount ?? 50;
639
- const script = buildToolScript(`
640
- var seq = app.project.activeSequence;
641
- if (!seq) return __error("No active sequence");
642
- var trackIdx = ${args.track_index};
643
- var clipIdx = ${args.clip_index};
644
- if (trackIdx >= seq.audioTracks.numTracks) return __error("Track index " + trackIdx + " out of range");
645
- var track = seq.audioTracks[trackIdx];
646
- if (clipIdx >= track.clips.numItems) return __error("Clip index " + clipIdx + " out of range");
647
- app.enableQE();
648
- if (!qe || !qe.project) return __error("QE DOM not available");
649
- var qeSeq = qe.project.getActiveSequence();
650
- if (!qeSeq) return __error("No active sequence in QE DOM");
651
- var qeTrack = qeSeq.getAudioTrackAt(trackIdx);
652
- if (!qeTrack) return __error("Track not found in QE");
653
- var qeClip = qeTrack.getItemAt(clipIdx);
654
- if (!qeClip) return __error("Clip not found in QE");
655
- var effectNames = ["DeNoise", "Noise Reduction", "Adaptive Noise Reduction"];
656
- var effect = null;
657
- for (var i = 0; i < effectNames.length; i++) {
658
- try { effect = qe.project.getAudioEffectByName(effectNames[i]); } catch(e) {}
659
- if (effect) break;
660
- }
661
- if (!effect) return __error("DeNoise effect not found.");
662
- qeClip.addAudioEffect(effect);
663
- return __result({ trackIndex: trackIdx, clipIndex: clipIdx, effect: "DeNoise", amount: ${amount}, applied: true, note: "Effect added. Adjust amount parameter in Effect Controls panel." });
639
+ const script = buildToolScript(`
640
+ var seq = app.project.activeSequence;
641
+ if (!seq) return __error("No active sequence");
642
+ var trackIdx = ${args.track_index};
643
+ var clipIdx = ${args.clip_index};
644
+ if (trackIdx >= seq.audioTracks.numTracks) return __error("Track index " + trackIdx + " out of range");
645
+ var track = seq.audioTracks[trackIdx];
646
+ if (clipIdx >= track.clips.numItems) return __error("Clip index " + clipIdx + " out of range");
647
+ app.enableQE();
648
+ if (!qe || !qe.project) return __error("QE DOM not available");
649
+ var qeSeq = qe.project.getActiveSequence();
650
+ if (!qeSeq) return __error("No active sequence in QE DOM");
651
+ var qeTrack = qeSeq.getAudioTrackAt(trackIdx);
652
+ if (!qeTrack) return __error("Track not found in QE");
653
+ var qeClip = qeTrack.getItemAt(clipIdx);
654
+ if (!qeClip) return __error("Clip not found in QE");
655
+ var effectNames = ["DeNoise", "Noise Reduction", "Adaptive Noise Reduction"];
656
+ var effect = null;
657
+ for (var i = 0; i < effectNames.length; i++) {
658
+ try { effect = qe.project.getAudioEffectByName(effectNames[i]); } catch(e) {}
659
+ if (effect) break;
660
+ }
661
+ if (!effect) return __error("DeNoise effect not found.");
662
+ qeClip.addAudioEffect(effect);
663
+ return __result({ trackIndex: trackIdx, clipIndex: clipIdx, effect: "DeNoise", amount: ${amount}, applied: true, note: "Effect added. Adjust amount parameter in Effect Controls panel." });
664
664
  `);
665
665
  return sendCommand(script, bridgeOptions);
666
666
  },
@@ -678,31 +678,31 @@ export function getAudioAdvancedTools(bridgeOptions) {
678
678
  },
679
679
  handler: async (args) => {
680
680
  const amount = args.amount ?? 50;
681
- const script = buildToolScript(`
682
- var seq = app.project.activeSequence;
683
- if (!seq) return __error("No active sequence");
684
- var trackIdx = ${args.track_index};
685
- var clipIdx = ${args.clip_index};
686
- if (trackIdx >= seq.audioTracks.numTracks) return __error("Track index " + trackIdx + " out of range");
687
- var track = seq.audioTracks[trackIdx];
688
- if (clipIdx >= track.clips.numItems) return __error("Clip index " + clipIdx + " out of range");
689
- app.enableQE();
690
- if (!qe || !qe.project) return __error("QE DOM not available");
691
- var qeSeq = qe.project.getActiveSequence();
692
- if (!qeSeq) return __error("No active sequence in QE DOM");
693
- var qeTrack = qeSeq.getAudioTrackAt(trackIdx);
694
- if (!qeTrack) return __error("Track not found in QE");
695
- var qeClip = qeTrack.getItemAt(clipIdx);
696
- if (!qeClip) return __error("Clip not found in QE");
697
- var effectNames = ["DeReverb", "De-Reverb", "Spectral Noise Reduction"];
698
- var effect = null;
699
- for (var i = 0; i < effectNames.length; i++) {
700
- try { effect = qe.project.getAudioEffectByName(effectNames[i]); } catch(e) {}
701
- if (effect) break;
702
- }
703
- if (!effect) return __error("DeReverb effect not found.");
704
- qeClip.addAudioEffect(effect);
705
- return __result({ trackIndex: trackIdx, clipIndex: clipIdx, effect: "DeReverb", amount: ${amount}, applied: true, note: "Effect added. Adjust amount in Effect Controls panel." });
681
+ const script = buildToolScript(`
682
+ var seq = app.project.activeSequence;
683
+ if (!seq) return __error("No active sequence");
684
+ var trackIdx = ${args.track_index};
685
+ var clipIdx = ${args.clip_index};
686
+ if (trackIdx >= seq.audioTracks.numTracks) return __error("Track index " + trackIdx + " out of range");
687
+ var track = seq.audioTracks[trackIdx];
688
+ if (clipIdx >= track.clips.numItems) return __error("Clip index " + clipIdx + " out of range");
689
+ app.enableQE();
690
+ if (!qe || !qe.project) return __error("QE DOM not available");
691
+ var qeSeq = qe.project.getActiveSequence();
692
+ if (!qeSeq) return __error("No active sequence in QE DOM");
693
+ var qeTrack = qeSeq.getAudioTrackAt(trackIdx);
694
+ if (!qeTrack) return __error("Track not found in QE");
695
+ var qeClip = qeTrack.getItemAt(clipIdx);
696
+ if (!qeClip) return __error("Clip not found in QE");
697
+ var effectNames = ["DeReverb", "De-Reverb", "Spectral Noise Reduction"];
698
+ var effect = null;
699
+ for (var i = 0; i < effectNames.length; i++) {
700
+ try { effect = qe.project.getAudioEffectByName(effectNames[i]); } catch(e) {}
701
+ if (effect) break;
702
+ }
703
+ if (!effect) return __error("DeReverb effect not found.");
704
+ qeClip.addAudioEffect(effect);
705
+ return __result({ trackIndex: trackIdx, clipIndex: clipIdx, effect: "DeReverb", amount: ${amount}, applied: true, note: "Effect added. Adjust amount in Effect Controls panel." });
706
706
  `);
707
707
  return sendCommand(script, bridgeOptions);
708
708
  },
@@ -720,31 +720,31 @@ export function getAudioAdvancedTools(bridgeOptions) {
720
720
  },
721
721
  handler: async (args) => {
722
722
  const frequency = args.frequency ?? 60;
723
- const script = buildToolScript(`
724
- var seq = app.project.activeSequence;
725
- if (!seq) return __error("No active sequence");
726
- var trackIdx = ${args.track_index};
727
- var clipIdx = ${args.clip_index};
728
- if (trackIdx >= seq.audioTracks.numTracks) return __error("Track index " + trackIdx + " out of range");
729
- var track = seq.audioTracks[trackIdx];
730
- if (clipIdx >= track.clips.numItems) return __error("Clip index " + clipIdx + " out of range");
731
- app.enableQE();
732
- if (!qe || !qe.project) return __error("QE DOM not available");
733
- var qeSeq = qe.project.getActiveSequence();
734
- if (!qeSeq) return __error("No active sequence in QE DOM");
735
- var qeTrack = qeSeq.getAudioTrackAt(trackIdx);
736
- if (!qeTrack) return __error("Track not found in QE");
737
- var qeClip = qeTrack.getItemAt(clipIdx);
738
- if (!qeClip) return __error("Clip not found in QE");
739
- var effectNames = ["DeHummer", "DeHum", "Notch Filter"];
740
- var effect = null;
741
- for (var i = 0; i < effectNames.length; i++) {
742
- try { effect = qe.project.getAudioEffectByName(effectNames[i]); } catch(e) {}
743
- if (effect) break;
744
- }
745
- if (!effect) return __error("DeHummer effect not found.");
746
- qeClip.addAudioEffect(effect);
747
- return __result({ trackIndex: trackIdx, clipIndex: clipIdx, effect: "DeHummer", frequency: ${frequency}, applied: true, note: "Effect added. Set frequency to " + ${frequency} + "Hz in Effect Controls panel." });
723
+ const script = buildToolScript(`
724
+ var seq = app.project.activeSequence;
725
+ if (!seq) return __error("No active sequence");
726
+ var trackIdx = ${args.track_index};
727
+ var clipIdx = ${args.clip_index};
728
+ if (trackIdx >= seq.audioTracks.numTracks) return __error("Track index " + trackIdx + " out of range");
729
+ var track = seq.audioTracks[trackIdx];
730
+ if (clipIdx >= track.clips.numItems) return __error("Clip index " + clipIdx + " out of range");
731
+ app.enableQE();
732
+ if (!qe || !qe.project) return __error("QE DOM not available");
733
+ var qeSeq = qe.project.getActiveSequence();
734
+ if (!qeSeq) return __error("No active sequence in QE DOM");
735
+ var qeTrack = qeSeq.getAudioTrackAt(trackIdx);
736
+ if (!qeTrack) return __error("Track not found in QE");
737
+ var qeClip = qeTrack.getItemAt(clipIdx);
738
+ if (!qeClip) return __error("Clip not found in QE");
739
+ var effectNames = ["DeHummer", "DeHum", "Notch Filter"];
740
+ var effect = null;
741
+ for (var i = 0; i < effectNames.length; i++) {
742
+ try { effect = qe.project.getAudioEffectByName(effectNames[i]); } catch(e) {}
743
+ if (effect) break;
744
+ }
745
+ if (!effect) return __error("DeHummer effect not found.");
746
+ qeClip.addAudioEffect(effect);
747
+ return __result({ trackIndex: trackIdx, clipIndex: clipIdx, effect: "DeHummer", frequency: ${frequency}, applied: true, note: "Effect added. Set frequency to " + ${frequency} + "Hz in Effect Controls panel." });
748
748
  `);
749
749
  return sendCommand(script, bridgeOptions);
750
750
  },
@@ -762,28 +762,28 @@ export function getAudioAdvancedTools(bridgeOptions) {
762
762
  },
763
763
  handler: async (args) => {
764
764
  const presetNameEsc = args.preset_name.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
765
- const script = buildToolScript(`
766
- var seq = app.project.activeSequence;
767
- if (!seq) return __error("No active sequence");
768
- var trackIdx = ${args.track_index};
769
- var clipIdx = ${args.clip_index};
770
- var presetName = "${presetNameEsc}";
771
- if (trackIdx >= seq.audioTracks.numTracks) return __error("Track index " + trackIdx + " out of range");
772
- var track = seq.audioTracks[trackIdx];
773
- if (clipIdx >= track.clips.numItems) return __error("Clip index " + clipIdx + " out of range");
774
- app.enableQE();
775
- if (!qe || !qe.project) return __error("QE DOM not available");
776
- var qeSeq = qe.project.getActiveSequence();
777
- if (!qeSeq) return __error("No active sequence in QE DOM");
778
- var qeTrack = qeSeq.getAudioTrackAt(trackIdx);
779
- if (!qeTrack) return __error("Track not found in QE");
780
- var qeClip = qeTrack.getItemAt(clipIdx);
781
- if (!qeClip) return __error("Clip not found in QE");
782
- var effect = null;
783
- try { effect = qe.project.getAudioEffectByName(presetName); } catch(e) {}
784
- if (!effect) return __error("Audio effect not found: " + presetName + ". Use get_audio_effect_presets to see available effects.");
785
- qeClip.addAudioEffect(effect);
786
- return __result({ trackIndex: trackIdx, clipIndex: clipIdx, effect: presetName, applied: true });
765
+ const script = buildToolScript(`
766
+ var seq = app.project.activeSequence;
767
+ if (!seq) return __error("No active sequence");
768
+ var trackIdx = ${args.track_index};
769
+ var clipIdx = ${args.clip_index};
770
+ var presetName = "${presetNameEsc}";
771
+ if (trackIdx >= seq.audioTracks.numTracks) return __error("Track index " + trackIdx + " out of range");
772
+ var track = seq.audioTracks[trackIdx];
773
+ if (clipIdx >= track.clips.numItems) return __error("Clip index " + clipIdx + " out of range");
774
+ app.enableQE();
775
+ if (!qe || !qe.project) return __error("QE DOM not available");
776
+ var qeSeq = qe.project.getActiveSequence();
777
+ if (!qeSeq) return __error("No active sequence in QE DOM");
778
+ var qeTrack = qeSeq.getAudioTrackAt(trackIdx);
779
+ if (!qeTrack) return __error("Track not found in QE");
780
+ var qeClip = qeTrack.getItemAt(clipIdx);
781
+ if (!qeClip) return __error("Clip not found in QE");
782
+ var effect = null;
783
+ try { effect = qe.project.getAudioEffectByName(presetName); } catch(e) {}
784
+ if (!effect) return __error("Audio effect not found: " + presetName + ". Use get_audio_effect_presets to see available effects.");
785
+ qeClip.addAudioEffect(effect);
786
+ return __result({ trackIndex: trackIdx, clipIndex: clipIdx, effect: presetName, applied: true });
787
787
  `);
788
788
  return sendCommand(script, bridgeOptions);
789
789
  },
@@ -796,37 +796,37 @@ export function getAudioAdvancedTools(bridgeOptions) {
796
796
  required: [],
797
797
  },
798
798
  handler: async (_args) => {
799
- const script = buildToolScript(`
800
- var commonEffects = [
801
- { name: "Parametric Equalizer", category: "EQ", description: "Multi-band parametric EQ" },
802
- { name: "Studio Equalizer", category: "EQ", description: "Graphical EQ with bands" },
803
- { name: "Graphic Equalizer (10 Band)", category: "EQ", description: "10-band graphic EQ" },
804
- { name: "Graphic Equalizer (20 Band)", category: "EQ", description: "20-band graphic EQ" },
805
- { name: "Dynamics", category: "Dynamics", description: "Compressor/expander/gate/limiter" },
806
- { name: "Multiband Compressor", category: "Dynamics", description: "Multi-band compression" },
807
- { name: "Single-band Compressor", category: "Dynamics", description: "Single-band compression" },
808
- { name: "Hard Limiter", category: "Dynamics", description: "Brick wall limiter" },
809
- { name: "Loudness Maximizer", category: "Dynamics", description: "Maximize perceived loudness" },
810
- { name: "DeNoise", category: "Restoration", description: "Reduce background noise" },
811
- { name: "DeReverb", category: "Restoration", description: "Reduce room reverb" },
812
- { name: "DeHummer", category: "Restoration", description: "Remove electrical hum" },
813
- { name: "DeEsser", category: "Restoration", description: "Reduce sibilance" },
814
- { name: "Adaptive Noise Reduction", category: "Restoration", description: "Adaptive noise gate" },
815
- { name: "Click/Pop Eliminator", category: "Restoration", description: "Remove clicks and pops" },
816
- { name: "Reverb", category: "Ambience", description: "Add room reverb" },
817
- { name: "Studio Reverb", category: "Ambience", description: "Studio reverb effect" },
818
- { name: "Convolution Reverb", category: "Ambience", description: "Convolution-based reverb" },
819
- { name: "Chorus/Flanger", category: "Modulation", description: "Chorus and flanger" },
820
- { name: "Flanger", category: "Modulation", description: "Flanger modulation" },
821
- { name: "Phaser", category: "Modulation", description: "Phaser effect" },
822
- { name: "Pitch Shifter", category: "Pitch", description: "Real-time pitch shifting" },
823
- { name: "Analog Delay", category: "Delay", description: "Tape-style delay" },
824
- { name: "Delay", category: "Delay", description: "Basic digital delay" },
825
- { name: "Notch Filter", category: "Filter", description: "Notch/band-reject filter" },
826
- { name: "High Pass", category: "Filter", description: "High-pass filter" },
827
- { name: "Low Pass", category: "Filter", description: "Low-pass filter" }
828
- ];
829
- return __result({ effects: commonEffects, note: "List shows common Premiere Pro audio effects. Available effects depend on your installation and installed audio plugins." });
799
+ const script = buildToolScript(`
800
+ var commonEffects = [
801
+ { name: "Parametric Equalizer", category: "EQ", description: "Multi-band parametric EQ" },
802
+ { name: "Studio Equalizer", category: "EQ", description: "Graphical EQ with bands" },
803
+ { name: "Graphic Equalizer (10 Band)", category: "EQ", description: "10-band graphic EQ" },
804
+ { name: "Graphic Equalizer (20 Band)", category: "EQ", description: "20-band graphic EQ" },
805
+ { name: "Dynamics", category: "Dynamics", description: "Compressor/expander/gate/limiter" },
806
+ { name: "Multiband Compressor", category: "Dynamics", description: "Multi-band compression" },
807
+ { name: "Single-band Compressor", category: "Dynamics", description: "Single-band compression" },
808
+ { name: "Hard Limiter", category: "Dynamics", description: "Brick wall limiter" },
809
+ { name: "Loudness Maximizer", category: "Dynamics", description: "Maximize perceived loudness" },
810
+ { name: "DeNoise", category: "Restoration", description: "Reduce background noise" },
811
+ { name: "DeReverb", category: "Restoration", description: "Reduce room reverb" },
812
+ { name: "DeHummer", category: "Restoration", description: "Remove electrical hum" },
813
+ { name: "DeEsser", category: "Restoration", description: "Reduce sibilance" },
814
+ { name: "Adaptive Noise Reduction", category: "Restoration", description: "Adaptive noise gate" },
815
+ { name: "Click/Pop Eliminator", category: "Restoration", description: "Remove clicks and pops" },
816
+ { name: "Reverb", category: "Ambience", description: "Add room reverb" },
817
+ { name: "Studio Reverb", category: "Ambience", description: "Studio reverb effect" },
818
+ { name: "Convolution Reverb", category: "Ambience", description: "Convolution-based reverb" },
819
+ { name: "Chorus/Flanger", category: "Modulation", description: "Chorus and flanger" },
820
+ { name: "Flanger", category: "Modulation", description: "Flanger modulation" },
821
+ { name: "Phaser", category: "Modulation", description: "Phaser effect" },
822
+ { name: "Pitch Shifter", category: "Pitch", description: "Real-time pitch shifting" },
823
+ { name: "Analog Delay", category: "Delay", description: "Tape-style delay" },
824
+ { name: "Delay", category: "Delay", description: "Basic digital delay" },
825
+ { name: "Notch Filter", category: "Filter", description: "Notch/band-reject filter" },
826
+ { name: "High Pass", category: "Filter", description: "High-pass filter" },
827
+ { name: "Low Pass", category: "Filter", description: "Low-pass filter" }
828
+ ];
829
+ return __result({ effects: commonEffects, note: "List shows common Premiere Pro audio effects. Available effects depend on your installation and installed audio plugins." });
830
830
  `);
831
831
  return sendCommand(script, bridgeOptions);
832
832
  },
@@ -842,43 +842,43 @@ export function getAudioAdvancedTools(bridgeOptions) {
842
842
  required: ["track_index", "clip_index"],
843
843
  },
844
844
  handler: async (args) => {
845
- const script = buildToolScript(`
846
- var seq = app.project.activeSequence;
847
- if (!seq) return __error("No active sequence");
848
- var trackIdx = ${args.track_index};
849
- var clipIdx = ${args.clip_index};
850
- if (trackIdx >= seq.audioTracks.numTracks) return __error("Track index " + trackIdx + " out of range");
851
- var track = seq.audioTracks[trackIdx];
852
- if (clipIdx >= track.clips.numItems) return __error("Clip index " + clipIdx + " out of range");
853
- var clip = track.clips[clipIdx];
854
- var levelDb = 0;
855
- var found = false;
856
- for (var i = 0; i < clip.components.numItems; i++) {
857
- var comp = clip.components[i];
858
- if (comp.matchName === "audioVolume" || comp.displayName === "Volume") {
859
- for (var p = 0; p < comp.properties.numItems; p++) {
860
- if (comp.properties[p].displayName === "Level") {
861
- try { levelDb = comp.properties[p].getValue(); found = true; } catch(e) {}
862
- break;
863
- }
864
- }
865
- }
866
- if (found) break;
867
- }
868
- var startSecs = clip.start ? __ticksToSeconds(clip.start.ticks) : 0;
869
- var endSecs = clip.end ? __ticksToSeconds(clip.end.ticks) : 0;
870
- var durationSecs = endSecs - startSecs;
871
- return __result({
872
- trackIndex: trackIdx,
873
- clipIndex: clipIdx,
874
- clip: clip.name,
875
- levelDb: levelDb,
876
- durationSeconds: durationSecs,
877
- startSeconds: startSecs,
878
- endSeconds: endSecs,
879
- couldClip: levelDb > 0,
880
- note: "LUFS integrated loudness requires dedicated loudness analysis. levelDb is the static clip gain/Level property."
881
- });
845
+ const script = buildToolScript(`
846
+ var seq = app.project.activeSequence;
847
+ if (!seq) return __error("No active sequence");
848
+ var trackIdx = ${args.track_index};
849
+ var clipIdx = ${args.clip_index};
850
+ if (trackIdx >= seq.audioTracks.numTracks) return __error("Track index " + trackIdx + " out of range");
851
+ var track = seq.audioTracks[trackIdx];
852
+ if (clipIdx >= track.clips.numItems) return __error("Clip index " + clipIdx + " out of range");
853
+ var clip = track.clips[clipIdx];
854
+ var levelDb = 0;
855
+ var found = false;
856
+ for (var i = 0; i < clip.components.numItems; i++) {
857
+ var comp = clip.components[i];
858
+ if (comp.matchName === "audioVolume" || comp.displayName === "Volume") {
859
+ for (var p = 0; p < comp.properties.numItems; p++) {
860
+ if (comp.properties[p].displayName === "Level") {
861
+ try { levelDb = comp.properties[p].getValue(); found = true; } catch(e) {}
862
+ break;
863
+ }
864
+ }
865
+ }
866
+ if (found) break;
867
+ }
868
+ var startSecs = clip.start ? __ticksToSeconds(clip.start.ticks) : 0;
869
+ var endSecs = clip.end ? __ticksToSeconds(clip.end.ticks) : 0;
870
+ var durationSecs = endSecs - startSecs;
871
+ return __result({
872
+ trackIndex: trackIdx,
873
+ clipIndex: clipIdx,
874
+ clip: clip.name,
875
+ levelDb: levelDb,
876
+ durationSeconds: durationSecs,
877
+ startSeconds: startSecs,
878
+ endSeconds: endSecs,
879
+ couldClip: levelDb > 0,
880
+ note: "LUFS integrated loudness requires dedicated loudness analysis. levelDb is the static clip gain/Level property."
881
+ });
882
882
  `);
883
883
  return sendCommand(script, bridgeOptions);
884
884
  },
@@ -891,37 +891,37 @@ export function getAudioAdvancedTools(bridgeOptions) {
891
891
  required: [],
892
892
  },
893
893
  handler: async (_args) => {
894
- const script = buildToolScript(`
895
- var seq = app.project.activeSequence;
896
- if (!seq) return __error("No active sequence");
897
- var tracks = [];
898
- var totalClips = 0;
899
- var maxLevel = -96;
900
- for (var t = 0; t < seq.audioTracks.numTracks; t++) {
901
- var track = seq.audioTracks[t];
902
- var trackData = { index: t, name: track.name || ("Audio " + (t + 1)), clips: [] };
903
- for (var c = 0; c < track.clips.numItems; c++) {
904
- var clip = track.clips[c];
905
- var levelDb = 0;
906
- for (var i = 0; i < clip.components.numItems; i++) {
907
- var comp = clip.components[i];
908
- if (comp.matchName === "audioVolume" || comp.displayName === "Volume") {
909
- for (var p = 0; p < comp.properties.numItems; p++) {
910
- if (comp.properties[p].displayName === "Level") {
911
- try { levelDb = comp.properties[p].getValue(); } catch(e) {}
912
- break;
913
- }
914
- }
915
- break;
916
- }
917
- }
918
- if (levelDb > maxLevel) maxLevel = levelDb;
919
- trackData.clips.push({ name: clip.name, levelDb: levelDb, start: __ticksToSeconds(clip.start.ticks), end: __ticksToSeconds(clip.end.ticks) });
920
- totalClips++;
921
- }
922
- tracks.push(trackData);
923
- }
924
- return __result({ tracks: tracks, totalClips: totalClips, maxLevelDb: maxLevel, couldClip: maxLevel > 0 });
894
+ const script = buildToolScript(`
895
+ var seq = app.project.activeSequence;
896
+ if (!seq) return __error("No active sequence");
897
+ var tracks = [];
898
+ var totalClips = 0;
899
+ var maxLevel = -96;
900
+ for (var t = 0; t < seq.audioTracks.numTracks; t++) {
901
+ var track = seq.audioTracks[t];
902
+ var trackData = { index: t, name: track.name || ("Audio " + (t + 1)), clips: [] };
903
+ for (var c = 0; c < track.clips.numItems; c++) {
904
+ var clip = track.clips[c];
905
+ var levelDb = 0;
906
+ for (var i = 0; i < clip.components.numItems; i++) {
907
+ var comp = clip.components[i];
908
+ if (comp.matchName === "audioVolume" || comp.displayName === "Volume") {
909
+ for (var p = 0; p < comp.properties.numItems; p++) {
910
+ if (comp.properties[p].displayName === "Level") {
911
+ try { levelDb = comp.properties[p].getValue(); } catch(e) {}
912
+ break;
913
+ }
914
+ }
915
+ break;
916
+ }
917
+ }
918
+ if (levelDb > maxLevel) maxLevel = levelDb;
919
+ trackData.clips.push({ name: clip.name, levelDb: levelDb, start: __ticksToSeconds(clip.start.ticks), end: __ticksToSeconds(clip.end.ticks) });
920
+ totalClips++;
921
+ }
922
+ tracks.push(trackData);
923
+ }
924
+ return __result({ tracks: tracks, totalClips: totalClips, maxLevelDb: maxLevel, couldClip: maxLevel > 0 });
925
925
  `);
926
926
  return sendCommand(script, bridgeOptions);
927
927
  },
@@ -937,37 +937,37 @@ export function getAudioAdvancedTools(bridgeOptions) {
937
937
  required: ["track_index", "clip_index"],
938
938
  },
939
939
  handler: async (args) => {
940
- const script = buildToolScript(`
941
- var seq = app.project.activeSequence;
942
- if (!seq) return __error("No active sequence");
943
- var trackIdx = ${args.track_index};
944
- var clipIdx = ${args.clip_index};
945
- if (trackIdx >= seq.audioTracks.numTracks) return __error("Track index " + trackIdx + " out of range");
946
- var track = seq.audioTracks[trackIdx];
947
- if (clipIdx >= track.clips.numItems) return __error("Clip index " + clipIdx + " out of range");
948
- var clip = track.clips[clipIdx];
949
- var levelDb = 0;
950
- for (var i = 0; i < clip.components.numItems; i++) {
951
- var comp = clip.components[i];
952
- if (comp.matchName === "audioVolume" || comp.displayName === "Volume") {
953
- for (var p = 0; p < comp.properties.numItems; p++) {
954
- if (comp.properties[p].displayName === "Level") {
955
- try { levelDb = comp.properties[p].getValue(); } catch(e) {}
956
- break;
957
- }
958
- }
959
- break;
960
- }
961
- }
962
- var couldClip = levelDb > 0;
963
- return __result({
964
- trackIndex: trackIdx,
965
- clipIndex: clipIdx,
966
- clip: clip.name,
967
- levelDb: levelDb,
968
- couldClip: couldClip,
969
- message: couldClip ? "WARNING: Clip level is " + levelDb + " dB (above 0 dB) - may cause digital clipping." : "OK: Clip level " + levelDb + " dB is at or below 0 dB."
970
- });
940
+ const script = buildToolScript(`
941
+ var seq = app.project.activeSequence;
942
+ if (!seq) return __error("No active sequence");
943
+ var trackIdx = ${args.track_index};
944
+ var clipIdx = ${args.clip_index};
945
+ if (trackIdx >= seq.audioTracks.numTracks) return __error("Track index " + trackIdx + " out of range");
946
+ var track = seq.audioTracks[trackIdx];
947
+ if (clipIdx >= track.clips.numItems) return __error("Clip index " + clipIdx + " out of range");
948
+ var clip = track.clips[clipIdx];
949
+ var levelDb = 0;
950
+ for (var i = 0; i < clip.components.numItems; i++) {
951
+ var comp = clip.components[i];
952
+ if (comp.matchName === "audioVolume" || comp.displayName === "Volume") {
953
+ for (var p = 0; p < comp.properties.numItems; p++) {
954
+ if (comp.properties[p].displayName === "Level") {
955
+ try { levelDb = comp.properties[p].getValue(); } catch(e) {}
956
+ break;
957
+ }
958
+ }
959
+ break;
960
+ }
961
+ }
962
+ var couldClip = levelDb > 0;
963
+ return __result({
964
+ trackIndex: trackIdx,
965
+ clipIndex: clipIdx,
966
+ clip: clip.name,
967
+ levelDb: levelDb,
968
+ couldClip: couldClip,
969
+ message: couldClip ? "WARNING: Clip level is " + levelDb + " dB (above 0 dB) - may cause digital clipping." : "OK: Clip level " + levelDb + " dB is at or below 0 dB."
970
+ });
971
971
  `);
972
972
  return sendCommand(script, bridgeOptions);
973
973
  },
@@ -983,38 +983,38 @@ export function getAudioAdvancedTools(bridgeOptions) {
983
983
  },
984
984
  handler: async (args) => {
985
985
  const targetDb = args.target_db ?? -6;
986
- const script = buildToolScript(`
987
- var seq = app.project.activeSequence;
988
- if (!seq) return __error("No active sequence");
989
- var targetDb = ${targetDb};
990
- var totalSet = 0;
991
- var errors = [];
992
- for (var t = 0; t < seq.audioTracks.numTracks; t++) {
993
- var track = seq.audioTracks[t];
994
- for (var c = 0; c < track.clips.numItems; c++) {
995
- var clip = track.clips[c];
996
- var found = false;
997
- for (var i = 0; i < clip.components.numItems; i++) {
998
- var comp = clip.components[i];
999
- if (comp.matchName === "audioVolume" || comp.displayName === "Volume") {
1000
- for (var p = 0; p < comp.properties.numItems; p++) {
1001
- if (comp.properties[p].displayName === "Level") {
1002
- try {
1003
- comp.properties[p].setValue(targetDb, true);
1004
- totalSet++;
1005
- found = true;
1006
- } catch(e) {
1007
- errors.push("Track " + t + " clip " + c + ": " + e.toString());
1008
- }
1009
- break;
1010
- }
1011
- }
1012
- }
1013
- if (found) break;
1014
- }
1015
- }
1016
- }
1017
- return __result({ targetDb: targetDb, clipsNormalized: totalSet, errors: errors });
986
+ const script = buildToolScript(`
987
+ var seq = app.project.activeSequence;
988
+ if (!seq) return __error("No active sequence");
989
+ var targetDb = ${targetDb};
990
+ var totalSet = 0;
991
+ var errors = [];
992
+ for (var t = 0; t < seq.audioTracks.numTracks; t++) {
993
+ var track = seq.audioTracks[t];
994
+ for (var c = 0; c < track.clips.numItems; c++) {
995
+ var clip = track.clips[c];
996
+ var found = false;
997
+ for (var i = 0; i < clip.components.numItems; i++) {
998
+ var comp = clip.components[i];
999
+ if (comp.matchName === "audioVolume" || comp.displayName === "Volume") {
1000
+ for (var p = 0; p < comp.properties.numItems; p++) {
1001
+ if (comp.properties[p].displayName === "Level") {
1002
+ try {
1003
+ comp.properties[p].setValue(targetDb, true);
1004
+ totalSet++;
1005
+ found = true;
1006
+ } catch(e) {
1007
+ errors.push("Track " + t + " clip " + c + ": " + e.toString());
1008
+ }
1009
+ break;
1010
+ }
1011
+ }
1012
+ }
1013
+ if (found) break;
1014
+ }
1015
+ }
1016
+ }
1017
+ return __result({ targetDb: targetDb, clipsNormalized: totalSet, errors: errors });
1018
1018
  `);
1019
1019
  return sendCommand(script, bridgeOptions);
1020
1020
  },
@@ -1027,17 +1027,17 @@ export function getAudioAdvancedTools(bridgeOptions) {
1027
1027
  required: [],
1028
1028
  },
1029
1029
  handler: async (_args) => {
1030
- const script = buildToolScript(`
1031
- var seq = app.project.activeSequence;
1032
- if (!seq) return __error("No active sequence");
1033
- var count = 0;
1034
- for (var t = 0; t < seq.audioTracks.numTracks; t++) {
1035
- var track = seq.audioTracks[t];
1036
- try {
1037
- if (track.setMute) { track.setMute(1); count++; }
1038
- } catch(e) {}
1039
- }
1040
- return __result({ muted: count, totalTracks: seq.audioTracks.numTracks });
1030
+ const script = buildToolScript(`
1031
+ var seq = app.project.activeSequence;
1032
+ if (!seq) return __error("No active sequence");
1033
+ var count = 0;
1034
+ for (var t = 0; t < seq.audioTracks.numTracks; t++) {
1035
+ var track = seq.audioTracks[t];
1036
+ try {
1037
+ if (track.setMute) { track.setMute(1); count++; }
1038
+ } catch(e) {}
1039
+ }
1040
+ return __result({ muted: count, totalTracks: seq.audioTracks.numTracks });
1041
1041
  `);
1042
1042
  return sendCommand(script, bridgeOptions);
1043
1043
  },
@@ -1050,17 +1050,17 @@ export function getAudioAdvancedTools(bridgeOptions) {
1050
1050
  required: [],
1051
1051
  },
1052
1052
  handler: async (_args) => {
1053
- const script = buildToolScript(`
1054
- var seq = app.project.activeSequence;
1055
- if (!seq) return __error("No active sequence");
1056
- var count = 0;
1057
- for (var t = 0; t < seq.audioTracks.numTracks; t++) {
1058
- var track = seq.audioTracks[t];
1059
- try {
1060
- if (track.setMute) { track.setMute(0); count++; }
1061
- } catch(e) {}
1062
- }
1063
- return __result({ unmuted: count, totalTracks: seq.audioTracks.numTracks });
1053
+ const script = buildToolScript(`
1054
+ var seq = app.project.activeSequence;
1055
+ if (!seq) return __error("No active sequence");
1056
+ var count = 0;
1057
+ for (var t = 0; t < seq.audioTracks.numTracks; t++) {
1058
+ var track = seq.audioTracks[t];
1059
+ try {
1060
+ if (track.setMute) { track.setMute(0); count++; }
1061
+ } catch(e) {}
1062
+ }
1063
+ return __result({ unmuted: count, totalTracks: seq.audioTracks.numTracks });
1064
1064
  `);
1065
1065
  return sendCommand(script, bridgeOptions);
1066
1066
  },
@@ -1078,26 +1078,26 @@ export function getAudioAdvancedTools(bridgeOptions) {
1078
1078
  handler: async (args) => {
1079
1079
  const name = (args.name || "Submix").replace(/"/g, '\\"');
1080
1080
  const channelType = (args.channel_type || "stereo").replace(/"/g, '\\"');
1081
- const script = buildToolScript(`
1082
- var seq = app.project.activeSequence;
1083
- if (!seq) return __error("No active sequence");
1084
- var trackName = "${name}";
1085
- var channelType = "${channelType}";
1086
- try {
1087
- app.enableQE();
1088
- if (qe && qe.project) {
1089
- var qeSeq = qe.project.getActiveSequence();
1090
- if (qeSeq && qeSeq.addAudioSubmixTrack) {
1091
- var channelTypeMap = { "mono": 1, "stereo": 2, "5.1": 6, "adaptive": 0 };
1092
- var chType = channelTypeMap[channelType] !== undefined ? channelTypeMap[channelType] : 2;
1093
- qeSeq.addAudioSubmixTrack(chType);
1094
- return __result({ name: trackName, channelType: channelType, created: true });
1095
- }
1096
- }
1097
- } catch(e) {
1098
- return __error("Could not create submix track via QE DOM: " + e.toString());
1099
- }
1100
- return __result({ note: "Submix track creation not available via this API in the current Premiere version. Add via Sequence > Add Tracks in the Premiere Pro UI.", requestedName: trackName, requestedChannelType: channelType });
1081
+ const script = buildToolScript(`
1082
+ var seq = app.project.activeSequence;
1083
+ if (!seq) return __error("No active sequence");
1084
+ var trackName = "${name}";
1085
+ var channelType = "${channelType}";
1086
+ try {
1087
+ app.enableQE();
1088
+ if (qe && qe.project) {
1089
+ var qeSeq = qe.project.getActiveSequence();
1090
+ if (qeSeq && qeSeq.addAudioSubmixTrack) {
1091
+ var channelTypeMap = { "mono": 1, "stereo": 2, "5.1": 6, "adaptive": 0 };
1092
+ var chType = channelTypeMap[channelType] !== undefined ? channelTypeMap[channelType] : 2;
1093
+ qeSeq.addAudioSubmixTrack(chType);
1094
+ return __result({ name: trackName, channelType: channelType, created: true });
1095
+ }
1096
+ }
1097
+ } catch(e) {
1098
+ return __error("Could not create submix track via QE DOM: " + e.toString());
1099
+ }
1100
+ return __result({ note: "Submix track creation not available via this API in the current Premiere version. Add via Sequence > Add Tracks in the Premiere Pro UI.", requestedName: trackName, requestedChannelType: channelType });
1101
1101
  `);
1102
1102
  return sendCommand(script, bridgeOptions);
1103
1103
  },
@@ -1112,22 +1112,22 @@ export function getAudioAdvancedTools(bridgeOptions) {
1112
1112
  required: ["track_index"],
1113
1113
  },
1114
1114
  handler: async (args) => {
1115
- const script = buildToolScript(`
1116
- var seq = app.project.activeSequence;
1117
- if (!seq) return __error("No active sequence");
1118
- var voTrackIdx = ${args.track_index};
1119
- if (voTrackIdx >= seq.audioTracks.numTracks) return __error("Track index " + voTrackIdx + " out of range");
1120
- var mutedCount = 0;
1121
- var unmutedCount = 0;
1122
- for (var t = 0; t < seq.audioTracks.numTracks; t++) {
1123
- var track = seq.audioTracks[t];
1124
- if (t !== voTrackIdx) {
1125
- try { if (track.setMute) { track.setMute(1); mutedCount++; } } catch(e) {}
1126
- } else {
1127
- try { if (track.setMute) { track.setMute(0); unmutedCount++; } } catch(e) {}
1128
- }
1129
- }
1130
- return __result({ voTrackIndex: voTrackIdx, voTrackName: seq.audioTracks[voTrackIdx].name || ("Audio " + (voTrackIdx + 1)), otherTracksMuted: mutedCount, voTrackActive: true });
1115
+ const script = buildToolScript(`
1116
+ var seq = app.project.activeSequence;
1117
+ if (!seq) return __error("No active sequence");
1118
+ var voTrackIdx = ${args.track_index};
1119
+ if (voTrackIdx >= seq.audioTracks.numTracks) return __error("Track index " + voTrackIdx + " out of range");
1120
+ var mutedCount = 0;
1121
+ var unmutedCount = 0;
1122
+ for (var t = 0; t < seq.audioTracks.numTracks; t++) {
1123
+ var track = seq.audioTracks[t];
1124
+ if (t !== voTrackIdx) {
1125
+ try { if (track.setMute) { track.setMute(1); mutedCount++; } } catch(e) {}
1126
+ } else {
1127
+ try { if (track.setMute) { track.setMute(0); unmutedCount++; } } catch(e) {}
1128
+ }
1129
+ }
1130
+ return __result({ voTrackIndex: voTrackIdx, voTrackName: seq.audioTracks[voTrackIdx].name || ("Audio " + (voTrackIdx + 1)), otherTracksMuted: mutedCount, voTrackActive: true });
1131
1131
  `);
1132
1132
  return sendCommand(script, bridgeOptions);
1133
1133
  },
@@ -1147,63 +1147,63 @@ export function getAudioAdvancedTools(bridgeOptions) {
1147
1147
  handler: async (args) => {
1148
1148
  const duckAmount = args.duck_amount ?? -15;
1149
1149
  const sensitivity = args.sensitivity ?? 0.5;
1150
- const script = buildToolScript(`
1151
- var seq = app.project.activeSequence;
1152
- if (!seq) return __error("No active sequence");
1153
- var voTrackIdx = ${args.vo_track_index};
1154
- var musicTrackIdx = ${args.music_track_index};
1155
- var duckAmount = ${duckAmount};
1156
- var fadeDuration = ${sensitivity};
1157
- if (voTrackIdx >= seq.audioTracks.numTracks) return __error("VO track index " + voTrackIdx + " out of range");
1158
- if (musicTrackIdx >= seq.audioTracks.numTracks) return __error("Music track index " + musicTrackIdx + " out of range");
1159
- var voTrack = seq.audioTracks[voTrackIdx];
1160
- var musicTrack = seq.audioTracks[musicTrackIdx];
1161
- var processedClips = 0;
1162
- var errors = [];
1163
- for (var mc = 0; mc < musicTrack.clips.numItems; mc++) {
1164
- var musicClip = musicTrack.clips[mc];
1165
- var levelProp = null;
1166
- for (var ci = 0; ci < musicClip.components.numItems; ci++) {
1167
- var comp = musicClip.components[ci];
1168
- if (comp.matchName === "audioVolume" || comp.displayName === "Volume") {
1169
- for (var pi = 0; pi < comp.properties.numItems; pi++) {
1170
- if (comp.properties[pi].displayName === "Level") {
1171
- levelProp = comp.properties[pi];
1172
- break;
1173
- }
1174
- }
1175
- }
1176
- if (levelProp) break;
1177
- }
1178
- if (!levelProp) continue;
1179
- levelProp.setTimeVarying(true);
1180
- var musicStart = __ticksToSeconds(musicClip.start.ticks);
1181
- var musicEnd = __ticksToSeconds(musicClip.end.ticks);
1182
- for (var vc = 0; vc < voTrack.clips.numItems; vc++) {
1183
- var voClip = voTrack.clips[vc];
1184
- var voStart = __ticksToSeconds(voClip.start.ticks);
1185
- var voEnd = __ticksToSeconds(voClip.end.ticks);
1186
- if (voStart >= musicEnd || voEnd <= musicStart) continue;
1187
- var fadeInTime = Math.max(musicStart, voStart - fadeDuration);
1188
- var fadeInEnd = Math.min(musicEnd, voStart);
1189
- var fadeOutStart = Math.max(musicStart, voEnd);
1190
- var fadeOutEnd = Math.min(musicEnd, voEnd + fadeDuration);
1191
- try {
1192
- var t1 = new Time(); t1.ticks = String(__secondsToTicks(fadeInTime));
1193
- levelProp.addKey(t1); levelProp.setValueAtKey(t1, 0);
1194
- var t2 = new Time(); t2.ticks = String(__secondsToTicks(fadeInEnd));
1195
- levelProp.addKey(t2); levelProp.setValueAtKey(t2, duckAmount);
1196
- var t3 = new Time(); t3.ticks = String(__secondsToTicks(fadeOutStart));
1197
- levelProp.addKey(t3); levelProp.setValueAtKey(t3, duckAmount);
1198
- var t4 = new Time(); t4.ticks = String(__secondsToTicks(fadeOutEnd));
1199
- levelProp.addKey(t4); levelProp.setValueAtKey(t4, 0);
1200
- processedClips++;
1201
- } catch(e) {
1202
- errors.push("VO clip " + vc + ": " + e.toString());
1203
- }
1204
- }
1205
- }
1206
- return __result({ voTrackIndex: voTrackIdx, musicTrackIndex: musicTrackIdx, duckAmount: duckAmount, fadeDuration: fadeDuration, duckingRegionsAdded: processedClips, errors: errors });
1150
+ const script = buildToolScript(`
1151
+ var seq = app.project.activeSequence;
1152
+ if (!seq) return __error("No active sequence");
1153
+ var voTrackIdx = ${args.vo_track_index};
1154
+ var musicTrackIdx = ${args.music_track_index};
1155
+ var duckAmount = ${duckAmount};
1156
+ var fadeDuration = ${sensitivity};
1157
+ if (voTrackIdx >= seq.audioTracks.numTracks) return __error("VO track index " + voTrackIdx + " out of range");
1158
+ if (musicTrackIdx >= seq.audioTracks.numTracks) return __error("Music track index " + musicTrackIdx + " out of range");
1159
+ var voTrack = seq.audioTracks[voTrackIdx];
1160
+ var musicTrack = seq.audioTracks[musicTrackIdx];
1161
+ var processedClips = 0;
1162
+ var errors = [];
1163
+ for (var mc = 0; mc < musicTrack.clips.numItems; mc++) {
1164
+ var musicClip = musicTrack.clips[mc];
1165
+ var levelProp = null;
1166
+ for (var ci = 0; ci < musicClip.components.numItems; ci++) {
1167
+ var comp = musicClip.components[ci];
1168
+ if (comp.matchName === "audioVolume" || comp.displayName === "Volume") {
1169
+ for (var pi = 0; pi < comp.properties.numItems; pi++) {
1170
+ if (comp.properties[pi].displayName === "Level") {
1171
+ levelProp = comp.properties[pi];
1172
+ break;
1173
+ }
1174
+ }
1175
+ }
1176
+ if (levelProp) break;
1177
+ }
1178
+ if (!levelProp) continue;
1179
+ levelProp.setTimeVarying(true);
1180
+ var musicStart = __ticksToSeconds(musicClip.start.ticks);
1181
+ var musicEnd = __ticksToSeconds(musicClip.end.ticks);
1182
+ for (var vc = 0; vc < voTrack.clips.numItems; vc++) {
1183
+ var voClip = voTrack.clips[vc];
1184
+ var voStart = __ticksToSeconds(voClip.start.ticks);
1185
+ var voEnd = __ticksToSeconds(voClip.end.ticks);
1186
+ if (voStart >= musicEnd || voEnd <= musicStart) continue;
1187
+ var fadeInTime = Math.max(musicStart, voStart - fadeDuration);
1188
+ var fadeInEnd = Math.min(musicEnd, voStart);
1189
+ var fadeOutStart = Math.max(musicStart, voEnd);
1190
+ var fadeOutEnd = Math.min(musicEnd, voEnd + fadeDuration);
1191
+ try {
1192
+ var t1 = new Time(); t1.ticks = String(__secondsToTicks(fadeInTime));
1193
+ levelProp.addKey(t1); levelProp.setValueAtKey(t1, 0);
1194
+ var t2 = new Time(); t2.ticks = String(__secondsToTicks(fadeInEnd));
1195
+ levelProp.addKey(t2); levelProp.setValueAtKey(t2, duckAmount);
1196
+ var t3 = new Time(); t3.ticks = String(__secondsToTicks(fadeOutStart));
1197
+ levelProp.addKey(t3); levelProp.setValueAtKey(t3, duckAmount);
1198
+ var t4 = new Time(); t4.ticks = String(__secondsToTicks(fadeOutEnd));
1199
+ levelProp.addKey(t4); levelProp.setValueAtKey(t4, 0);
1200
+ processedClips++;
1201
+ } catch(e) {
1202
+ errors.push("VO clip " + vc + ": " + e.toString());
1203
+ }
1204
+ }
1205
+ }
1206
+ return __result({ voTrackIndex: voTrackIdx, musicTrackIndex: musicTrackIdx, duckAmount: duckAmount, fadeDuration: fadeDuration, duckingRegionsAdded: processedClips, errors: errors });
1207
1207
  `);
1208
1208
  return sendCommand(script, bridgeOptions);
1209
1209
  },
@@ -1219,31 +1219,31 @@ export function getAudioAdvancedTools(bridgeOptions) {
1219
1219
  required: ["track_index", "level_db"],
1220
1220
  },
1221
1221
  handler: async (args) => {
1222
- const script = buildToolScript(`
1223
- var seq = app.project.activeSequence;
1224
- if (!seq) return __error("No active sequence");
1225
- var trackIdx = ${args.track_index};
1226
- var levelDb = ${args.level_db};
1227
- if (trackIdx >= seq.audioTracks.numTracks) return __error("Track index " + trackIdx + " out of range");
1228
- var track = seq.audioTracks[trackIdx];
1229
- var found = false;
1230
- if (track.components) {
1231
- for (var ci = 0; ci < track.components.numItems; ci++) {
1232
- var comp = track.components[ci];
1233
- if (comp.matchName === "audioVolume" || comp.displayName === "Volume") {
1234
- for (var pi = 0; pi < comp.properties.numItems; pi++) {
1235
- if (comp.properties[pi].displayName === "Level") {
1236
- comp.properties[pi].setValue(levelDb, true);
1237
- found = true;
1238
- break;
1239
- }
1240
- }
1241
- }
1242
- if (found) break;
1243
- }
1244
- }
1245
- if (!found) return __result({ note: "Track-level Volume component not found on track " + trackIdx + ". Track fader may not be accessible via ExtendScript.", trackIndex: trackIdx, requestedLevel: levelDb });
1246
- return __result({ trackIndex: trackIdx, levelDb: levelDb, applied: true });
1222
+ const script = buildToolScript(`
1223
+ var seq = app.project.activeSequence;
1224
+ if (!seq) return __error("No active sequence");
1225
+ var trackIdx = ${args.track_index};
1226
+ var levelDb = ${args.level_db};
1227
+ if (trackIdx >= seq.audioTracks.numTracks) return __error("Track index " + trackIdx + " out of range");
1228
+ var track = seq.audioTracks[trackIdx];
1229
+ var found = false;
1230
+ if (track.components) {
1231
+ for (var ci = 0; ci < track.components.numItems; ci++) {
1232
+ var comp = track.components[ci];
1233
+ if (comp.matchName === "audioVolume" || comp.displayName === "Volume") {
1234
+ for (var pi = 0; pi < comp.properties.numItems; pi++) {
1235
+ if (comp.properties[pi].displayName === "Level") {
1236
+ comp.properties[pi].setValue(levelDb, true);
1237
+ found = true;
1238
+ break;
1239
+ }
1240
+ }
1241
+ }
1242
+ if (found) break;
1243
+ }
1244
+ }
1245
+ if (!found) return __result({ note: "Track-level Volume component not found on track " + trackIdx + ". Track fader may not be accessible via ExtendScript.", trackIndex: trackIdx, requestedLevel: levelDb });
1246
+ return __result({ trackIndex: trackIdx, levelDb: levelDb, applied: true });
1247
1247
  `);
1248
1248
  return sendCommand(script, bridgeOptions);
1249
1249
  },
@@ -1259,18 +1259,18 @@ export function getAudioAdvancedTools(bridgeOptions) {
1259
1259
  required: ["track_index", "soloed"],
1260
1260
  },
1261
1261
  handler: async (args) => {
1262
- const script = buildToolScript(`
1263
- var seq = app.project.activeSequence;
1264
- if (!seq) return __error("No active sequence");
1265
- var trackIdx = ${args.track_index};
1266
- var soloed = ${args.soloed};
1267
- if (trackIdx >= seq.audioTracks.numTracks) return __error("Track index " + trackIdx + " out of range");
1268
- var track = seq.audioTracks[trackIdx];
1269
- if (track.setSolo) {
1270
- track.setSolo(soloed ? 1 : 0);
1271
- return __result({ trackIndex: trackIdx, soloed: soloed });
1272
- }
1273
- return __result({ note: "setSolo not supported on this track type in the current Premiere version.", trackIndex: trackIdx, requestedSolo: soloed });
1262
+ const script = buildToolScript(`
1263
+ var seq = app.project.activeSequence;
1264
+ if (!seq) return __error("No active sequence");
1265
+ var trackIdx = ${args.track_index};
1266
+ var soloed = ${args.soloed};
1267
+ if (trackIdx >= seq.audioTracks.numTracks) return __error("Track index " + trackIdx + " out of range");
1268
+ var track = seq.audioTracks[trackIdx];
1269
+ if (track.setSolo) {
1270
+ track.setSolo(soloed ? 1 : 0);
1271
+ return __result({ trackIndex: trackIdx, soloed: soloed });
1272
+ }
1273
+ return __result({ note: "setSolo not supported on this track type in the current Premiere version.", trackIndex: trackIdx, requestedSolo: soloed });
1274
1274
  `);
1275
1275
  return sendCommand(script, bridgeOptions);
1276
1276
  },
@@ -1285,24 +1285,24 @@ export function getAudioAdvancedTools(bridgeOptions) {
1285
1285
  required: ["project_item_index"],
1286
1286
  },
1287
1287
  handler: async (args) => {
1288
- const script = buildToolScript(`
1289
- var itemIndex = ${args.project_item_index};
1290
- if (!app.project || !app.project.rootItem) return __error("No project open");
1291
- if (itemIndex >= app.project.rootItem.children.numItems) return __error("Item index " + itemIndex + " out of range");
1292
- var item = app.project.rootItem.children[itemIndex];
1293
- try {
1294
- var interp = item.getFootageInterpretation();
1295
- if (!interp) return __error("Cannot get footage interpretation for: " + item.name);
1296
- // audioChannelType: 0=Stereo, 1=Mono, 2=5.1, 4=Adaptive
1297
- var prevType = interp.audioChannelType;
1298
- interp.audioChannelType = 1; // Mono
1299
- item.setFootageInterpretation(interp);
1300
- return __result({ item: item.name, index: itemIndex, converted: true,
1301
- previousChannelType: prevType, newChannelType: 1,
1302
- note: "Audio channel type set to Mono (1). This affects how PP treats the audio channels of this item." });
1303
- } catch(e) {
1304
- return __error("Could not set audio channel type: " + e.toString());
1305
- }
1288
+ const script = buildToolScript(`
1289
+ var itemIndex = ${args.project_item_index};
1290
+ if (!app.project || !app.project.rootItem) return __error("No project open");
1291
+ if (itemIndex >= app.project.rootItem.children.numItems) return __error("Item index " + itemIndex + " out of range");
1292
+ var item = app.project.rootItem.children[itemIndex];
1293
+ try {
1294
+ var interp = item.getFootageInterpretation();
1295
+ if (!interp) return __error("Cannot get footage interpretation for: " + item.name);
1296
+ // audioChannelType: 0=Stereo, 1=Mono, 2=5.1, 4=Adaptive
1297
+ var prevType = interp.audioChannelType;
1298
+ interp.audioChannelType = 1; // Mono
1299
+ item.setFootageInterpretation(interp);
1300
+ return __result({ item: item.name, index: itemIndex, converted: true,
1301
+ previousChannelType: prevType, newChannelType: 1,
1302
+ note: "Audio channel type set to Mono (1). This affects how PP treats the audio channels of this item." });
1303
+ } catch(e) {
1304
+ return __error("Could not set audio channel type: " + e.toString());
1305
+ }
1306
1306
  `);
1307
1307
  return sendCommand(script, bridgeOptions);
1308
1308
  },
@@ -1317,25 +1317,25 @@ export function getAudioAdvancedTools(bridgeOptions) {
1317
1317
  required: ["project_item_index"],
1318
1318
  },
1319
1319
  handler: async (args) => {
1320
- const script = buildToolScript(`
1321
- var itemIndex = ${args.project_item_index};
1322
- if (!app.project || !app.project.rootItem) return __error("No project open");
1323
- if (itemIndex >= app.project.rootItem.children.numItems) return __error("Item index " + itemIndex + " out of range");
1324
- var item = app.project.rootItem.children[itemIndex];
1325
- var mediaPath = "";
1326
- try { mediaPath = item.getMediaPath(); } catch(e) {}
1327
- // Gather audio interpretation info
1328
- var audioInfo = {};
1329
- try {
1330
- var interp = item.getFootageInterpretation();
1331
- if (interp) {
1332
- audioInfo.channelType = interp.audioChannelType;
1333
- audioInfo.numChannels = interp.audioNumChannels;
1334
- }
1335
- } catch(e) {}
1336
- return __result({ item: item.name, index: itemIndex, mediaPath: mediaPath,
1337
- audioInfo: audioInfo,
1338
- note: "To extract audio as a separate file, use export_audio_only tool with this item's media path as source. Audio interpretation info shown above." });
1320
+ const script = buildToolScript(`
1321
+ var itemIndex = ${args.project_item_index};
1322
+ if (!app.project || !app.project.rootItem) return __error("No project open");
1323
+ if (itemIndex >= app.project.rootItem.children.numItems) return __error("Item index " + itemIndex + " out of range");
1324
+ var item = app.project.rootItem.children[itemIndex];
1325
+ var mediaPath = "";
1326
+ try { mediaPath = item.getMediaPath(); } catch(e) {}
1327
+ // Gather audio interpretation info
1328
+ var audioInfo = {};
1329
+ try {
1330
+ var interp = item.getFootageInterpretation();
1331
+ if (interp) {
1332
+ audioInfo.channelType = interp.audioChannelType;
1333
+ audioInfo.numChannels = interp.audioNumChannels;
1334
+ }
1335
+ } catch(e) {}
1336
+ return __result({ item: item.name, index: itemIndex, mediaPath: mediaPath,
1337
+ audioInfo: audioInfo,
1338
+ note: "To extract audio as a separate file, use export_audio_only tool with this item's media path as source. Audio interpretation info shown above." });
1339
1339
  `);
1340
1340
  return sendCommand(script, bridgeOptions);
1341
1341
  },
@@ -1354,28 +1354,28 @@ export function getAudioAdvancedTools(bridgeOptions) {
1354
1354
  },
1355
1355
  handler: async (args) => {
1356
1356
  const effectNameEsc = args.effect_name.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
1357
- const script = buildToolScript(`
1358
- var seq = app.project.activeSequence;
1359
- if (!seq) return __error("No active sequence");
1360
- var trackIdx = ${args.track_index};
1361
- var clipIdx = ${args.clip_index};
1362
- var effectName = "${effectNameEsc}";
1363
- if (trackIdx >= seq.audioTracks.numTracks) return __error("Track index " + trackIdx + " out of range");
1364
- var track = seq.audioTracks[trackIdx];
1365
- if (clipIdx >= track.clips.numItems) return __error("Clip index " + clipIdx + " out of range");
1366
- app.enableQE();
1367
- if (!qe || !qe.project) return __error("QE DOM not available");
1368
- var qeSeq = qe.project.getActiveSequence();
1369
- if (!qeSeq) return __error("No active sequence in QE DOM");
1370
- var qeTrack = qeSeq.getAudioTrackAt(trackIdx);
1371
- if (!qeTrack) return __error("Track " + trackIdx + " not found in QE");
1372
- var qeClip = qeTrack.getItemAt(clipIdx);
1373
- if (!qeClip) return __error("Clip " + clipIdx + " not found in QE");
1374
- var effect = null;
1375
- try { effect = qe.project.getAudioEffectByName(effectName); } catch(e) {}
1376
- if (!effect) return __error("Audio effect not found: " + effectName + ". Use get_audio_effect_presets for available effects.");
1377
- qeClip.addAudioEffect(effect);
1378
- return __result({ trackIndex: trackIdx, clipIndex: clipIdx, effectName: effectName, applied: true });
1357
+ const script = buildToolScript(`
1358
+ var seq = app.project.activeSequence;
1359
+ if (!seq) return __error("No active sequence");
1360
+ var trackIdx = ${args.track_index};
1361
+ var clipIdx = ${args.clip_index};
1362
+ var effectName = "${effectNameEsc}";
1363
+ if (trackIdx >= seq.audioTracks.numTracks) return __error("Track index " + trackIdx + " out of range");
1364
+ var track = seq.audioTracks[trackIdx];
1365
+ if (clipIdx >= track.clips.numItems) return __error("Clip index " + clipIdx + " out of range");
1366
+ app.enableQE();
1367
+ if (!qe || !qe.project) return __error("QE DOM not available");
1368
+ var qeSeq = qe.project.getActiveSequence();
1369
+ if (!qeSeq) return __error("No active sequence in QE DOM");
1370
+ var qeTrack = qeSeq.getAudioTrackAt(trackIdx);
1371
+ if (!qeTrack) return __error("Track " + trackIdx + " not found in QE");
1372
+ var qeClip = qeTrack.getItemAt(clipIdx);
1373
+ if (!qeClip) return __error("Clip " + clipIdx + " not found in QE");
1374
+ var effect = null;
1375
+ try { effect = qe.project.getAudioEffectByName(effectName); } catch(e) {}
1376
+ if (!effect) return __error("Audio effect not found: " + effectName + ". Use get_audio_effect_presets for available effects.");
1377
+ qeClip.addAudioEffect(effect);
1378
+ return __result({ trackIndex: trackIdx, clipIndex: clipIdx, effectName: effectName, applied: true });
1379
1379
  `);
1380
1380
  return sendCommand(script, bridgeOptions);
1381
1381
  },
@@ -1394,30 +1394,30 @@ export function getAudioAdvancedTools(bridgeOptions) {
1394
1394
  },
1395
1395
  handler: async (args) => {
1396
1396
  const targetDb = args.target_db ?? 0;
1397
- const script = buildToolScript(`
1398
- var seq = app.project.activeSequence;
1399
- if (!seq) return __error("No active sequence");
1400
- var trackIdx = ${args.track_index};
1401
- var clipIdx = ${args.clip_index};
1402
- var fadeDuration = ${args.duration_seconds};
1403
- var targetDb = ${targetDb};
1404
- if (trackIdx >= seq.audioTracks.numTracks) return __error("Track index " + trackIdx + " out of range");
1405
- var track = seq.audioTracks[trackIdx];
1406
- if (clipIdx >= track.clips.numItems) return __error("Clip index " + clipIdx + " out of range");
1407
- var clip = track.clips[clipIdx];
1408
- var volComp = __findIntrinsic(clip, "volume");
1409
- if (!volComp) return __error("Volume component not found on clip");
1410
- var levelProp = __findProp(volComp, ["Level", "Nivel", "Pegel", "Volume"]);
1411
- if (!levelProp) return __error("Volume Level property not found on clip");
1412
- var clipStart = __ticksToSeconds(clip.start.ticks);
1413
- var clipEnd = __ticksToSeconds(clip.end.ticks);
1414
- var fadeEnd = Math.min(clipStart + fadeDuration, clipEnd);
1415
- levelProp.setTimeVarying(true);
1416
- var t1 = new Time(); t1.ticks = String(__secondsToTicks(clipStart));
1417
- levelProp.addKey(t1); levelProp.setValueAtKey(t1, -96);
1418
- var t2 = new Time(); t2.ticks = String(__secondsToTicks(fadeEnd));
1419
- levelProp.addKey(t2); levelProp.setValueAtKey(t2, targetDb);
1420
- return __result({ trackIndex: trackIdx, clipIndex: clipIdx, clip: clip.name, fadeStart: clipStart, fadeEnd: fadeEnd, startDb: -96, endDb: targetDb });
1397
+ const script = buildToolScript(`
1398
+ var seq = app.project.activeSequence;
1399
+ if (!seq) return __error("No active sequence");
1400
+ var trackIdx = ${args.track_index};
1401
+ var clipIdx = ${args.clip_index};
1402
+ var fadeDuration = ${args.duration_seconds};
1403
+ var targetDb = ${targetDb};
1404
+ if (trackIdx >= seq.audioTracks.numTracks) return __error("Track index " + trackIdx + " out of range");
1405
+ var track = seq.audioTracks[trackIdx];
1406
+ if (clipIdx >= track.clips.numItems) return __error("Clip index " + clipIdx + " out of range");
1407
+ var clip = track.clips[clipIdx];
1408
+ var volComp = __findIntrinsic(clip, "volume");
1409
+ if (!volComp) return __error("Volume component not found on clip");
1410
+ var levelProp = __findProp(volComp, ["Level", "Nivel", "Pegel", "Volume"]);
1411
+ if (!levelProp) return __error("Volume Level property not found on clip");
1412
+ var clipStart = __ticksToSeconds(clip.start.ticks);
1413
+ var clipEnd = __ticksToSeconds(clip.end.ticks);
1414
+ var fadeEnd = Math.min(clipStart + fadeDuration, clipEnd);
1415
+ levelProp.setTimeVarying(true);
1416
+ var t1 = new Time(); t1.ticks = String(__secondsToTicks(clipStart));
1417
+ levelProp.addKey(t1); levelProp.setValueAtKey(t1, -96);
1418
+ var t2 = new Time(); t2.ticks = String(__secondsToTicks(fadeEnd));
1419
+ levelProp.addKey(t2); levelProp.setValueAtKey(t2, targetDb);
1420
+ return __result({ trackIndex: trackIdx, clipIndex: clipIdx, clip: clip.name, fadeStart: clipStart, fadeEnd: fadeEnd, startDb: -96, endDb: targetDb });
1421
1421
  `);
1422
1422
  return sendCommand(script, bridgeOptions);
1423
1423
  },
@@ -1436,30 +1436,30 @@ export function getAudioAdvancedTools(bridgeOptions) {
1436
1436
  },
1437
1437
  handler: async (args) => {
1438
1438
  const startDb = args.start_db ?? 0;
1439
- const script = buildToolScript(`
1440
- var seq = app.project.activeSequence;
1441
- if (!seq) return __error("No active sequence");
1442
- var trackIdx = ${args.track_index};
1443
- var clipIdx = ${args.clip_index};
1444
- var fadeDuration = ${args.duration_seconds};
1445
- var startDb = ${startDb};
1446
- if (trackIdx >= seq.audioTracks.numTracks) return __error("Track index " + trackIdx + " out of range");
1447
- var track = seq.audioTracks[trackIdx];
1448
- if (clipIdx >= track.clips.numItems) return __error("Clip index " + clipIdx + " out of range");
1449
- var clip = track.clips[clipIdx];
1450
- var volComp = __findIntrinsic(clip, "volume");
1451
- if (!volComp) return __error("Volume component not found on clip");
1452
- var levelProp = __findProp(volComp, ["Level", "Nivel", "Pegel", "Volume"]);
1453
- if (!levelProp) return __error("Volume Level property not found on clip");
1454
- var clipStart = __ticksToSeconds(clip.start.ticks);
1455
- var clipEnd = __ticksToSeconds(clip.end.ticks);
1456
- var fadeStart = Math.max(clipEnd - fadeDuration, clipStart);
1457
- levelProp.setTimeVarying(true);
1458
- var t1 = new Time(); t1.ticks = String(__secondsToTicks(fadeStart));
1459
- levelProp.addKey(t1); levelProp.setValueAtKey(t1, startDb);
1460
- var t2 = new Time(); t2.ticks = String(__secondsToTicks(clipEnd));
1461
- levelProp.addKey(t2); levelProp.setValueAtKey(t2, -96);
1462
- return __result({ trackIndex: trackIdx, clipIndex: clipIdx, clip: clip.name, fadeStart: fadeStart, fadeEnd: clipEnd, startDb: startDb, endDb: -96 });
1439
+ const script = buildToolScript(`
1440
+ var seq = app.project.activeSequence;
1441
+ if (!seq) return __error("No active sequence");
1442
+ var trackIdx = ${args.track_index};
1443
+ var clipIdx = ${args.clip_index};
1444
+ var fadeDuration = ${args.duration_seconds};
1445
+ var startDb = ${startDb};
1446
+ if (trackIdx >= seq.audioTracks.numTracks) return __error("Track index " + trackIdx + " out of range");
1447
+ var track = seq.audioTracks[trackIdx];
1448
+ if (clipIdx >= track.clips.numItems) return __error("Clip index " + clipIdx + " out of range");
1449
+ var clip = track.clips[clipIdx];
1450
+ var volComp = __findIntrinsic(clip, "volume");
1451
+ if (!volComp) return __error("Volume component not found on clip");
1452
+ var levelProp = __findProp(volComp, ["Level", "Nivel", "Pegel", "Volume"]);
1453
+ if (!levelProp) return __error("Volume Level property not found on clip");
1454
+ var clipStart = __ticksToSeconds(clip.start.ticks);
1455
+ var clipEnd = __ticksToSeconds(clip.end.ticks);
1456
+ var fadeStart = Math.max(clipEnd - fadeDuration, clipStart);
1457
+ levelProp.setTimeVarying(true);
1458
+ var t1 = new Time(); t1.ticks = String(__secondsToTicks(fadeStart));
1459
+ levelProp.addKey(t1); levelProp.setValueAtKey(t1, startDb);
1460
+ var t2 = new Time(); t2.ticks = String(__secondsToTicks(clipEnd));
1461
+ levelProp.addKey(t2); levelProp.setValueAtKey(t2, -96);
1462
+ return __result({ trackIndex: trackIdx, clipIndex: clipIdx, clip: clip.name, fadeStart: fadeStart, fadeEnd: clipEnd, startDb: startDb, endDb: -96 });
1463
1463
  `);
1464
1464
  return sendCommand(script, bridgeOptions);
1465
1465
  },
@@ -1476,50 +1476,50 @@ export function getAudioAdvancedTools(bridgeOptions) {
1476
1476
  required: ["track_index", "clip_index", "duration_seconds"],
1477
1477
  },
1478
1478
  handler: async (args) => {
1479
- const script = buildToolScript(`
1480
- var seq = app.project.activeSequence;
1481
- if (!seq) return __error("No active sequence");
1482
- var trackIdx = ${args.track_index};
1483
- var clipIdx = ${args.clip_index};
1484
- var totalDuration = ${args.duration_seconds};
1485
- var halfDuration = totalDuration / 2;
1486
- if (trackIdx >= seq.audioTracks.numTracks) return __error("Track index " + trackIdx + " out of range");
1487
- var track = seq.audioTracks[trackIdx];
1488
- if (clipIdx >= track.clips.numItems - 1) return __error("Clip index " + clipIdx + " must have a next clip for crossfade");
1489
- var clip1 = track.clips[clipIdx];
1490
- var clip2 = track.clips[clipIdx + 1];
1491
- var getLevel = function(clip) {
1492
- for (var i = 0; i < clip.components.numItems; i++) {
1493
- var comp = clip.components[i];
1494
- if (comp.matchName === "audioVolume" || comp.displayName === "Volume") {
1495
- for (var p = 0; p < comp.properties.numItems; p++) {
1496
- if (comp.properties[p].displayName === "Level") return comp.properties[p];
1497
- }
1498
- }
1499
- }
1500
- return null;
1501
- };
1502
- var level1 = getLevel(clip1);
1503
- var level2 = getLevel(clip2);
1504
- if (!level1) return __error("Could not find Volume Level on clip " + clipIdx);
1505
- if (!level2) return __error("Could not find Volume Level on clip " + (clipIdx + 1));
1506
- var clip1End = __ticksToSeconds(clip1.end.ticks);
1507
- var clip1Start = __ticksToSeconds(clip1.start.ticks);
1508
- var clip2Start = __ticksToSeconds(clip2.start.ticks);
1509
- var clip2End = __ticksToSeconds(clip2.end.ticks);
1510
- var fadeOutStart = Math.max(clip1Start, clip1End - halfDuration);
1511
- level1.setTimeVarying(true);
1512
- var t1a = new Time(); t1a.ticks = String(__secondsToTicks(fadeOutStart));
1513
- level1.addKey(t1a); level1.setValueAtKey(t1a, 0);
1514
- var t1b = new Time(); t1b.ticks = String(__secondsToTicks(clip1End));
1515
- level1.addKey(t1b); level1.setValueAtKey(t1b, -96);
1516
- var fadeInEnd = Math.min(clip2End, clip2Start + halfDuration);
1517
- level2.setTimeVarying(true);
1518
- var t2a = new Time(); t2a.ticks = String(__secondsToTicks(clip2Start));
1519
- level2.addKey(t2a); level2.setValueAtKey(t2a, -96);
1520
- var t2b = new Time(); t2b.ticks = String(__secondsToTicks(fadeInEnd));
1521
- level2.addKey(t2b); level2.setValueAtKey(t2b, 0);
1522
- return __result({ trackIndex: trackIdx, clip1Index: clipIdx, clip2Index: clipIdx + 1, clip1: clip1.name, clip2: clip2.name, fadeOutStart: fadeOutStart, clip1End: clip1End, clip2Start: clip2Start, fadeInEnd: fadeInEnd });
1479
+ const script = buildToolScript(`
1480
+ var seq = app.project.activeSequence;
1481
+ if (!seq) return __error("No active sequence");
1482
+ var trackIdx = ${args.track_index};
1483
+ var clipIdx = ${args.clip_index};
1484
+ var totalDuration = ${args.duration_seconds};
1485
+ var halfDuration = totalDuration / 2;
1486
+ if (trackIdx >= seq.audioTracks.numTracks) return __error("Track index " + trackIdx + " out of range");
1487
+ var track = seq.audioTracks[trackIdx];
1488
+ if (clipIdx >= track.clips.numItems - 1) return __error("Clip index " + clipIdx + " must have a next clip for crossfade");
1489
+ var clip1 = track.clips[clipIdx];
1490
+ var clip2 = track.clips[clipIdx + 1];
1491
+ var getLevel = function(clip) {
1492
+ for (var i = 0; i < clip.components.numItems; i++) {
1493
+ var comp = clip.components[i];
1494
+ if (comp.matchName === "audioVolume" || comp.displayName === "Volume") {
1495
+ for (var p = 0; p < comp.properties.numItems; p++) {
1496
+ if (comp.properties[p].displayName === "Level") return comp.properties[p];
1497
+ }
1498
+ }
1499
+ }
1500
+ return null;
1501
+ };
1502
+ var level1 = getLevel(clip1);
1503
+ var level2 = getLevel(clip2);
1504
+ if (!level1) return __error("Could not find Volume Level on clip " + clipIdx);
1505
+ if (!level2) return __error("Could not find Volume Level on clip " + (clipIdx + 1));
1506
+ var clip1End = __ticksToSeconds(clip1.end.ticks);
1507
+ var clip1Start = __ticksToSeconds(clip1.start.ticks);
1508
+ var clip2Start = __ticksToSeconds(clip2.start.ticks);
1509
+ var clip2End = __ticksToSeconds(clip2.end.ticks);
1510
+ var fadeOutStart = Math.max(clip1Start, clip1End - halfDuration);
1511
+ level1.setTimeVarying(true);
1512
+ var t1a = new Time(); t1a.ticks = String(__secondsToTicks(fadeOutStart));
1513
+ level1.addKey(t1a); level1.setValueAtKey(t1a, 0);
1514
+ var t1b = new Time(); t1b.ticks = String(__secondsToTicks(clip1End));
1515
+ level1.addKey(t1b); level1.setValueAtKey(t1b, -96);
1516
+ var fadeInEnd = Math.min(clip2End, clip2Start + halfDuration);
1517
+ level2.setTimeVarying(true);
1518
+ var t2a = new Time(); t2a.ticks = String(__secondsToTicks(clip2Start));
1519
+ level2.addKey(t2a); level2.setValueAtKey(t2a, -96);
1520
+ var t2b = new Time(); t2b.ticks = String(__secondsToTicks(fadeInEnd));
1521
+ level2.addKey(t2b); level2.setValueAtKey(t2b, 0);
1522
+ return __result({ trackIndex: trackIdx, clip1Index: clipIdx, clip2Index: clipIdx + 1, clip1: clip1.name, clip2: clip2.name, fadeOutStart: fadeOutStart, clip1End: clip1End, clip2Start: clip2Start, fadeInEnd: fadeInEnd });
1523
1523
  `);
1524
1524
  return sendCommand(script, bridgeOptions);
1525
1525
  },
@@ -1534,70 +1534,70 @@ export function getAudioAdvancedTools(bridgeOptions) {
1534
1534
  required: ["track_index"],
1535
1535
  },
1536
1536
  handler: async (args) => {
1537
- const script = buildToolScript(`
1538
- var seq = app.project.activeSequence;
1539
- if (!seq) return __error("No active sequence");
1540
- var trackIdx = ${args.track_index};
1541
- if (trackIdx >= seq.audioTracks.numTracks) return __error("Track index " + trackIdx + " out of range");
1542
- var track = seq.audioTracks[trackIdx];
1543
- var trackLevel = 0;
1544
- var trackPanning = 0;
1545
- if (track.components) {
1546
- for (var ci = 0; ci < track.components.numItems; ci++) {
1547
- var comp = track.components[ci];
1548
- if (comp.matchName === "audioVolume" || comp.displayName === "Volume") {
1549
- for (var pi = 0; pi < comp.properties.numItems; pi++) {
1550
- if (comp.properties[pi].displayName === "Level") {
1551
- try { trackLevel = comp.properties[pi].getValue(); } catch(e) {}
1552
- break;
1553
- }
1554
- }
1555
- }
1556
- if (comp.matchName === "audioPanner" || comp.displayName === "Panner") {
1557
- for (var pi2 = 0; pi2 < comp.properties.numItems; pi2++) {
1558
- var prop2 = comp.properties[pi2];
1559
- if (prop2.displayName === "Balance" || prop2.displayName === "Panner" || prop2.displayName === "Left/Right Balance") {
1560
- try { trackPanning = prop2.getValue(); } catch(e2) {}
1561
- break;
1562
- }
1563
- }
1564
- }
1565
- }
1566
- }
1567
- var clips = [];
1568
- for (var c = 0; c < track.clips.numItems; c++) {
1569
- var clip = track.clips[c];
1570
- var clipLevel = 0;
1571
- for (var ci2 = 0; ci2 < clip.components.numItems; ci2++) {
1572
- var comp2 = clip.components[ci2];
1573
- if (comp2.matchName === "audioVolume" || comp2.displayName === "Volume") {
1574
- for (var pi3 = 0; pi3 < comp2.properties.numItems; pi3++) {
1575
- if (comp2.properties[pi3].displayName === "Level") {
1576
- try { clipLevel = comp2.properties[pi3].getValue(); } catch(e3) {}
1577
- break;
1578
- }
1579
- }
1580
- break;
1581
- }
1582
- }
1583
- clips.push({
1584
- index: c,
1585
- nodeId: clip.nodeId,
1586
- name: clip.name,
1587
- start: __ticksToSeconds(clip.start.ticks),
1588
- end: __ticksToSeconds(clip.end.ticks),
1589
- level: clipLevel
1590
- });
1591
- }
1592
- return __result({
1593
- index: trackIdx,
1594
- name: track.name || ("Audio " + (trackIdx + 1)),
1595
- numClips: track.clips.numItems,
1596
- isMuted: __readMaybe(track, "isMuted", false),
1597
- level: trackLevel,
1598
- panning: trackPanning,
1599
- clips: clips
1600
- });
1537
+ const script = buildToolScript(`
1538
+ var seq = app.project.activeSequence;
1539
+ if (!seq) return __error("No active sequence");
1540
+ var trackIdx = ${args.track_index};
1541
+ if (trackIdx >= seq.audioTracks.numTracks) return __error("Track index " + trackIdx + " out of range");
1542
+ var track = seq.audioTracks[trackIdx];
1543
+ var trackLevel = 0;
1544
+ var trackPanning = 0;
1545
+ if (track.components) {
1546
+ for (var ci = 0; ci < track.components.numItems; ci++) {
1547
+ var comp = track.components[ci];
1548
+ if (comp.matchName === "audioVolume" || comp.displayName === "Volume") {
1549
+ for (var pi = 0; pi < comp.properties.numItems; pi++) {
1550
+ if (comp.properties[pi].displayName === "Level") {
1551
+ try { trackLevel = comp.properties[pi].getValue(); } catch(e) {}
1552
+ break;
1553
+ }
1554
+ }
1555
+ }
1556
+ if (comp.matchName === "audioPanner" || comp.displayName === "Panner") {
1557
+ for (var pi2 = 0; pi2 < comp.properties.numItems; pi2++) {
1558
+ var prop2 = comp.properties[pi2];
1559
+ if (prop2.displayName === "Balance" || prop2.displayName === "Panner" || prop2.displayName === "Left/Right Balance") {
1560
+ try { trackPanning = prop2.getValue(); } catch(e2) {}
1561
+ break;
1562
+ }
1563
+ }
1564
+ }
1565
+ }
1566
+ }
1567
+ var clips = [];
1568
+ for (var c = 0; c < track.clips.numItems; c++) {
1569
+ var clip = track.clips[c];
1570
+ var clipLevel = 0;
1571
+ for (var ci2 = 0; ci2 < clip.components.numItems; ci2++) {
1572
+ var comp2 = clip.components[ci2];
1573
+ if (comp2.matchName === "audioVolume" || comp2.displayName === "Volume") {
1574
+ for (var pi3 = 0; pi3 < comp2.properties.numItems; pi3++) {
1575
+ if (comp2.properties[pi3].displayName === "Level") {
1576
+ try { clipLevel = comp2.properties[pi3].getValue(); } catch(e3) {}
1577
+ break;
1578
+ }
1579
+ }
1580
+ break;
1581
+ }
1582
+ }
1583
+ clips.push({
1584
+ index: c,
1585
+ nodeId: clip.nodeId,
1586
+ name: clip.name,
1587
+ start: __ticksToSeconds(clip.start.ticks),
1588
+ end: __ticksToSeconds(clip.end.ticks),
1589
+ level: clipLevel
1590
+ });
1591
+ }
1592
+ return __result({
1593
+ index: trackIdx,
1594
+ name: track.name || ("Audio " + (trackIdx + 1)),
1595
+ numClips: track.clips.numItems,
1596
+ isMuted: __readMaybe(track, "isMuted", false),
1597
+ level: trackLevel,
1598
+ panning: trackPanning,
1599
+ clips: clips
1600
+ });
1601
1601
  `);
1602
1602
  return sendCommand(script, bridgeOptions);
1603
1603
  },
@@ -1614,36 +1614,36 @@ export function getAudioAdvancedTools(bridgeOptions) {
1614
1614
  },
1615
1615
  handler: async (args) => {
1616
1616
  const nodeIdEsc = args.node_id.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
1617
- const script = buildToolScript(`
1618
- var seq = app.project.activeSequence;
1619
- if (!seq) return __error("No active sequence");
1620
- var nodeId = "${nodeIdEsc}";
1621
- var levelDb = ${args.level_db};
1622
- var foundClip = null;
1623
- var foundTrack = -1;
1624
- var foundClipIdx = -1;
1625
- for (var t = 0; t < seq.audioTracks.numTracks; t++) {
1626
- var track = seq.audioTracks[t];
1627
- for (var c = 0; c < track.clips.numItems; c++) {
1628
- var clip = track.clips[c];
1629
- if (clip.nodeId === nodeId) {
1630
- foundClip = clip;
1631
- foundTrack = t;
1632
- foundClipIdx = c;
1633
- break;
1634
- }
1635
- }
1636
- if (foundClip) break;
1637
- }
1638
- if (!foundClip) return __error("Clip with nodeId '" + nodeId + "' not found in any audio track");
1639
- // Locale-resilient: __findIntrinsic uses matchName candidates first
1640
- // (incl. AE.ADBE Audio Levels for PP 2026), then localized displayName.
1641
- var volComp = __findIntrinsic(foundClip, "volume");
1642
- if (!volComp) return __error("Volume component not found on clip");
1643
- var levelProp = __findProp(volComp, ["Level", "Nivel", "Volume", "Volumen", "Pegel"]);
1644
- if (!levelProp) return __error("Volume Level property not found in Volume component");
1645
- levelProp.setValue(levelDb, true);
1646
- return __result({ nodeId: nodeId, clip: foundClip.name, trackIndex: foundTrack, clipIndex: foundClipIdx, levelDb: levelDb });
1617
+ const script = buildToolScript(`
1618
+ var seq = app.project.activeSequence;
1619
+ if (!seq) return __error("No active sequence");
1620
+ var nodeId = "${nodeIdEsc}";
1621
+ var levelDb = ${args.level_db};
1622
+ var foundClip = null;
1623
+ var foundTrack = -1;
1624
+ var foundClipIdx = -1;
1625
+ for (var t = 0; t < seq.audioTracks.numTracks; t++) {
1626
+ var track = seq.audioTracks[t];
1627
+ for (var c = 0; c < track.clips.numItems; c++) {
1628
+ var clip = track.clips[c];
1629
+ if (clip.nodeId === nodeId) {
1630
+ foundClip = clip;
1631
+ foundTrack = t;
1632
+ foundClipIdx = c;
1633
+ break;
1634
+ }
1635
+ }
1636
+ if (foundClip) break;
1637
+ }
1638
+ if (!foundClip) return __error("Clip with nodeId '" + nodeId + "' not found in any audio track");
1639
+ // Locale-resilient: __findIntrinsic uses matchName candidates first
1640
+ // (incl. AE.ADBE Audio Levels for PP 2026), then localized displayName.
1641
+ var volComp = __findIntrinsic(foundClip, "volume");
1642
+ if (!volComp) return __error("Volume component not found on clip");
1643
+ var levelProp = __findProp(volComp, ["Level", "Nivel", "Volume", "Volumen", "Pegel"]);
1644
+ if (!levelProp) return __error("Volume Level property not found in Volume component");
1645
+ levelProp.setValue(levelDb, true);
1646
+ return __result({ nodeId: nodeId, clip: foundClip.name, trackIndex: foundTrack, clipIndex: foundClipIdx, levelDb: levelDb });
1647
1647
  `);
1648
1648
  return sendCommand(script, bridgeOptions);
1649
1649
  },