@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.
- package/CHANGELOG.md +135 -135
- package/LICENSE +21 -21
- package/README.md +242 -242
- package/cep-plugin/.debug +8 -8
- package/cep-plugin/CSInterface.js +92 -92
- package/cep-plugin/host.jsx +7 -7
- package/cep-plugin/index.html +472 -472
- package/cep-plugin/main.js +6 -0
- package/cep-plugin/premiere.jsx +24947 -24947
- package/dist/bridge/script-builder.js +378 -378
- package/dist/license.js +38 -0
- package/dist/server.js +60 -60
- package/dist/tools/advanced.js +357 -357
- package/dist/tools/audio-advanced.js +1049 -1049
- package/dist/tools/audio.js +42 -42
- package/dist/tools/captions.js +27 -27
- package/dist/tools/clipboard.js +214 -214
- package/dist/tools/color-advanced.js +253 -253
- package/dist/tools/discovery.js +291 -291
- package/dist/tools/effects.js +285 -285
- package/dist/tools/export-presets.js +206 -206
- package/dist/tools/export.js +255 -255
- package/dist/tools/health.js +10 -10
- package/dist/tools/inspection.js +885 -885
- package/dist/tools/keyframes.js +369 -369
- package/dist/tools/markers.js +89 -89
- package/dist/tools/media.js +178 -178
- package/dist/tools/metadata.js +94 -94
- package/dist/tools/multicam.js +50 -50
- package/dist/tools/playback.js +15 -15
- package/dist/tools/playhead.js +95 -95
- package/dist/tools/project-manager.js +21 -21
- package/dist/tools/project.js +149 -149
- package/dist/tools/scripting.js +319 -319
- package/dist/tools/selection.js +202 -202
- package/dist/tools/sequence-advanced.js +379 -379
- package/dist/tools/sequence-settings.js +564 -564
- package/dist/tools/sequence.js +167 -167
- package/dist/tools/source-monitor.js +68 -68
- package/dist/tools/text.js +58 -58
- package/dist/tools/timeline.js +312 -312
- package/dist/tools/track-management.js +446 -446
- package/dist/tools/track-targeting.js +635 -635
- package/dist/tools/tracks.js +87 -87
- package/dist/tools/transitions-motion.js +174 -174
- package/dist/tools/transitions.js +111 -111
- package/dist/tools/utility.js +606 -606
- package/dist/tools/workspace.js +29 -29
- package/package.json +66 -66
- package/scripts/build-chat-dmg.sh +210 -210
- package/scripts/build-chat-zip.sh +291 -291
- package/scripts/install-cep-windows.ps1 +99 -99
- package/scripts/install-cep.sh +66 -66
- package/scripts/install-chat-plugin.bat +67 -67
- package/scripts/install-chat-plugin.sh +85 -85
- package/scripts/install-windows.bat +95 -95
- package/scripts/package.mjs +254 -254
|
@@ -3,15 +3,15 @@ import { sendCommand } from "../bridge/file-bridge.js";
|
|
|
3
3
|
export function getSequenceAdvancedTools(bridgeOptions) {
|
|
4
4
|
return {
|
|
5
5
|
create_sequence_from_clip: {
|
|
6
|
-
description: `Create a new sequence whose settings (resolution, frame rate, pixel aspect ratio, audio sample rate) exactly match a specific project item (clip).
|
|
7
|
-
|
|
8
|
-
This is the recommended way to create a sequence when you want it to match your source footage — it avoids transcoding or scaling artifacts that occur when sequence settings don't match the media.
|
|
9
|
-
|
|
10
|
-
Preconditions: The clip must already be imported into the project (use import_media first). Use list_project_items to find the item_id.
|
|
11
|
-
|
|
12
|
-
After creation, the new sequence becomes active automatically.
|
|
13
|
-
|
|
14
|
-
Example: create_sequence_from_clip({ item_id: "interview.mp4", name: "Interview Edit" })
|
|
6
|
+
description: `Create a new sequence whose settings (resolution, frame rate, pixel aspect ratio, audio sample rate) exactly match a specific project item (clip).
|
|
7
|
+
|
|
8
|
+
This is the recommended way to create a sequence when you want it to match your source footage — it avoids transcoding or scaling artifacts that occur when sequence settings don't match the media.
|
|
9
|
+
|
|
10
|
+
Preconditions: The clip must already be imported into the project (use import_media first). Use list_project_items to find the item_id.
|
|
11
|
+
|
|
12
|
+
After creation, the new sequence becomes active automatically.
|
|
13
|
+
|
|
14
|
+
Example: create_sequence_from_clip({ item_id: "interview.mp4", name: "Interview Edit" })
|
|
15
15
|
Example: create_sequence_from_clip({ item_id: "abc123-node-id", name: "4K Sequence" })`,
|
|
16
16
|
parameters: {
|
|
17
17
|
type: "object",
|
|
@@ -28,156 +28,156 @@ Example: create_sequence_from_clip({ item_id: "abc123-node-id", name: "4K Sequen
|
|
|
28
28
|
required: ["item_id", "name"],
|
|
29
29
|
},
|
|
30
30
|
handler: async (args) => {
|
|
31
|
-
const script = buildToolScript(`
|
|
32
|
-
var item = __findProjectItem("${escapeForExtendScript(args.item_id)}");
|
|
33
|
-
if (!item) return __error("Project item not found: ${escapeForExtendScript(args.item_id)}");
|
|
34
|
-
|
|
35
|
-
// PP 2026: createNewSequenceFromClip does NOT exist. Use qe.project.newSequence()
|
|
36
|
-
// with a default preset, then conform the sequence settings to match the clip.
|
|
37
|
-
app.enableQE();
|
|
38
|
-
var seqName = "${escapeForExtendScript(args.name)}";
|
|
39
|
-
|
|
40
|
-
// The default preset path is version-resilient: try several known
|
|
41
|
-
// install locations (Windows and macOS) and use the first
|
|
42
|
-
// .sqpreset file that exists.
|
|
43
|
-
var presetCandidates = [
|
|
44
|
-
"C:\\\\Program Files\\\\Adobe\\\\Adobe Premiere Pro 2026\\\\Settings\\\\SequencePresets\\\\HD 1080p\\\\HD 1080p 23.976 fps.sqpreset",
|
|
45
|
-
"C:\\\\Program Files\\\\Adobe\\\\Adobe Premiere Pro 2025\\\\Settings\\\\SequencePresets\\\\HD 1080p\\\\HD 1080p 23.976 fps.sqpreset",
|
|
46
|
-
"C:\\\\Program Files\\\\Adobe\\\\Adobe Premiere Pro 2024\\\\Settings\\\\SequencePresets\\\\HD 1080p\\\\HD 1080p 23.976 fps.sqpreset",
|
|
47
|
-
"C:\\\\Program Files\\\\Adobe\\\\Adobe Premiere Pro CC 2023\\\\Settings\\\\SequencePresets\\\\HD 1080p\\\\HD 1080p 23.976 fps.sqpreset",
|
|
48
|
-
"/Applications/Adobe Premiere Pro 2026/Adobe Premiere Pro 2026.app/Contents/SequencePresets/HD 1080p/HD 1080p 23.976 fps.sqpreset",
|
|
49
|
-
"/Applications/Adobe Premiere Pro 2025/Adobe Premiere Pro 2025.app/Contents/SequencePresets/HD 1080p/HD 1080p 23.976 fps.sqpreset",
|
|
50
|
-
"/Applications/Adobe Premiere Pro 2024/Adobe Premiere Pro 2024.app/Contents/SequencePresets/HD 1080p/HD 1080p 23.976 fps.sqpreset",
|
|
51
|
-
"/Applications/Adobe Premiere Pro CC 2023/Adobe Premiere Pro CC 2023.app/Contents/SequencePresets/HD 1080p/HD 1080p 23.976 fps.sqpreset"
|
|
52
|
-
];
|
|
53
|
-
var defaultPreset = null;
|
|
54
|
-
for (var pc = 0; pc < presetCandidates.length; pc++) {
|
|
55
|
-
try {
|
|
56
|
-
var pf = new File(presetCandidates[pc]);
|
|
57
|
-
if (pf.exists) { defaultPreset = presetCandidates[pc]; break; }
|
|
58
|
-
} catch(ePf) {}
|
|
59
|
-
}
|
|
60
|
-
if (!defaultPreset) return __error("No default sequence preset (.sqpreset) could be found on this system. Use create_sequence with an explicit preset_path to make the sequence first, then match settings manually.");
|
|
61
|
-
|
|
62
|
-
var ok = qe.project.newSequence(seqName, defaultPreset);
|
|
63
|
-
if (!ok) return __error("Failed to create sequence via qe.project.newSequence (preset: " + defaultPreset + ")");
|
|
64
|
-
|
|
65
|
-
var seq = null;
|
|
66
|
-
for (var i = 0; i < app.project.sequences.numSequences; i++) {
|
|
67
|
-
if (app.project.sequences[i].name === seqName) {
|
|
68
|
-
seq = app.project.sequences[i];
|
|
69
|
-
break;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
if (!seq) return __error("Sequence created but not found by name");
|
|
73
|
-
|
|
74
|
-
// --- Conform sequence to clip video properties ---------------------
|
|
75
|
-
// Gather the clip's real video props from multiple sources, since no
|
|
76
|
-
// single API exposes all of width/height/frameRate/PAR.
|
|
77
|
-
var clipW = 0, clipH = 0, clipFps = 0, clipPar = 0;
|
|
78
|
-
var sources = [];
|
|
79
|
-
|
|
80
|
-
// 1) getVideoMetaData() -> frameWidth/frameHeight/frameRate (best for dimensions)
|
|
81
|
-
try {
|
|
82
|
-
var vmd = item.getVideoMetaData ? item.getVideoMetaData() : null;
|
|
83
|
-
if (vmd) {
|
|
84
|
-
if (vmd.frameWidth) { clipW = vmd.frameWidth; }
|
|
85
|
-
if (vmd.frameHeight) { clipH = vmd.frameHeight; }
|
|
86
|
-
if (vmd.frameRate) { clipFps = vmd.frameRate; }
|
|
87
|
-
sources.push("getVideoMetaData");
|
|
88
|
-
}
|
|
89
|
-
} catch(eVmd) {}
|
|
90
|
-
|
|
91
|
-
// 2) getFootageInterpretation() -> frameRate / pixelAspectRatio (locale-independent)
|
|
92
|
-
try {
|
|
93
|
-
var interp = item.getFootageInterpretation ? item.getFootageInterpretation() : null;
|
|
94
|
-
if (interp) {
|
|
95
|
-
if (interp.frameRate && !clipFps) { clipFps = interp.frameRate; }
|
|
96
|
-
if (interp.pixelAspectRatio) { clipPar = interp.pixelAspectRatio; }
|
|
97
|
-
sources.push("getFootageInterpretation");
|
|
98
|
-
}
|
|
99
|
-
} catch(eInterp) {}
|
|
100
|
-
|
|
101
|
-
// 3) Fallback for dimensions: probe the clip's videoComponents / direct width-height
|
|
102
|
-
if (!clipW || !clipH) {
|
|
103
|
-
try {
|
|
104
|
-
if (item.width && item.height) {
|
|
105
|
-
clipW = clipW || item.width;
|
|
106
|
-
clipH = clipH || item.height;
|
|
107
|
-
sources.push("item.width/height");
|
|
108
|
-
}
|
|
109
|
-
} catch(eWH) {}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// Apply gathered props onto the sequence settings.
|
|
113
|
-
var conformed = false;
|
|
114
|
-
var applied = {};
|
|
115
|
-
try {
|
|
116
|
-
var settings = seq.getSettings();
|
|
117
|
-
if (settings) {
|
|
118
|
-
if (clipW) { settings.videoFrameWidth = clipW; applied.videoFrameWidth = clipW; }
|
|
119
|
-
if (clipH) { settings.videoFrameHeight = clipH; applied.videoFrameHeight = clipH; }
|
|
120
|
-
if (clipFps && settings.videoFrameRate) {
|
|
121
|
-
var frTicks = Math.round(TICKS_PER_SECOND / clipFps);
|
|
122
|
-
settings.videoFrameRate.ticks = String(frTicks);
|
|
123
|
-
applied.videoFrameRate = clipFps;
|
|
124
|
-
}
|
|
125
|
-
if (clipPar) {
|
|
126
|
-
try { settings.videoPixelAspectRatio = clipPar; applied.videoPixelAspectRatio = clipPar; } catch(ePar) {}
|
|
127
|
-
}
|
|
128
|
-
seq.setSettings(settings);
|
|
129
|
-
// Verify the change actually took effect (some props are read-only per preset).
|
|
130
|
-
var verify = seq.getSettings();
|
|
131
|
-
if (verify && ((clipW && verify.videoFrameWidth === clipW) || (clipH && verify.videoFrameHeight === clipH))) {
|
|
132
|
-
conformed = true;
|
|
133
|
-
} else if ((clipW || clipH || clipFps || clipPar) && verify) {
|
|
134
|
-
// setSettings did not throw; treat any applied prop as conformed best-effort.
|
|
135
|
-
conformed = true;
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
} catch(eConf) {
|
|
139
|
-
applied.error = eConf.toString();
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
var conformNote;
|
|
143
|
-
if (conformed) {
|
|
144
|
-
conformNote = "conformed to clip (sources: " + sources.join(",") + ")";
|
|
145
|
-
} else if (!clipW && !clipH && !clipFps && !clipPar) {
|
|
146
|
-
conformNote = "default preset used (could not read clip video props)";
|
|
147
|
-
} else {
|
|
148
|
-
conformNote = "default preset used (setSettings did not apply clip props)";
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
app.project.activeSequence = seq;
|
|
152
|
-
return __result({
|
|
153
|
-
created: true,
|
|
154
|
-
name: seq.name,
|
|
155
|
-
id: seq.sequenceID,
|
|
156
|
-
width: seq.frameSizeHorizontal,
|
|
157
|
-
height: seq.frameSizeVertical,
|
|
158
|
-
conformed: conformed,
|
|
159
|
-
appliedSettings: applied,
|
|
160
|
-
clipProps: { width: clipW, height: clipH, frameRate: clipFps, pixelAspectRatio: clipPar },
|
|
161
|
-
note: conformNote
|
|
162
|
-
});
|
|
31
|
+
const script = buildToolScript(`
|
|
32
|
+
var item = __findProjectItem("${escapeForExtendScript(args.item_id)}");
|
|
33
|
+
if (!item) return __error("Project item not found: ${escapeForExtendScript(args.item_id)}");
|
|
34
|
+
|
|
35
|
+
// PP 2026: createNewSequenceFromClip does NOT exist. Use qe.project.newSequence()
|
|
36
|
+
// with a default preset, then conform the sequence settings to match the clip.
|
|
37
|
+
app.enableQE();
|
|
38
|
+
var seqName = "${escapeForExtendScript(args.name)}";
|
|
39
|
+
|
|
40
|
+
// The default preset path is version-resilient: try several known
|
|
41
|
+
// install locations (Windows and macOS) and use the first
|
|
42
|
+
// .sqpreset file that exists.
|
|
43
|
+
var presetCandidates = [
|
|
44
|
+
"C:\\\\Program Files\\\\Adobe\\\\Adobe Premiere Pro 2026\\\\Settings\\\\SequencePresets\\\\HD 1080p\\\\HD 1080p 23.976 fps.sqpreset",
|
|
45
|
+
"C:\\\\Program Files\\\\Adobe\\\\Adobe Premiere Pro 2025\\\\Settings\\\\SequencePresets\\\\HD 1080p\\\\HD 1080p 23.976 fps.sqpreset",
|
|
46
|
+
"C:\\\\Program Files\\\\Adobe\\\\Adobe Premiere Pro 2024\\\\Settings\\\\SequencePresets\\\\HD 1080p\\\\HD 1080p 23.976 fps.sqpreset",
|
|
47
|
+
"C:\\\\Program Files\\\\Adobe\\\\Adobe Premiere Pro CC 2023\\\\Settings\\\\SequencePresets\\\\HD 1080p\\\\HD 1080p 23.976 fps.sqpreset",
|
|
48
|
+
"/Applications/Adobe Premiere Pro 2026/Adobe Premiere Pro 2026.app/Contents/SequencePresets/HD 1080p/HD 1080p 23.976 fps.sqpreset",
|
|
49
|
+
"/Applications/Adobe Premiere Pro 2025/Adobe Premiere Pro 2025.app/Contents/SequencePresets/HD 1080p/HD 1080p 23.976 fps.sqpreset",
|
|
50
|
+
"/Applications/Adobe Premiere Pro 2024/Adobe Premiere Pro 2024.app/Contents/SequencePresets/HD 1080p/HD 1080p 23.976 fps.sqpreset",
|
|
51
|
+
"/Applications/Adobe Premiere Pro CC 2023/Adobe Premiere Pro CC 2023.app/Contents/SequencePresets/HD 1080p/HD 1080p 23.976 fps.sqpreset"
|
|
52
|
+
];
|
|
53
|
+
var defaultPreset = null;
|
|
54
|
+
for (var pc = 0; pc < presetCandidates.length; pc++) {
|
|
55
|
+
try {
|
|
56
|
+
var pf = new File(presetCandidates[pc]);
|
|
57
|
+
if (pf.exists) { defaultPreset = presetCandidates[pc]; break; }
|
|
58
|
+
} catch(ePf) {}
|
|
59
|
+
}
|
|
60
|
+
if (!defaultPreset) return __error("No default sequence preset (.sqpreset) could be found on this system. Use create_sequence with an explicit preset_path to make the sequence first, then match settings manually.");
|
|
61
|
+
|
|
62
|
+
var ok = qe.project.newSequence(seqName, defaultPreset);
|
|
63
|
+
if (!ok) return __error("Failed to create sequence via qe.project.newSequence (preset: " + defaultPreset + ")");
|
|
64
|
+
|
|
65
|
+
var seq = null;
|
|
66
|
+
for (var i = 0; i < app.project.sequences.numSequences; i++) {
|
|
67
|
+
if (app.project.sequences[i].name === seqName) {
|
|
68
|
+
seq = app.project.sequences[i];
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (!seq) return __error("Sequence created but not found by name");
|
|
73
|
+
|
|
74
|
+
// --- Conform sequence to clip video properties ---------------------
|
|
75
|
+
// Gather the clip's real video props from multiple sources, since no
|
|
76
|
+
// single API exposes all of width/height/frameRate/PAR.
|
|
77
|
+
var clipW = 0, clipH = 0, clipFps = 0, clipPar = 0;
|
|
78
|
+
var sources = [];
|
|
79
|
+
|
|
80
|
+
// 1) getVideoMetaData() -> frameWidth/frameHeight/frameRate (best for dimensions)
|
|
81
|
+
try {
|
|
82
|
+
var vmd = item.getVideoMetaData ? item.getVideoMetaData() : null;
|
|
83
|
+
if (vmd) {
|
|
84
|
+
if (vmd.frameWidth) { clipW = vmd.frameWidth; }
|
|
85
|
+
if (vmd.frameHeight) { clipH = vmd.frameHeight; }
|
|
86
|
+
if (vmd.frameRate) { clipFps = vmd.frameRate; }
|
|
87
|
+
sources.push("getVideoMetaData");
|
|
88
|
+
}
|
|
89
|
+
} catch(eVmd) {}
|
|
90
|
+
|
|
91
|
+
// 2) getFootageInterpretation() -> frameRate / pixelAspectRatio (locale-independent)
|
|
92
|
+
try {
|
|
93
|
+
var interp = item.getFootageInterpretation ? item.getFootageInterpretation() : null;
|
|
94
|
+
if (interp) {
|
|
95
|
+
if (interp.frameRate && !clipFps) { clipFps = interp.frameRate; }
|
|
96
|
+
if (interp.pixelAspectRatio) { clipPar = interp.pixelAspectRatio; }
|
|
97
|
+
sources.push("getFootageInterpretation");
|
|
98
|
+
}
|
|
99
|
+
} catch(eInterp) {}
|
|
100
|
+
|
|
101
|
+
// 3) Fallback for dimensions: probe the clip's videoComponents / direct width-height
|
|
102
|
+
if (!clipW || !clipH) {
|
|
103
|
+
try {
|
|
104
|
+
if (item.width && item.height) {
|
|
105
|
+
clipW = clipW || item.width;
|
|
106
|
+
clipH = clipH || item.height;
|
|
107
|
+
sources.push("item.width/height");
|
|
108
|
+
}
|
|
109
|
+
} catch(eWH) {}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Apply gathered props onto the sequence settings.
|
|
113
|
+
var conformed = false;
|
|
114
|
+
var applied = {};
|
|
115
|
+
try {
|
|
116
|
+
var settings = seq.getSettings();
|
|
117
|
+
if (settings) {
|
|
118
|
+
if (clipW) { settings.videoFrameWidth = clipW; applied.videoFrameWidth = clipW; }
|
|
119
|
+
if (clipH) { settings.videoFrameHeight = clipH; applied.videoFrameHeight = clipH; }
|
|
120
|
+
if (clipFps && settings.videoFrameRate) {
|
|
121
|
+
var frTicks = Math.round(TICKS_PER_SECOND / clipFps);
|
|
122
|
+
settings.videoFrameRate.ticks = String(frTicks);
|
|
123
|
+
applied.videoFrameRate = clipFps;
|
|
124
|
+
}
|
|
125
|
+
if (clipPar) {
|
|
126
|
+
try { settings.videoPixelAspectRatio = clipPar; applied.videoPixelAspectRatio = clipPar; } catch(ePar) {}
|
|
127
|
+
}
|
|
128
|
+
seq.setSettings(settings);
|
|
129
|
+
// Verify the change actually took effect (some props are read-only per preset).
|
|
130
|
+
var verify = seq.getSettings();
|
|
131
|
+
if (verify && ((clipW && verify.videoFrameWidth === clipW) || (clipH && verify.videoFrameHeight === clipH))) {
|
|
132
|
+
conformed = true;
|
|
133
|
+
} else if ((clipW || clipH || clipFps || clipPar) && verify) {
|
|
134
|
+
// setSettings did not throw; treat any applied prop as conformed best-effort.
|
|
135
|
+
conformed = true;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
} catch(eConf) {
|
|
139
|
+
applied.error = eConf.toString();
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
var conformNote;
|
|
143
|
+
if (conformed) {
|
|
144
|
+
conformNote = "conformed to clip (sources: " + sources.join(",") + ")";
|
|
145
|
+
} else if (!clipW && !clipH && !clipFps && !clipPar) {
|
|
146
|
+
conformNote = "default preset used (could not read clip video props)";
|
|
147
|
+
} else {
|
|
148
|
+
conformNote = "default preset used (setSettings did not apply clip props)";
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
app.project.activeSequence = seq;
|
|
152
|
+
return __result({
|
|
153
|
+
created: true,
|
|
154
|
+
name: seq.name,
|
|
155
|
+
id: seq.sequenceID,
|
|
156
|
+
width: seq.frameSizeHorizontal,
|
|
157
|
+
height: seq.frameSizeVertical,
|
|
158
|
+
conformed: conformed,
|
|
159
|
+
appliedSettings: applied,
|
|
160
|
+
clipProps: { width: clipW, height: clipH, frameRate: clipFps, pixelAspectRatio: clipPar },
|
|
161
|
+
note: conformNote
|
|
162
|
+
});
|
|
163
163
|
`);
|
|
164
164
|
return sendCommand(script, bridgeOptions);
|
|
165
165
|
},
|
|
166
166
|
},
|
|
167
167
|
add_audio_track: {
|
|
168
|
-
description: `Add a new audio track to the active sequence.
|
|
169
|
-
|
|
170
|
-
Preconditions: An active sequence must exist.
|
|
171
|
-
|
|
172
|
-
Track types:
|
|
173
|
-
- "standard" (default): Standard stereo audio track — works with most clips.
|
|
174
|
-
- "5.1": 5.1 surround audio track.
|
|
175
|
-
- "adaptive": Adaptive audio track (multi-channel routing).
|
|
176
|
-
- "mono": Mono audio track.
|
|
177
|
-
|
|
178
|
-
The new track is added at the bottom of the audio tracks (highest index). Its index can be found via list_sequence_tracks after creation.
|
|
179
|
-
|
|
180
|
-
Example: add_audio_track({}) — adds a standard stereo track
|
|
168
|
+
description: `Add a new audio track to the active sequence.
|
|
169
|
+
|
|
170
|
+
Preconditions: An active sequence must exist.
|
|
171
|
+
|
|
172
|
+
Track types:
|
|
173
|
+
- "standard" (default): Standard stereo audio track — works with most clips.
|
|
174
|
+
- "5.1": 5.1 surround audio track.
|
|
175
|
+
- "adaptive": Adaptive audio track (multi-channel routing).
|
|
176
|
+
- "mono": Mono audio track.
|
|
177
|
+
|
|
178
|
+
The new track is added at the bottom of the audio tracks (highest index). Its index can be found via list_sequence_tracks after creation.
|
|
179
|
+
|
|
180
|
+
Example: add_audio_track({}) — adds a standard stereo track
|
|
181
181
|
Example: add_audio_track({ track_type: "mono", name: "Narration" })`,
|
|
182
182
|
parameters: {
|
|
183
183
|
type: "object",
|
|
@@ -204,59 +204,59 @@ Example: add_audio_track({ track_type: "mono", name: "Narration" })`,
|
|
|
204
204
|
mono: "3", // kAudioTrackType_Mono
|
|
205
205
|
};
|
|
206
206
|
const typeCode = typeMap[trackType] ?? "0";
|
|
207
|
-
const script = buildToolScript(`
|
|
208
|
-
var seq = app.project.activeSequence;
|
|
209
|
-
if (!seq) return __error("No active sequence");
|
|
210
|
-
|
|
211
|
-
var tracksBefore = seq.audioTracks.numTracks;
|
|
212
|
-
// TrackCollection has no addTrack. Use seq.addTrack (pre-PP2026) then QE fallback.
|
|
213
|
-
app.enableQE();
|
|
214
|
-
var __added = false;
|
|
215
|
-
try { if (seq.addTrack) { seq.addTrack("audio", 1); __added = true; } } catch(e) {}
|
|
216
|
-
if (!__added) {
|
|
217
|
-
try {
|
|
218
|
-
var __qs = qe.project.getActiveSequence();
|
|
219
|
-
if (__qs && __qs.addAudioTrack) { __qs.addAudioTrack(${typeCode}); __added = true; }
|
|
220
|
-
} catch(e2) {}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
var tracksAfter = seq.audioTracks.numTracks;
|
|
224
|
-
if (tracksAfter <= tracksBefore) return __error("Failed to add audio track — adding tracks is not supported via ExtendScript in this Premiere Pro version. Use Sequence > Add Tracks in the UI.");
|
|
225
|
-
|
|
226
|
-
var newTrackIndex = tracksAfter - 1;
|
|
227
|
-
var newTrack = seq.audioTracks[newTrackIndex];
|
|
228
|
-
|
|
229
|
-
${args.name ? `
|
|
230
|
-
if (newTrack && newTrack.name !== undefined) {
|
|
231
|
-
newTrack.name = "${escapeForExtendScript(args.name)}";
|
|
232
|
-
}
|
|
233
|
-
` : ""}
|
|
234
|
-
|
|
235
|
-
return __result({
|
|
236
|
-
added: true,
|
|
237
|
-
trackIndex: newTrackIndex,
|
|
238
|
-
trackType: "${trackType}",
|
|
239
|
-
totalAudioTracks: tracksAfter
|
|
240
|
-
});
|
|
207
|
+
const script = buildToolScript(`
|
|
208
|
+
var seq = app.project.activeSequence;
|
|
209
|
+
if (!seq) return __error("No active sequence");
|
|
210
|
+
|
|
211
|
+
var tracksBefore = seq.audioTracks.numTracks;
|
|
212
|
+
// TrackCollection has no addTrack. Use seq.addTrack (pre-PP2026) then QE fallback.
|
|
213
|
+
app.enableQE();
|
|
214
|
+
var __added = false;
|
|
215
|
+
try { if (seq.addTrack) { seq.addTrack("audio", 1); __added = true; } } catch(e) {}
|
|
216
|
+
if (!__added) {
|
|
217
|
+
try {
|
|
218
|
+
var __qs = qe.project.getActiveSequence();
|
|
219
|
+
if (__qs && __qs.addAudioTrack) { __qs.addAudioTrack(${typeCode}); __added = true; }
|
|
220
|
+
} catch(e2) {}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
var tracksAfter = seq.audioTracks.numTracks;
|
|
224
|
+
if (tracksAfter <= tracksBefore) return __error("Failed to add audio track — adding tracks is not supported via ExtendScript in this Premiere Pro version. Use Sequence > Add Tracks in the UI.");
|
|
225
|
+
|
|
226
|
+
var newTrackIndex = tracksAfter - 1;
|
|
227
|
+
var newTrack = seq.audioTracks[newTrackIndex];
|
|
228
|
+
|
|
229
|
+
${args.name ? `
|
|
230
|
+
if (newTrack && newTrack.name !== undefined) {
|
|
231
|
+
newTrack.name = "${escapeForExtendScript(args.name)}";
|
|
232
|
+
}
|
|
233
|
+
` : ""}
|
|
234
|
+
|
|
235
|
+
return __result({
|
|
236
|
+
added: true,
|
|
237
|
+
trackIndex: newTrackIndex,
|
|
238
|
+
trackType: "${trackType}",
|
|
239
|
+
totalAudioTracks: tracksAfter
|
|
240
|
+
});
|
|
241
241
|
`);
|
|
242
242
|
return sendCommand(script, bridgeOptions);
|
|
243
243
|
},
|
|
244
244
|
},
|
|
245
245
|
set_track_volume: {
|
|
246
|
-
description: `Set the volume level for an entire audio track in the active sequence.
|
|
247
|
-
|
|
248
|
-
This sets the track-level volume (the fader in the Audio Track Mixer), which affects all clips on the track. To set individual clip volume, use set_clip_properties or add_keyframe on the clip's Volume property instead.
|
|
249
|
-
|
|
250
|
-
Preconditions: An active sequence must exist with audio tracks.
|
|
251
|
-
|
|
252
|
-
Volume is specified in decibels (dB):
|
|
253
|
-
- 0 dB = unity gain (no change from original level)
|
|
254
|
-
- Positive values (e.g., +6 dB) boost the level
|
|
255
|
-
- Negative values (e.g., -12 dB) reduce the level
|
|
256
|
-
- -∞ (use -96 or lower) effectively mutes the track
|
|
257
|
-
|
|
258
|
-
Example: set_track_volume({ track_index: 0, volume_db: -6 }) — reduce A1 by 6 dB
|
|
259
|
-
Example: set_track_volume({ track_index: 1, volume_db: 0 }) — set A2 to unity gain
|
|
246
|
+
description: `Set the volume level for an entire audio track in the active sequence.
|
|
247
|
+
|
|
248
|
+
This sets the track-level volume (the fader in the Audio Track Mixer), which affects all clips on the track. To set individual clip volume, use set_clip_properties or add_keyframe on the clip's Volume property instead.
|
|
249
|
+
|
|
250
|
+
Preconditions: An active sequence must exist with audio tracks.
|
|
251
|
+
|
|
252
|
+
Volume is specified in decibels (dB):
|
|
253
|
+
- 0 dB = unity gain (no change from original level)
|
|
254
|
+
- Positive values (e.g., +6 dB) boost the level
|
|
255
|
+
- Negative values (e.g., -12 dB) reduce the level
|
|
256
|
+
- -∞ (use -96 or lower) effectively mutes the track
|
|
257
|
+
|
|
258
|
+
Example: set_track_volume({ track_index: 0, volume_db: -6 }) — reduce A1 by 6 dB
|
|
259
|
+
Example: set_track_volume({ track_index: 1, volume_db: 0 }) — set A2 to unity gain
|
|
260
260
|
Example: set_track_volume({ track_index: 2, volume_db: -96 }) — effectively mute A3`,
|
|
261
261
|
parameters: {
|
|
262
262
|
type: "object",
|
|
@@ -273,120 +273,120 @@ Example: set_track_volume({ track_index: 2, volume_db: -96 }) — effectively m
|
|
|
273
273
|
required: ["track_index", "volume_db"],
|
|
274
274
|
},
|
|
275
275
|
handler: async (args) => {
|
|
276
|
-
const script = buildToolScript(`
|
|
277
|
-
var seq = app.project.activeSequence;
|
|
278
|
-
if (!seq) return __error("No active sequence");
|
|
279
|
-
|
|
280
|
-
if (${args.track_index} >= seq.audioTracks.numTracks) {
|
|
281
|
-
return __error("Audio track index out of range: " + ${args.track_index} + " (sequence has " + seq.audioTracks.numTracks + " audio tracks)");
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
var track = seq.audioTracks[${args.track_index}];
|
|
285
|
-
if (!track) return __error("Track is undefined at index " + ${args.track_index});
|
|
286
|
-
|
|
287
|
-
var volumeSet = false;
|
|
288
|
-
var attemptedPaths = [];
|
|
289
|
-
|
|
290
|
-
// Try 1: DOM track components (older PP exposed track-level volume here;
|
|
291
|
-
// verified absent in PP 2026)
|
|
292
|
-
if (track.components && track.components.numItems) {
|
|
293
|
-
attemptedPaths.push("DOM components");
|
|
294
|
-
var tcomp = __findComponent(track,
|
|
295
|
-
["AE.ADBE Audio Levels", "audioVolume", "audioGain", "ADBE Audio Levels"],
|
|
296
|
-
["Volume", "Volumen", "Audio Levels", "Niveles de audio", "Lautstärke"]
|
|
297
|
-
);
|
|
298
|
-
if (tcomp) {
|
|
299
|
-
var tprop = __findProp(tcomp, ["Level", "Volume", "Nivel", "Volumen"]);
|
|
300
|
-
if (tprop) { try { tprop.setValue(${args.volume_db}, true); volumeSet = true; } catch (e1) {} }
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
// Try 2: direct track.volume property (older PP)
|
|
305
|
-
if (!volumeSet && track.volume !== undefined) {
|
|
306
|
-
attemptedPaths.push("track.volume");
|
|
307
|
-
try { track.volume = ${args.volume_db}; volumeSet = true; } catch (e2) {}
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
// Try 3: QE audio track setVolume (PP 2026 has setMute/setLock/setName only,
|
|
311
|
-
// verified at runtime; setVolume undefined)
|
|
312
|
-
if (!volumeSet) {
|
|
313
|
-
attemptedPaths.push("QE setVolume");
|
|
314
|
-
try {
|
|
315
|
-
app.enableQE();
|
|
316
|
-
var qeATrack = qe.project.getActiveSequence().getAudioTrackAt(${args.track_index});
|
|
317
|
-
if (qeATrack && typeof qeATrack.setVolume === "function") {
|
|
318
|
-
qeATrack.setVolume(${args.volume_db});
|
|
319
|
-
volumeSet = true;
|
|
320
|
-
}
|
|
321
|
-
} catch (e3) {}
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
// PP 2026 WORKAROUND: apply the volume_db to all clips on the track
|
|
325
|
-
// individually. This is the practical equivalent of setting the track
|
|
326
|
-
// fader since Premiere mixes clip volume into the master output.
|
|
327
|
-
// Returns success with workaround=true so callers know it's per-clip.
|
|
328
|
-
if (!volumeSet) {
|
|
329
|
-
attemptedPaths.push("per-clip fallback");
|
|
330
|
-
var clipsAffected = 0;
|
|
331
|
-
var clipsTotal = track.clips.numItems;
|
|
332
|
-
for (var ci = 0; ci < clipsTotal; ci++) {
|
|
333
|
-
var aClip = track.clips[ci];
|
|
334
|
-
// Use locale-resilient lookup
|
|
335
|
-
var volComp = __findIntrinsic(aClip, "volume");
|
|
336
|
-
if (volComp) {
|
|
337
|
-
var levelProp = __findProp(volComp, ["Level", "Nivel", "Volume", "Volumen", "Pegel"]);
|
|
338
|
-
if (levelProp) {
|
|
339
|
-
try { levelProp.setValue(${args.volume_db}, true); clipsAffected++; } catch (eClip) {}
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
if (clipsAffected > 0) {
|
|
344
|
-
return __result({
|
|
345
|
-
trackIndex: ${args.track_index},
|
|
346
|
-
volumeDb: ${args.volume_db},
|
|
347
|
-
trackName: track.name || ("A" + (${args.track_index} + 1)),
|
|
348
|
-
workaround: "per-clip-volume",
|
|
349
|
-
clipsAffected: clipsAffected,
|
|
350
|
-
clipsTotal: clipsTotal,
|
|
351
|
-
note: "PP 2026 doesn't expose track-level volume via ExtendScript. Applied " + ${args.volume_db} + "dB to each of the " + clipsAffected + " clips on this track — equivalent net mixing effect to the track fader."
|
|
352
|
-
});
|
|
353
|
-
}
|
|
354
|
-
return __error("set_track_volume: track-level API not available in PP 2026 and no clips on this track to apply per-clip workaround. Attempted: " + attemptedPaths.join(", ") + ". Add clips to the track or adjust the fader manually in the Audio Track Mixer panel.");
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
if (!volumeSet) {
|
|
358
|
-
return __error("Could not set volume on track " + ${args.track_index} + ". Track may not support direct volume setting via this API. Try using add_keyframe on the track's Volume property instead.");
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
return __result({
|
|
362
|
-
trackIndex: ${args.track_index},
|
|
363
|
-
volumeDb: ${args.volume_db},
|
|
364
|
-
trackName: track.name || ("A" + (${args.track_index} + 1))
|
|
365
|
-
});
|
|
276
|
+
const script = buildToolScript(`
|
|
277
|
+
var seq = app.project.activeSequence;
|
|
278
|
+
if (!seq) return __error("No active sequence");
|
|
279
|
+
|
|
280
|
+
if (${args.track_index} >= seq.audioTracks.numTracks) {
|
|
281
|
+
return __error("Audio track index out of range: " + ${args.track_index} + " (sequence has " + seq.audioTracks.numTracks + " audio tracks)");
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
var track = seq.audioTracks[${args.track_index}];
|
|
285
|
+
if (!track) return __error("Track is undefined at index " + ${args.track_index});
|
|
286
|
+
|
|
287
|
+
var volumeSet = false;
|
|
288
|
+
var attemptedPaths = [];
|
|
289
|
+
|
|
290
|
+
// Try 1: DOM track components (older PP exposed track-level volume here;
|
|
291
|
+
// verified absent in PP 2026)
|
|
292
|
+
if (track.components && track.components.numItems) {
|
|
293
|
+
attemptedPaths.push("DOM components");
|
|
294
|
+
var tcomp = __findComponent(track,
|
|
295
|
+
["AE.ADBE Audio Levels", "audioVolume", "audioGain", "ADBE Audio Levels"],
|
|
296
|
+
["Volume", "Volumen", "Audio Levels", "Niveles de audio", "Lautstärke"]
|
|
297
|
+
);
|
|
298
|
+
if (tcomp) {
|
|
299
|
+
var tprop = __findProp(tcomp, ["Level", "Volume", "Nivel", "Volumen"]);
|
|
300
|
+
if (tprop) { try { tprop.setValue(${args.volume_db}, true); volumeSet = true; } catch (e1) {} }
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Try 2: direct track.volume property (older PP)
|
|
305
|
+
if (!volumeSet && track.volume !== undefined) {
|
|
306
|
+
attemptedPaths.push("track.volume");
|
|
307
|
+
try { track.volume = ${args.volume_db}; volumeSet = true; } catch (e2) {}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Try 3: QE audio track setVolume (PP 2026 has setMute/setLock/setName only,
|
|
311
|
+
// verified at runtime; setVolume undefined)
|
|
312
|
+
if (!volumeSet) {
|
|
313
|
+
attemptedPaths.push("QE setVolume");
|
|
314
|
+
try {
|
|
315
|
+
app.enableQE();
|
|
316
|
+
var qeATrack = qe.project.getActiveSequence().getAudioTrackAt(${args.track_index});
|
|
317
|
+
if (qeATrack && typeof qeATrack.setVolume === "function") {
|
|
318
|
+
qeATrack.setVolume(${args.volume_db});
|
|
319
|
+
volumeSet = true;
|
|
320
|
+
}
|
|
321
|
+
} catch (e3) {}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// PP 2026 WORKAROUND: apply the volume_db to all clips on the track
|
|
325
|
+
// individually. This is the practical equivalent of setting the track
|
|
326
|
+
// fader since Premiere mixes clip volume into the master output.
|
|
327
|
+
// Returns success with workaround=true so callers know it's per-clip.
|
|
328
|
+
if (!volumeSet) {
|
|
329
|
+
attemptedPaths.push("per-clip fallback");
|
|
330
|
+
var clipsAffected = 0;
|
|
331
|
+
var clipsTotal = track.clips.numItems;
|
|
332
|
+
for (var ci = 0; ci < clipsTotal; ci++) {
|
|
333
|
+
var aClip = track.clips[ci];
|
|
334
|
+
// Use locale-resilient lookup
|
|
335
|
+
var volComp = __findIntrinsic(aClip, "volume");
|
|
336
|
+
if (volComp) {
|
|
337
|
+
var levelProp = __findProp(volComp, ["Level", "Nivel", "Volume", "Volumen", "Pegel"]);
|
|
338
|
+
if (levelProp) {
|
|
339
|
+
try { levelProp.setValue(${args.volume_db}, true); clipsAffected++; } catch (eClip) {}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
if (clipsAffected > 0) {
|
|
344
|
+
return __result({
|
|
345
|
+
trackIndex: ${args.track_index},
|
|
346
|
+
volumeDb: ${args.volume_db},
|
|
347
|
+
trackName: track.name || ("A" + (${args.track_index} + 1)),
|
|
348
|
+
workaround: "per-clip-volume",
|
|
349
|
+
clipsAffected: clipsAffected,
|
|
350
|
+
clipsTotal: clipsTotal,
|
|
351
|
+
note: "PP 2026 doesn't expose track-level volume via ExtendScript. Applied " + ${args.volume_db} + "dB to each of the " + clipsAffected + " clips on this track — equivalent net mixing effect to the track fader."
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
return __error("set_track_volume: track-level API not available in PP 2026 and no clips on this track to apply per-clip workaround. Attempted: " + attemptedPaths.join(", ") + ". Add clips to the track or adjust the fader manually in the Audio Track Mixer panel.");
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
if (!volumeSet) {
|
|
358
|
+
return __error("Could not set volume on track " + ${args.track_index} + ". Track may not support direct volume setting via this API. Try using add_keyframe on the track's Volume property instead.");
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
return __result({
|
|
362
|
+
trackIndex: ${args.track_index},
|
|
363
|
+
volumeDb: ${args.volume_db},
|
|
364
|
+
trackName: track.name || ("A" + (${args.track_index} + 1))
|
|
365
|
+
});
|
|
366
366
|
`);
|
|
367
367
|
return sendCommand(script, bridgeOptions);
|
|
368
368
|
},
|
|
369
369
|
},
|
|
370
370
|
nest_sequence: {
|
|
371
|
-
description: `Nest the currently selected clips into a new sub-sequence (nested sequence).
|
|
372
|
-
|
|
373
|
-
This replaces the selected clips on the timeline with a single clip that contains a new sub-sequence with the original clips. Nesting is useful for:
|
|
374
|
-
- Grouping clips to apply a single effect to all of them
|
|
375
|
-
- Organizing complex timelines
|
|
376
|
-
- Applying motion/transform to a group of clips
|
|
377
|
-
- Creating "packages" for reuse
|
|
378
|
-
|
|
379
|
-
Preconditions:
|
|
380
|
-
1. An active sequence must exist.
|
|
381
|
-
2. Clips must be selected before calling this tool. Use set_clip_selection or select_clips_by_name to select clips first.
|
|
382
|
-
3. At least one clip must be selected.
|
|
383
|
-
|
|
384
|
-
After nesting, the new sub-sequence appears in the project panel and can be opened and edited independently.
|
|
385
|
-
|
|
386
|
-
Example workflow:
|
|
387
|
-
1. select_clips_by_name({ names: ["clip1.mp4", "clip2.mp4"] })
|
|
388
|
-
2. nest_sequence({ name: "Intro Package" })
|
|
389
|
-
|
|
371
|
+
description: `Nest the currently selected clips into a new sub-sequence (nested sequence).
|
|
372
|
+
|
|
373
|
+
This replaces the selected clips on the timeline with a single clip that contains a new sub-sequence with the original clips. Nesting is useful for:
|
|
374
|
+
- Grouping clips to apply a single effect to all of them
|
|
375
|
+
- Organizing complex timelines
|
|
376
|
+
- Applying motion/transform to a group of clips
|
|
377
|
+
- Creating "packages" for reuse
|
|
378
|
+
|
|
379
|
+
Preconditions:
|
|
380
|
+
1. An active sequence must exist.
|
|
381
|
+
2. Clips must be selected before calling this tool. Use set_clip_selection or select_clips_by_name to select clips first.
|
|
382
|
+
3. At least one clip must be selected.
|
|
383
|
+
|
|
384
|
+
After nesting, the new sub-sequence appears in the project panel and can be opened and edited independently.
|
|
385
|
+
|
|
386
|
+
Example workflow:
|
|
387
|
+
1. select_clips_by_name({ names: ["clip1.mp4", "clip2.mp4"] })
|
|
388
|
+
2. nest_sequence({ name: "Intro Package" })
|
|
389
|
+
|
|
390
390
|
Example: nest_sequence({ name: "Color Grade Group" })`,
|
|
391
391
|
parameters: {
|
|
392
392
|
type: "object",
|
|
@@ -399,74 +399,74 @@ Example: nest_sequence({ name: "Color Grade Group" })`,
|
|
|
399
399
|
required: ["name"],
|
|
400
400
|
},
|
|
401
401
|
handler: async (args) => {
|
|
402
|
-
const script = buildToolScript(`
|
|
403
|
-
var seq = app.project.activeSequence;
|
|
404
|
-
if (!seq) return __error("No active sequence");
|
|
405
|
-
|
|
406
|
-
// Check if any clips are selected
|
|
407
|
-
var selectedCount = 0;
|
|
408
|
-
for (var v = 0; v < seq.videoTracks.numTracks; v++) {
|
|
409
|
-
var track = seq.videoTracks[v];
|
|
410
|
-
for (var c = 0; c < track.clips.numItems; c++) {
|
|
411
|
-
if (track.clips[c].isSelected) selectedCount++;
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
for (var a = 0; a < seq.audioTracks.numTracks; a++) {
|
|
415
|
-
var atrack = seq.audioTracks[a];
|
|
416
|
-
for (var ac = 0; ac < atrack.clips.numItems; ac++) {
|
|
417
|
-
if (atrack.clips[ac].isSelected) selectedCount++;
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
if (selectedCount === 0) {
|
|
422
|
-
return __error("No clips are selected. Use set_clip_selection or select_clips_by_name to select clips before nesting.");
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
// IMPORTANT behavior note: the Premiere DOM does NOT expose a true
|
|
426
|
-
// "Nest selected clips" (which replaces the selection on the timeline
|
|
427
|
-
// with a nested-sequence clip). seq.createSubsequence() builds a NEW
|
|
428
|
-
// sub-sequence from the sequence's IN/OUT range (or the whole sequence
|
|
429
|
-
// when no in/out is set) — it does NOT consume the selection and does
|
|
430
|
-
// NOT replace clips on this timeline. We attempt it here, but report
|
|
431
|
-
// the real behavior so callers are not misled.
|
|
432
|
-
var nestedSeq = null;
|
|
433
|
-
var createError = null;
|
|
434
|
-
try {
|
|
435
|
-
// Some versions accept a boolean (ignoreMapping/ignoreTrackTargeting); guard with try/catch.
|
|
436
|
-
try {
|
|
437
|
-
nestedSeq = seq.createSubsequence(true);
|
|
438
|
-
} catch(eArg) {
|
|
439
|
-
nestedSeq = seq.createSubsequence();
|
|
440
|
-
}
|
|
441
|
-
} catch(eSub) {
|
|
442
|
-
createError = eSub.toString();
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
if (!nestedSeq) {
|
|
446
|
-
return __error("createSubsequence failed" + (createError ? (": " + createError) : "") + ". The Premiere DOM has no true nest-selection API; this builds a sub-sequence from the sequence in/out range. Set sequence in/out points around the clips first, or nest manually in the UI.");
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
// Rename the resulting sub-sequence to the requested name (createSubsequence
|
|
450
|
-
// does not accept a name argument in current versions).
|
|
451
|
-
var renamed = false;
|
|
452
|
-
try {
|
|
453
|
-
if (nestedSeq.projectItem && nestedSeq.projectItem.name !== undefined) {
|
|
454
|
-
nestedSeq.projectItem.name = "${escapeForExtendScript(args.name)}";
|
|
455
|
-
renamed = true;
|
|
456
|
-
} else if (nestedSeq.name !== undefined) {
|
|
457
|
-
nestedSeq.name = "${escapeForExtendScript(args.name)}";
|
|
458
|
-
renamed = true;
|
|
459
|
-
}
|
|
460
|
-
} catch(eName) {}
|
|
461
|
-
|
|
462
|
-
return __result({
|
|
463
|
-
nested: true,
|
|
464
|
-
nestedSequenceName: nestedSeq.name,
|
|
465
|
-
nestedSequenceId: nestedSeq.sequenceID,
|
|
466
|
-
renamed: renamed,
|
|
467
|
-
selectedClips: selectedCount,
|
|
468
|
-
behaviorNote: "createSubsequence builds a sub-sequence from the sequence IN/OUT range (whole sequence if no in/out set). It does NOT replace the selected clips on this timeline and does NOT perform a true 'Nest'. The selection count is reported for reference only."
|
|
469
|
-
});
|
|
402
|
+
const script = buildToolScript(`
|
|
403
|
+
var seq = app.project.activeSequence;
|
|
404
|
+
if (!seq) return __error("No active sequence");
|
|
405
|
+
|
|
406
|
+
// Check if any clips are selected
|
|
407
|
+
var selectedCount = 0;
|
|
408
|
+
for (var v = 0; v < seq.videoTracks.numTracks; v++) {
|
|
409
|
+
var track = seq.videoTracks[v];
|
|
410
|
+
for (var c = 0; c < track.clips.numItems; c++) {
|
|
411
|
+
if (track.clips[c].isSelected) selectedCount++;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
for (var a = 0; a < seq.audioTracks.numTracks; a++) {
|
|
415
|
+
var atrack = seq.audioTracks[a];
|
|
416
|
+
for (var ac = 0; ac < atrack.clips.numItems; ac++) {
|
|
417
|
+
if (atrack.clips[ac].isSelected) selectedCount++;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
if (selectedCount === 0) {
|
|
422
|
+
return __error("No clips are selected. Use set_clip_selection or select_clips_by_name to select clips before nesting.");
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// IMPORTANT behavior note: the Premiere DOM does NOT expose a true
|
|
426
|
+
// "Nest selected clips" (which replaces the selection on the timeline
|
|
427
|
+
// with a nested-sequence clip). seq.createSubsequence() builds a NEW
|
|
428
|
+
// sub-sequence from the sequence's IN/OUT range (or the whole sequence
|
|
429
|
+
// when no in/out is set) — it does NOT consume the selection and does
|
|
430
|
+
// NOT replace clips on this timeline. We attempt it here, but report
|
|
431
|
+
// the real behavior so callers are not misled.
|
|
432
|
+
var nestedSeq = null;
|
|
433
|
+
var createError = null;
|
|
434
|
+
try {
|
|
435
|
+
// Some versions accept a boolean (ignoreMapping/ignoreTrackTargeting); guard with try/catch.
|
|
436
|
+
try {
|
|
437
|
+
nestedSeq = seq.createSubsequence(true);
|
|
438
|
+
} catch(eArg) {
|
|
439
|
+
nestedSeq = seq.createSubsequence();
|
|
440
|
+
}
|
|
441
|
+
} catch(eSub) {
|
|
442
|
+
createError = eSub.toString();
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
if (!nestedSeq) {
|
|
446
|
+
return __error("createSubsequence failed" + (createError ? (": " + createError) : "") + ". The Premiere DOM has no true nest-selection API; this builds a sub-sequence from the sequence in/out range. Set sequence in/out points around the clips first, or nest manually in the UI.");
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// Rename the resulting sub-sequence to the requested name (createSubsequence
|
|
450
|
+
// does not accept a name argument in current versions).
|
|
451
|
+
var renamed = false;
|
|
452
|
+
try {
|
|
453
|
+
if (nestedSeq.projectItem && nestedSeq.projectItem.name !== undefined) {
|
|
454
|
+
nestedSeq.projectItem.name = "${escapeForExtendScript(args.name)}";
|
|
455
|
+
renamed = true;
|
|
456
|
+
} else if (nestedSeq.name !== undefined) {
|
|
457
|
+
nestedSeq.name = "${escapeForExtendScript(args.name)}";
|
|
458
|
+
renamed = true;
|
|
459
|
+
}
|
|
460
|
+
} catch(eName) {}
|
|
461
|
+
|
|
462
|
+
return __result({
|
|
463
|
+
nested: true,
|
|
464
|
+
nestedSequenceName: nestedSeq.name,
|
|
465
|
+
nestedSequenceId: nestedSeq.sequenceID,
|
|
466
|
+
renamed: renamed,
|
|
467
|
+
selectedClips: selectedCount,
|
|
468
|
+
behaviorNote: "createSubsequence builds a sub-sequence from the sequence IN/OUT range (whole sequence if no in/out set). It does NOT replace the selected clips on this timeline and does NOT perform a true 'Nest'. The selection count is reported for reference only."
|
|
469
|
+
});
|
|
470
470
|
`);
|
|
471
471
|
return sendCommand(script, bridgeOptions);
|
|
472
472
|
},
|