@synthetic-ai/premiere-mcp 2.0.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 (104) hide show
  1. package/CHANGELOG.md +73 -0
  2. package/LICENSE +21 -0
  3. package/README.md +242 -0
  4. package/cep-plugin/.debug +8 -0
  5. package/cep-plugin/CSInterface.js +70 -0
  6. package/cep-plugin/CSXS/manifest.xml +49 -0
  7. package/cep-plugin/host.jsx +7 -0
  8. package/cep-plugin/index.html +419 -0
  9. package/cep-plugin/main.js +327 -0
  10. package/cep-plugin/premiere.jsx +24947 -0
  11. package/cep-plugin/synthetic-logo.png +0 -0
  12. package/dist/bridge/file-bridge.d.ts +29 -0
  13. package/dist/bridge/file-bridge.js +145 -0
  14. package/dist/bridge/script-builder.d.ts +19 -0
  15. package/dist/bridge/script-builder.js +408 -0
  16. package/dist/http-server.d.ts +21 -0
  17. package/dist/http-server.js +140 -0
  18. package/dist/index.d.ts +3 -0
  19. package/dist/index.js +70 -0
  20. package/dist/resources/extendscript-reference.d.ts +2 -0
  21. package/dist/resources/extendscript-reference.js +219 -0
  22. package/dist/server.d.ts +4 -0
  23. package/dist/server.js +276 -0
  24. package/dist/tools/advanced.d.ts +530 -0
  25. package/dist/tools/advanced.js +923 -0
  26. package/dist/tools/audio-advanced.d.ts +847 -0
  27. package/dist/tools/audio-advanced.js +1653 -0
  28. package/dist/tools/audio.d.ts +84 -0
  29. package/dist/tools/audio.js +123 -0
  30. package/dist/tools/captions.d.ts +30 -0
  31. package/dist/tools/captions.js +73 -0
  32. package/dist/tools/clipboard.d.ts +153 -0
  33. package/dist/tools/clipboard.js +381 -0
  34. package/dist/tools/color-advanced.d.ts +821 -0
  35. package/dist/tools/color-advanced.js +772 -0
  36. package/dist/tools/discovery.d.ts +128 -0
  37. package/dist/tools/discovery.js +445 -0
  38. package/dist/tools/effects.d.ts +211 -0
  39. package/dist/tools/effects.js +554 -0
  40. package/dist/tools/export-presets.d.ts +333 -0
  41. package/dist/tools/export-presets.js +490 -0
  42. package/dist/tools/export.d.ts +320 -0
  43. package/dist/tools/export.js +618 -0
  44. package/dist/tools/health.d.ts +9 -0
  45. package/dist/tools/health.js +25 -0
  46. package/dist/tools/inspection.d.ts +179 -0
  47. package/dist/tools/inspection.js +1089 -0
  48. package/dist/tools/keyframes.d.ts +253 -0
  49. package/dist/tools/keyframes.js +611 -0
  50. package/dist/tools/markers.d.ts +117 -0
  51. package/dist/tools/markers.js +206 -0
  52. package/dist/tools/media.d.ts +297 -0
  53. package/dist/tools/media.js +493 -0
  54. package/dist/tools/metadata.d.ts +178 -0
  55. package/dist/tools/metadata.js +278 -0
  56. package/dist/tools/multicam.d.ts +199 -0
  57. package/dist/tools/multicam.js +212 -0
  58. package/dist/tools/playback.d.ts +34 -0
  59. package/dist/tools/playback.js +63 -0
  60. package/dist/tools/playhead.d.ts +77 -0
  61. package/dist/tools/playhead.js +185 -0
  62. package/dist/tools/project-manager.d.ts +65 -0
  63. package/dist/tools/project-manager.js +81 -0
  64. package/dist/tools/project.d.ts +390 -0
  65. package/dist/tools/project.js +596 -0
  66. package/dist/tools/scripting.d.ts +98 -0
  67. package/dist/tools/scripting.js +434 -0
  68. package/dist/tools/selection.d.ts +120 -0
  69. package/dist/tools/selection.js +334 -0
  70. package/dist/tools/sequence-advanced.d.ts +84 -0
  71. package/dist/tools/sequence-advanced.js +471 -0
  72. package/dist/tools/sequence-settings.d.ts +397 -0
  73. package/dist/tools/sequence-settings.js +967 -0
  74. package/dist/tools/sequence.d.ts +211 -0
  75. package/dist/tools/sequence.js +378 -0
  76. package/dist/tools/source-monitor.d.ts +95 -0
  77. package/dist/tools/source-monitor.js +180 -0
  78. package/dist/tools/text.d.ts +97 -0
  79. package/dist/tools/text.js +159 -0
  80. package/dist/tools/timeline.d.ts +265 -0
  81. package/dist/tools/timeline.js +575 -0
  82. package/dist/tools/track-management.d.ts +482 -0
  83. package/dist/tools/track-management.js +787 -0
  84. package/dist/tools/track-targeting.d.ts +666 -0
  85. package/dist/tools/track-targeting.js +1318 -0
  86. package/dist/tools/tracks.d.ts +90 -0
  87. package/dist/tools/tracks.js +183 -0
  88. package/dist/tools/transitions-motion.d.ts +788 -0
  89. package/dist/tools/transitions-motion.js +680 -0
  90. package/dist/tools/transitions.d.ts +124 -0
  91. package/dist/tools/transitions.js +267 -0
  92. package/dist/tools/utility.d.ts +469 -0
  93. package/dist/tools/utility.js +1118 -0
  94. package/dist/tools/workspace.d.ts +25 -0
  95. package/dist/tools/workspace.js +61 -0
  96. package/package.json +66 -0
  97. package/scripts/build-chat-dmg.sh +210 -0
  98. package/scripts/build-chat-zip.sh +291 -0
  99. package/scripts/install-cep-windows.ps1 +99 -0
  100. package/scripts/install-cep.sh +66 -0
  101. package/scripts/install-chat-plugin.bat +67 -0
  102. package/scripts/install-chat-plugin.sh +85 -0
  103. package/scripts/install-windows.bat +95 -0
  104. package/scripts/package.mjs +218 -0
@@ -0,0 +1,1089 @@
1
+ import { buildToolScript, escapeForExtendScript } from "../bridge/script-builder.js";
2
+ import { sendCommand } from "../bridge/file-bridge.js";
3
+ export function getInspectionTools(bridgeOptions) {
4
+ return {
5
+ get_full_project_overview: {
6
+ description: "Get a comprehensive overview of the entire project: all bins (recursive tree), all sequences, media stats, offline items, and project settings. This is the best first call to fully understand a project.",
7
+ parameters: {},
8
+ handler: async () => {
9
+ const script = buildToolScript(`
10
+ var project = app.project;
11
+ if (!project) return __error("No project is open");
12
+
13
+ function walkBin(bin, depth) {
14
+ var items = [];
15
+ for (var i = 0; i < bin.children.numItems; i++) {
16
+ var item = bin.children[i];
17
+ var entry = {
18
+ nodeId: item.nodeId,
19
+ name: item.name,
20
+ treePath: item.treePath,
21
+ type: item.type === 1 ? "clip" : item.type === 2 ? "bin" : item.type === 3 ? "root" : item.type === 4 ? "file" : "unknown"
22
+ };
23
+ try { entry.mediaPath = item.getMediaPath(); } catch(e) {}
24
+ try { entry.offline = item.isOffline(); } catch(e) {}
25
+ try { entry.colorLabel = item.getColorLabel(); } catch(e) {}
26
+ if (item.type === 2 && depth < 10) {
27
+ entry.children = walkBin(item, depth + 1);
28
+ entry.childCount = entry.children.length;
29
+ }
30
+ items.push(entry);
31
+ }
32
+ return items;
33
+ }
34
+
35
+ var binTree = walkBin(project.rootItem, 0);
36
+
37
+ var sequences = [];
38
+ for (var i = 0; i < project.sequences.numSequences; i++) {
39
+ var seq = project.sequences[i];
40
+ var clipCount = 0;
41
+ for (var t = 0; t < seq.videoTracks.numTracks; t++) clipCount += seq.videoTracks[t].clips.numItems;
42
+ for (var t = 0; t < seq.audioTracks.numTracks; t++) clipCount += seq.audioTracks[t].clips.numItems;
43
+ sequences.push({
44
+ name: seq.name,
45
+ id: seq.sequenceID,
46
+ width: seq.frameSizeHorizontal,
47
+ height: seq.frameSizeVertical,
48
+ durationSeconds: __ticksToSeconds(seq.end),
49
+ videoTracks: seq.videoTracks.numTracks,
50
+ audioTracks: seq.audioTracks.numTracks,
51
+ totalClips: clipCount
52
+ });
53
+ }
54
+
55
+ var totalItems = 0;
56
+ var offlineCount = 0;
57
+ var mediaTypes = {};
58
+ function countItems(bin) {
59
+ for (var i = 0; i < bin.children.numItems; i++) {
60
+ var item = bin.children[i];
61
+ totalItems++;
62
+ try { if (item.isOffline()) offlineCount++; } catch(e) {}
63
+ try {
64
+ var mp = item.getMediaPath();
65
+ if (mp) {
66
+ var ext = mp.split(".").pop().toLowerCase();
67
+ if (!mediaTypes[ext]) mediaTypes[ext] = 0;
68
+ mediaTypes[ext]++;
69
+ }
70
+ } catch(e) {}
71
+ if (item.type === 2) countItems(item);
72
+ }
73
+ }
74
+ countItems(project.rootItem);
75
+
76
+ var activeSeqInfo = null;
77
+ var active = project.activeSequence;
78
+ if (active) {
79
+ activeSeqInfo = { name: active.name, id: active.sequenceID };
80
+ }
81
+
82
+ return __result({
83
+ projectName: project.name,
84
+ projectPath: project.path,
85
+ totalItems: totalItems,
86
+ offlineItems: offlineCount,
87
+ sequenceCount: sequences.length,
88
+ mediaFileTypes: mediaTypes,
89
+ activeSequence: activeSeqInfo,
90
+ sequences: sequences,
91
+ binTree: binTree
92
+ });
93
+ `);
94
+ return sendCommand(script, bridgeOptions);
95
+ },
96
+ },
97
+ get_bin_contents: {
98
+ description: "Get detailed contents of a specific bin (folder) including all nested items, media paths, offline status, color labels, and metadata. Searches by bin name or node ID.",
99
+ parameters: {
100
+ type: "object",
101
+ properties: {
102
+ bin_id: {
103
+ type: "string",
104
+ description: "Bin name, node ID, or path (e.g., 'Footage', 'Footage/Raw')",
105
+ },
106
+ recursive: {
107
+ type: "boolean",
108
+ description: "Include items from sub-bins recursively (default: true)",
109
+ },
110
+ },
111
+ required: ["bin_id"],
112
+ },
113
+ handler: async (args) => {
114
+ const recursive = args.recursive !== false;
115
+ const script = buildToolScript(`
116
+ var root = app.project.rootItem;
117
+ var target = null;
118
+
119
+ // Try by node ID first
120
+ function findById(parent, id) {
121
+ for (var i = 0; i < parent.children.numItems; i++) {
122
+ var item = parent.children[i];
123
+ if (item.nodeId === id) return item;
124
+ if (item.type === 2) {
125
+ var found = findById(item, id);
126
+ if (found) return found;
127
+ }
128
+ }
129
+ return null;
130
+ }
131
+
132
+ target = findById(root, "${escapeForExtendScript(args.bin_id)}");
133
+
134
+ // Try by path
135
+ if (!target) {
136
+ var parts = "${escapeForExtendScript(args.bin_id)}".split("/");
137
+ var current = root;
138
+ for (var p = 0; p < parts.length; p++) {
139
+ var found = false;
140
+ for (var i = 0; i < current.children.numItems; i++) {
141
+ if (current.children[i].name === parts[p] && current.children[i].type === 2) {
142
+ current = current.children[i];
143
+ found = true;
144
+ break;
145
+ }
146
+ }
147
+ if (!found) { current = null; break; }
148
+ }
149
+ if (current && current !== root) target = current;
150
+ }
151
+
152
+ // Try by name
153
+ if (!target) {
154
+ function findByName(parent, name) {
155
+ for (var i = 0; i < parent.children.numItems; i++) {
156
+ var item = parent.children[i];
157
+ if (item.name === name && item.type === 2) return item;
158
+ if (item.type === 2) {
159
+ var found = findByName(item, name);
160
+ if (found) return found;
161
+ }
162
+ }
163
+ return null;
164
+ }
165
+ target = findByName(root, "${escapeForExtendScript(args.bin_id)}");
166
+ }
167
+
168
+ if (!target) return __error("Bin not found: ${escapeForExtendScript(args.bin_id)}");
169
+
170
+ function getItemDetails(item) {
171
+ var info = {
172
+ nodeId: item.nodeId,
173
+ name: item.name,
174
+ type: item.type === 1 ? "clip" : item.type === 2 ? "bin" : item.type === 4 ? "file" : "unknown",
175
+ treePath: item.treePath
176
+ };
177
+ try { info.mediaPath = item.getMediaPath(); } catch(e) {}
178
+ try { info.offline = item.isOffline(); } catch(e) {}
179
+ try { info.colorLabel = item.getColorLabel(); } catch(e) {}
180
+ try {
181
+ var interp = item.getFootageInterpretation();
182
+ if (interp) {
183
+ info.frameRate = interp.frameRate;
184
+ info.pixelAspectRatio = interp.pixelAspectRatio;
185
+ }
186
+ } catch(e) {}
187
+ try {
188
+ info.inPoint = __ticksToSeconds(item.getInPoint().ticks);
189
+ info.outPoint = __ticksToSeconds(item.getOutPoint().ticks);
190
+ } catch(e) {}
191
+ return info;
192
+ }
193
+
194
+ function walkItems(bin, recurse) {
195
+ var items = [];
196
+ for (var i = 0; i < bin.children.numItems; i++) {
197
+ var item = bin.children[i];
198
+ var info = getItemDetails(item);
199
+ if (item.type === 2 && recurse) {
200
+ info.children = walkItems(item, true);
201
+ info.childCount = info.children.length;
202
+ }
203
+ items.push(info);
204
+ }
205
+ return items;
206
+ }
207
+
208
+ var contents = walkItems(target, ${recursive});
209
+ return __result({
210
+ binName: target.name,
211
+ binNodeId: target.nodeId,
212
+ binPath: target.treePath,
213
+ itemCount: contents.length,
214
+ items: contents
215
+ });
216
+ `);
217
+ return sendCommand(script, bridgeOptions);
218
+ },
219
+ },
220
+ get_full_sequence_info: {
221
+ description: "Get exhaustive information about a sequence: settings, all tracks with lock/mute/target state, all clips with positions/effects/speed/enabled state, all markers, transitions, in/out points, and work area.",
222
+ parameters: {
223
+ type: "object",
224
+ properties: {
225
+ sequence_id: {
226
+ type: "string",
227
+ description: "Sequence name or ID. Uses active sequence if omitted.",
228
+ },
229
+ },
230
+ },
231
+ handler: async (args) => {
232
+ const seqLookup = args.sequence_id
233
+ ? `var seq = __findSequence("${escapeForExtendScript(args.sequence_id)}"); if (!seq) return __error("Sequence not found");`
234
+ : `var seq = app.project.activeSequence; if (!seq) return __error("No active sequence");`;
235
+ const script = buildToolScript(`
236
+ ${seqLookup}
237
+
238
+ // Settings
239
+ var info = {
240
+ name: seq.name,
241
+ id: seq.sequenceID,
242
+ width: seq.frameSizeHorizontal,
243
+ height: seq.frameSizeVertical,
244
+ durationSeconds: __ticksToSeconds(seq.end)
245
+ };
246
+
247
+ try {
248
+ var s = seq.getSettings();
249
+ if (s) {
250
+ info.frameRate = s.videoFrameRate;
251
+ info.audioSampleRate = s.audioSampleRate;
252
+ info.audioChannelType = s.audioChannelType;
253
+ info.audioChannelCount = s.audioChannelCount;
254
+ info.videoFieldType = s.videoFieldType;
255
+ info.videoDisplayFormat = s.videoDisplayFormat;
256
+ info.audioDisplayFormat = s.audioDisplayFormat;
257
+ }
258
+ } catch(e) {}
259
+
260
+ // Playhead
261
+ try { info.playheadSeconds = __ticksToSeconds(seq.getPlayerPosition().ticks); } catch(e) {}
262
+
263
+ // In/Out points
264
+ try { info.inPointSeconds = __ticksToSeconds(seq.getInPoint()); } catch(e) {}
265
+ try { info.outPointSeconds = __ticksToSeconds(seq.getOutPoint()); } catch(e) {}
266
+
267
+ // Work area
268
+ try { info.workAreaIn = __ticksToSeconds(seq.getWorkAreaInPoint()); } catch(e) {}
269
+ try { info.workAreaOut = __ticksToSeconds(seq.getWorkAreaOutPoint()); } catch(e) {}
270
+
271
+ // Zero point
272
+ try { info.zeroPoint = __ticksToSeconds(seq.zeroPoint); } catch(e) {}
273
+
274
+ // Markers
275
+ var markers = [];
276
+ try {
277
+ var m = seq.markers.getFirstMarker();
278
+ while (m) {
279
+ var mInfo = {
280
+ name: m.name,
281
+ comments: m.comments,
282
+ startSeconds: __ticksToSeconds(m.start.ticks),
283
+ endSeconds: __ticksToSeconds(m.end.ticks),
284
+ type: m.type
285
+ };
286
+ try { mInfo.colorIndex = m.getColorByIndex(); } catch(e) {}
287
+ markers.push(mInfo);
288
+ m = seq.markers.getNextMarker(m);
289
+ }
290
+ } catch(e) {}
291
+ info.markers = markers;
292
+ info.markerCount = markers.length;
293
+
294
+ // Video tracks
295
+ info.videoTracks = [];
296
+ for (var t = 0; t < seq.videoTracks.numTracks; t++) {
297
+ var track = seq.videoTracks[t];
298
+ var trackInfo = {
299
+ index: t,
300
+ name: track.name,
301
+ isMuted: track.isMuted(),
302
+ isLocked: track.isLocked()
303
+ };
304
+ try { trackInfo.isTargeted = track.isTargeted(); } catch(e) {}
305
+
306
+ // Clips
307
+ trackInfo.clips = [];
308
+ for (var c = 0; c < track.clips.numItems; c++) {
309
+ var clip = track.clips[c];
310
+ var ci = {
311
+ index: c,
312
+ nodeId: clip.nodeId,
313
+ name: clip.name,
314
+ startSeconds: __ticksToSeconds(clip.start.ticks),
315
+ endSeconds: __ticksToSeconds(clip.end.ticks),
316
+ durationSeconds: __ticksToSeconds(clip.duration.ticks),
317
+ inPointSeconds: __ticksToSeconds(clip.inPoint.ticks),
318
+ outPointSeconds: __ticksToSeconds(clip.outPoint.ticks),
319
+ mediaType: clip.mediaType
320
+ };
321
+ try { ci.enabled = !clip.isDisabled(); } catch(e) { ci.enabled = true; }
322
+ try { ci.speed = clip.getSpeed(); } catch(e) {}
323
+ try { ci.reversed = clip.isSpeedReversed(); } catch(e) {}
324
+ try { ci.isAdjustmentLayer = clip.isAdjustmentLayer(); } catch(e) {}
325
+ try { ci.isSelected = clip.isSelected(); } catch(e) {}
326
+
327
+ // Source media info
328
+ try {
329
+ if (clip.projectItem) {
330
+ ci.sourceNodeId = clip.projectItem.nodeId;
331
+ ci.sourceName = clip.projectItem.name;
332
+ try { ci.sourceMediaPath = clip.projectItem.getMediaPath(); } catch(e) {}
333
+ try { ci.sourceOffline = clip.projectItem.isOffline(); } catch(e) {}
334
+ }
335
+ } catch(e) {}
336
+
337
+ // Effect count
338
+ try {
339
+ ci.effectCount = clip.components.numItems;
340
+ ci.effects = [];
341
+ for (var e = 0; e < clip.components.numItems; e++) {
342
+ ci.effects.push(clip.components[e].displayName);
343
+ }
344
+ } catch(e) {}
345
+
346
+ trackInfo.clips.push(ci);
347
+ }
348
+
349
+ // Transitions
350
+ trackInfo.transitions = [];
351
+ try {
352
+ for (var tr = 0; tr < track.transitions.numItems; tr++) {
353
+ var trans = track.transitions[tr];
354
+ trackInfo.transitions.push({
355
+ index: tr,
356
+ startSeconds: __ticksToSeconds(trans.start.ticks),
357
+ endSeconds: __ticksToSeconds(trans.end.ticks),
358
+ type: trans.type
359
+ });
360
+ }
361
+ } catch(e) {}
362
+
363
+ trackInfo.clipCount = trackInfo.clips.length;
364
+ trackInfo.transitionCount = trackInfo.transitions.length;
365
+ info.videoTracks.push(trackInfo);
366
+ }
367
+
368
+ // Audio tracks
369
+ info.audioTracks = [];
370
+ for (var t = 0; t < seq.audioTracks.numTracks; t++) {
371
+ var track = seq.audioTracks[t];
372
+ var trackInfo = {
373
+ index: t,
374
+ name: track.name,
375
+ isMuted: track.isMuted(),
376
+ isLocked: track.isLocked()
377
+ };
378
+ try { trackInfo.isTargeted = track.isTargeted(); } catch(e) {}
379
+
380
+ trackInfo.clips = [];
381
+ for (var c = 0; c < track.clips.numItems; c++) {
382
+ var clip = track.clips[c];
383
+ var ci = {
384
+ index: c,
385
+ nodeId: clip.nodeId,
386
+ name: clip.name,
387
+ startSeconds: __ticksToSeconds(clip.start.ticks),
388
+ endSeconds: __ticksToSeconds(clip.end.ticks),
389
+ durationSeconds: __ticksToSeconds(clip.duration.ticks),
390
+ inPointSeconds: __ticksToSeconds(clip.inPoint.ticks),
391
+ outPointSeconds: __ticksToSeconds(clip.outPoint.ticks),
392
+ mediaType: clip.mediaType
393
+ };
394
+ try { ci.enabled = !clip.isDisabled(); } catch(e) { ci.enabled = true; }
395
+ try { ci.speed = clip.getSpeed(); } catch(e) {}
396
+ try {
397
+ if (clip.projectItem) {
398
+ ci.sourceNodeId = clip.projectItem.nodeId;
399
+ ci.sourceName = clip.projectItem.name;
400
+ try { ci.sourceMediaPath = clip.projectItem.getMediaPath(); } catch(e) {}
401
+ }
402
+ } catch(e) {}
403
+ try {
404
+ ci.effectCount = clip.components.numItems;
405
+ ci.effects = [];
406
+ for (var e = 0; e < clip.components.numItems; e++) {
407
+ ci.effects.push(clip.components[e].displayName);
408
+ }
409
+ } catch(e) {}
410
+ trackInfo.clips.push(ci);
411
+ }
412
+
413
+ trackInfo.transitions = [];
414
+ try {
415
+ for (var tr = 0; tr < track.transitions.numItems; tr++) {
416
+ var trans = track.transitions[tr];
417
+ trackInfo.transitions.push({
418
+ index: tr,
419
+ startSeconds: __ticksToSeconds(trans.start.ticks),
420
+ endSeconds: __ticksToSeconds(trans.end.ticks)
421
+ });
422
+ }
423
+ } catch(e) {}
424
+
425
+ trackInfo.clipCount = trackInfo.clips.length;
426
+ trackInfo.transitionCount = trackInfo.transitions.length;
427
+ info.audioTracks.push(trackInfo);
428
+ }
429
+
430
+ info.videoTrackCount = info.videoTracks.length;
431
+ info.audioTrackCount = info.audioTracks.length;
432
+
433
+ return __result(info);
434
+ `);
435
+ return sendCommand(script, bridgeOptions);
436
+ },
437
+ },
438
+ get_full_clip_info: {
439
+ description: "Get exhaustive information about a specific clip: all effects with every property value, source media details, footage interpretation, metadata, markers, speed, enabled state, color label, linked clips, and proxy status.",
440
+ parameters: {
441
+ type: "object",
442
+ properties: {
443
+ node_id: {
444
+ type: "string",
445
+ description: "Node ID of the clip on the timeline",
446
+ },
447
+ },
448
+ required: ["node_id"],
449
+ },
450
+ handler: async (args) => {
451
+ const script = buildToolScript(`
452
+ var found = __findClip("${escapeForExtendScript(args.node_id)}");
453
+ if (!found) return __error("Clip not found: ${escapeForExtendScript(args.node_id)}");
454
+
455
+ var clip = found.clip;
456
+ var info = {
457
+ nodeId: clip.nodeId,
458
+ name: clip.name,
459
+ trackType: found.trackType,
460
+ trackIndex: found.trackIndex,
461
+ clipIndex: found.clipIndex,
462
+ startSeconds: __ticksToSeconds(clip.start.ticks),
463
+ endSeconds: __ticksToSeconds(clip.end.ticks),
464
+ durationSeconds: __ticksToSeconds(clip.duration.ticks),
465
+ inPointSeconds: __ticksToSeconds(clip.inPoint.ticks),
466
+ outPointSeconds: __ticksToSeconds(clip.outPoint.ticks),
467
+ mediaType: clip.mediaType
468
+ };
469
+
470
+ try { info.enabled = !clip.isDisabled(); } catch(e) { info.enabled = true; }
471
+ try { info.speed = clip.getSpeed(); } catch(e) {}
472
+ try { info.reversed = clip.isSpeedReversed(); } catch(e) {}
473
+ try { info.isSelected = clip.isSelected(); } catch(e) {}
474
+ try { info.isAdjustmentLayer = clip.isAdjustmentLayer(); } catch(e) {}
475
+
476
+ // Source project item details
477
+ try {
478
+ var src = clip.projectItem;
479
+ if (src) {
480
+ info.source = {
481
+ nodeId: src.nodeId,
482
+ name: src.name,
483
+ treePath: src.treePath
484
+ };
485
+ try { info.source.mediaPath = src.getMediaPath(); } catch(e) {}
486
+ try { info.source.offline = src.isOffline(); } catch(e) {}
487
+ try { info.source.colorLabel = src.getColorLabel(); } catch(e) {}
488
+ try {
489
+ var interp = src.getFootageInterpretation();
490
+ if (interp) {
491
+ info.source.frameRate = interp.frameRate;
492
+ info.source.pixelAspectRatio = interp.pixelAspectRatio;
493
+ info.source.fieldType = interp.fieldType;
494
+ info.source.alphaUsage = interp.alphaUsage;
495
+ }
496
+ } catch(e) {}
497
+ // XMP metadata
498
+ try {
499
+ var xmp = src.getProjectMetadata();
500
+ if (xmp && xmp.length < 5000) info.source.projectMetadata = xmp;
501
+ } catch(e) {}
502
+ // Proxy info
503
+ try { info.source.hasProxy = src.hasProxy(); } catch(e) {}
504
+ try { info.source.canChangeMediaPath = src.canChangeMediaPath(); } catch(e) {}
505
+ }
506
+ } catch(e) {}
507
+
508
+ // All effects/components with full property details
509
+ info.components = [];
510
+ try {
511
+ for (var i = 0; i < clip.components.numItems; i++) {
512
+ var comp = clip.components[i];
513
+ var compInfo = {
514
+ index: i,
515
+ displayName: comp.displayName,
516
+ matchName: comp.matchName,
517
+ properties: []
518
+ };
519
+ for (var p = 0; p < comp.properties.numItems; p++) {
520
+ var prop = comp.properties[p];
521
+ var propInfo = {
522
+ index: p,
523
+ displayName: prop.displayName
524
+ };
525
+ try { propInfo.matchName = prop.matchName; } catch(e) {}
526
+ try { propInfo.value = prop.getValue(0, 0); } catch(e) {}
527
+ try { propInfo.isTimeVarying = prop.isTimeVarying(); } catch(e) {}
528
+ try { propInfo.keyframesSupported = prop.areKeyframesSupported(); } catch(e) {}
529
+ try {
530
+ if (prop.isTimeVarying()) {
531
+ var keys = prop.getKeys();
532
+ if (keys && keys.length > 0) {
533
+ propInfo.keyframeCount = keys.length;
534
+ propInfo.keyframeTimes = [];
535
+ for (var k = 0; k < Math.min(keys.length, 20); k++) {
536
+ propInfo.keyframeTimes.push(__ticksToSeconds(keys[k].ticks));
537
+ }
538
+ if (keys.length > 20) propInfo.keyframesTruncated = true;
539
+ }
540
+ }
541
+ } catch(e) {}
542
+ compInfo.properties.push(propInfo);
543
+ }
544
+ info.components.push(compInfo);
545
+ }
546
+ } catch(e) {}
547
+ info.componentCount = info.components.length;
548
+
549
+ // Clip markers
550
+ info.markers = [];
551
+ try {
552
+ var m = clip.markers.getFirstMarker();
553
+ while (m) {
554
+ info.markers.push({
555
+ name: m.name,
556
+ comments: m.comments,
557
+ startSeconds: __ticksToSeconds(m.start.ticks),
558
+ endSeconds: __ticksToSeconds(m.end.ticks)
559
+ });
560
+ m = clip.markers.getNextMarker(m);
561
+ }
562
+ } catch(e) {}
563
+
564
+ return __result(info);
565
+ `);
566
+ return sendCommand(script, bridgeOptions);
567
+ },
568
+ },
569
+ get_project_item_info: {
570
+ description: "Get detailed information about a project item (media file in the project panel): media path, resolution, duration, frame rate, codec info, metadata, color label, offline status, in/out points, and proxy status.",
571
+ parameters: {
572
+ type: "object",
573
+ properties: {
574
+ item_id: {
575
+ type: "string",
576
+ description: "Node ID or name of the project item",
577
+ },
578
+ },
579
+ required: ["item_id"],
580
+ },
581
+ handler: async (args) => {
582
+ const script = buildToolScript(`
583
+ var item = __findProjectItem("${escapeForExtendScript(args.item_id)}");
584
+ if (!item) return __error("Item not found: ${escapeForExtendScript(args.item_id)}");
585
+
586
+ var info = {
587
+ nodeId: item.nodeId,
588
+ name: item.name,
589
+ type: item.type === 1 ? "clip" : item.type === 2 ? "bin" : item.type === 3 ? "root" : item.type === 4 ? "file" : "unknown",
590
+ treePath: item.treePath
591
+ };
592
+
593
+ try { info.mediaPath = item.getMediaPath(); } catch(e) {}
594
+ try { info.offline = item.isOffline(); } catch(e) {}
595
+ try { info.colorLabel = item.getColorLabel(); } catch(e) {}
596
+ try { info.canChangeMediaPath = item.canChangeMediaPath(); } catch(e) {}
597
+
598
+ // Footage interpretation
599
+ try {
600
+ var interp = item.getFootageInterpretation();
601
+ if (interp) {
602
+ info.frameRate = interp.frameRate;
603
+ info.pixelAspectRatio = interp.pixelAspectRatio;
604
+ info.fieldType = interp.fieldType;
605
+ info.alphaUsage = interp.alphaUsage;
606
+ info.ignoreAlpha = interp.ignoreAlpha;
607
+ info.invertAlpha = interp.invertAlpha;
608
+ }
609
+ } catch(e) {}
610
+
611
+ // In/Out points
612
+ try { info.inPoint = __ticksToSeconds(item.getInPoint().ticks); } catch(e) {}
613
+ try { info.outPoint = __ticksToSeconds(item.getOutPoint().ticks); } catch(e) {}
614
+
615
+ // Proxy
616
+ try { info.hasProxy = item.hasProxy(); } catch(e) {}
617
+
618
+ // Project metadata (XMP)
619
+ try {
620
+ var xmp = item.getProjectMetadata();
621
+ if (xmp && xmp.length < 10000) info.projectMetadata = xmp;
622
+ else if (xmp) info.projectMetadataLength = xmp.length;
623
+ } catch(e) {}
624
+
625
+ // XMP metadata
626
+ try {
627
+ var xmp2 = item.getXMPMetadata();
628
+ if (xmp2 && xmp2.length < 10000) info.xmpMetadata = xmp2;
629
+ else if (xmp2) info.xmpMetadataLength = xmp2.length;
630
+ } catch(e) {}
631
+
632
+ // Item markers
633
+ info.markers = [];
634
+ try {
635
+ var m = item.getMarkers();
636
+ if (m) {
637
+ var marker = m.getFirstMarker();
638
+ while (marker) {
639
+ info.markers.push({
640
+ name: marker.name,
641
+ comments: marker.comments,
642
+ startSeconds: __ticksToSeconds(marker.start.ticks)
643
+ });
644
+ marker = m.getNextMarker(marker);
645
+ }
646
+ }
647
+ } catch(e) {}
648
+
649
+ // Child count for bins
650
+ if (item.type === 2) {
651
+ info.childCount = item.children.numItems;
652
+ }
653
+
654
+ return __result(info);
655
+ `);
656
+ return sendCommand(script, bridgeOptions);
657
+ },
658
+ },
659
+ search_project_items: {
660
+ description: "Search for project items by name, media file extension, offline status, or color label. Returns matching items with full details.",
661
+ parameters: {
662
+ type: "object",
663
+ properties: {
664
+ query: {
665
+ type: "string",
666
+ description: "Search query (matched against item name, case-insensitive substring match)",
667
+ },
668
+ extension: {
669
+ type: "string",
670
+ description: "Filter by file extension (e.g., 'mp4', 'wav', 'png'). Without dot.",
671
+ },
672
+ offline_only: {
673
+ type: "boolean",
674
+ description: "If true, only return offline/missing items",
675
+ },
676
+ color_label: {
677
+ type: "number",
678
+ description: "Filter by color label index (0-15)",
679
+ },
680
+ item_type: {
681
+ type: "string",
682
+ enum: ["clip", "bin", "all"],
683
+ description: "Filter by item type (default: all)",
684
+ },
685
+ max_results: {
686
+ type: "number",
687
+ description: "Maximum results to return (default: 100)",
688
+ },
689
+ },
690
+ },
691
+ handler: async (args) => {
692
+ const maxResults = args.max_results ?? 100;
693
+ const script = buildToolScript(`
694
+ var results = [];
695
+ var maxR = ${maxResults};
696
+ var query = ${args.query ? `"${escapeForExtendScript(args.query)}".toLowerCase()` : "null"};
697
+ var ext = ${args.extension ? `"${escapeForExtendScript(args.extension)}".toLowerCase()` : "null"};
698
+ var offlineOnly = ${args.offline_only ? "true" : "false"};
699
+ var colorFilter = ${args.color_label !== undefined ? args.color_label : "-1"};
700
+ var typeFilter = "${args.item_type || "all"}";
701
+
702
+ function searchBin(bin) {
703
+ if (results.length >= maxR) return;
704
+ for (var i = 0; i < bin.children.numItems; i++) {
705
+ if (results.length >= maxR) return;
706
+ var item = bin.children[i];
707
+
708
+ // Type filter
709
+ if (typeFilter === "clip" && item.type === 2) {
710
+ searchBin(item);
711
+ continue;
712
+ }
713
+ if (typeFilter === "bin" && item.type !== 2) continue;
714
+
715
+ var match = true;
716
+
717
+ // Name filter
718
+ if (query && item.name.toLowerCase().indexOf(query) === -1) match = false;
719
+
720
+ // Extension filter
721
+ if (match && ext) {
722
+ try {
723
+ var mp = item.getMediaPath();
724
+ if (!mp || mp.split(".").pop().toLowerCase() !== ext) match = false;
725
+ } catch(e) { match = false; }
726
+ }
727
+
728
+ // Offline filter
729
+ if (match && offlineOnly) {
730
+ try { if (!item.isOffline()) match = false; } catch(e) { match = false; }
731
+ }
732
+
733
+ // Color label filter
734
+ if (match && colorFilter >= 0) {
735
+ try { if (item.getColorLabel() !== colorFilter) match = false; } catch(e) { match = false; }
736
+ }
737
+
738
+ if (match) {
739
+ var entry = {
740
+ nodeId: item.nodeId,
741
+ name: item.name,
742
+ type: item.type === 1 ? "clip" : item.type === 2 ? "bin" : item.type === 4 ? "file" : "unknown",
743
+ treePath: item.treePath
744
+ };
745
+ try { entry.mediaPath = item.getMediaPath(); } catch(e) {}
746
+ try { entry.offline = item.isOffline(); } catch(e) {}
747
+ try { entry.colorLabel = item.getColorLabel(); } catch(e) {}
748
+ results.push(entry);
749
+ }
750
+
751
+ if (item.type === 2) searchBin(item);
752
+ }
753
+ }
754
+
755
+ searchBin(app.project.rootItem);
756
+ return __result({ resultCount: results.length, maxResults: maxR, items: results });
757
+ `);
758
+ return sendCommand(script, bridgeOptions);
759
+ },
760
+ },
761
+ get_timeline_gaps: {
762
+ description: "Find all gaps (empty spaces) on the timeline between clips. Useful for identifying where content is missing or where clips can be tightened.",
763
+ parameters: {
764
+ type: "object",
765
+ properties: {
766
+ sequence_id: {
767
+ type: "string",
768
+ description: "Sequence name or ID. Uses active sequence if omitted.",
769
+ },
770
+ track_type: {
771
+ type: "string",
772
+ enum: ["video", "audio", "both"],
773
+ description: "Which track type to analyze (default: both)",
774
+ },
775
+ min_gap_seconds: {
776
+ type: "number",
777
+ description: "Minimum gap duration in seconds to report (default: 0.04 = ~1 frame at 24fps)",
778
+ },
779
+ },
780
+ },
781
+ handler: async (args) => {
782
+ const seqLookup = args.sequence_id
783
+ ? `var seq = __findSequence("${escapeForExtendScript(args.sequence_id)}"); if (!seq) return __error("Sequence not found");`
784
+ : `var seq = app.project.activeSequence; if (!seq) return __error("No active sequence");`;
785
+ const minGap = args.min_gap_seconds ?? 0.04;
786
+ const trackType = args.track_type || "both";
787
+ const script = buildToolScript(`
788
+ ${seqLookup}
789
+
790
+ var gaps = [];
791
+ var minGapTicks = __secondsToTicks(${minGap});
792
+
793
+ function findGaps(tracks, type) {
794
+ for (var t = 0; t < tracks.numTracks; t++) {
795
+ var track = tracks[t];
796
+ if (track.clips.numItems === 0) continue;
797
+ var prevEnd = 0;
798
+ for (var c = 0; c < track.clips.numItems; c++) {
799
+ var clip = track.clips[c];
800
+ var startTicks = parseFloat(clip.start.ticks);
801
+ var endTicks = parseFloat(clip.end.ticks);
802
+ var gapTicks = startTicks - prevEnd;
803
+ if (gapTicks > minGapTicks) {
804
+ gaps.push({
805
+ trackType: type,
806
+ trackIndex: t,
807
+ trackName: track.name,
808
+ gapStartSeconds: __ticksToSeconds("" + prevEnd),
809
+ gapEndSeconds: __ticksToSeconds("" + startTicks),
810
+ gapDurationSeconds: __ticksToSeconds("" + gapTicks),
811
+ beforeClip: c > 0 ? track.clips[c - 1].name : null,
812
+ afterClip: clip.name
813
+ });
814
+ }
815
+ prevEnd = endTicks;
816
+ }
817
+ }
818
+ }
819
+
820
+ if ("${trackType}" !== "audio") findGaps(seq.videoTracks, "video");
821
+ if ("${trackType}" !== "video") findGaps(seq.audioTracks, "audio");
822
+
823
+ return __result({
824
+ sequenceName: seq.name,
825
+ gapCount: gaps.length,
826
+ minGapSeconds: ${minGap},
827
+ gaps: gaps
828
+ });
829
+ `);
830
+ return sendCommand(script, bridgeOptions);
831
+ },
832
+ },
833
+ get_timeline_summary: {
834
+ description: "Get a human-readable summary of the timeline: total duration, clip count per track, total gaps, coverage percentage, used media files, effect usage, and marker overview. Great for a quick understanding of sequence state.",
835
+ parameters: {
836
+ type: "object",
837
+ properties: {
838
+ sequence_id: {
839
+ type: "string",
840
+ description: "Sequence name or ID. Uses active sequence if omitted.",
841
+ },
842
+ },
843
+ },
844
+ handler: async (args) => {
845
+ const seqLookup = args.sequence_id
846
+ ? `var seq = __findSequence("${escapeForExtendScript(args.sequence_id)}"); if (!seq) return __error("Sequence not found");`
847
+ : `var seq = app.project.activeSequence; if (!seq) return __error("No active sequence");`;
848
+ const script = buildToolScript(`
849
+ ${seqLookup}
850
+
851
+ var summary = {
852
+ name: seq.name,
853
+ id: seq.sequenceID,
854
+ resolution: seq.frameSizeHorizontal + "x" + seq.frameSizeVertical,
855
+ durationSeconds: __ticksToSeconds(seq.end)
856
+ };
857
+
858
+ try {
859
+ var s = seq.getSettings();
860
+ if (s) summary.frameRate = s.videoFrameRate;
861
+ } catch(e) {}
862
+
863
+ // Playhead
864
+ try { summary.playheadSeconds = __ticksToSeconds(seq.getPlayerPosition().ticks); } catch(e) {}
865
+
866
+ var totalVideoClips = 0;
867
+ var totalAudioClips = 0;
868
+ var usedMedia = {};
869
+ var effectUsage = {};
870
+ var disabledClips = 0;
871
+ var totalVideoFilled = 0;
872
+ var seqEndTicks = parseFloat(seq.end);
873
+
874
+ // Video tracks
875
+ var videoTrackSummary = [];
876
+ for (var t = 0; t < seq.videoTracks.numTracks; t++) {
877
+ var track = seq.videoTracks[t];
878
+ var trackFilled = 0;
879
+ for (var c = 0; c < track.clips.numItems; c++) {
880
+ totalVideoClips++;
881
+ var clip = track.clips[c];
882
+ trackFilled += parseFloat(clip.end.ticks) - parseFloat(clip.start.ticks);
883
+ try { if (clip.isDisabled()) disabledClips++; } catch(e) {}
884
+ try {
885
+ if (clip.projectItem) {
886
+ var srcName = clip.projectItem.name;
887
+ if (!usedMedia[srcName]) usedMedia[srcName] = 0;
888
+ usedMedia[srcName]++;
889
+ }
890
+ } catch(e) {}
891
+ try {
892
+ for (var e = 0; e < clip.components.numItems; e++) {
893
+ var eName = clip.components[e].displayName;
894
+ if (!effectUsage[eName]) effectUsage[eName] = 0;
895
+ effectUsage[eName]++;
896
+ }
897
+ } catch(e) {}
898
+ }
899
+ if (t === 0) totalVideoFilled = trackFilled;
900
+ videoTrackSummary.push({
901
+ index: t,
902
+ name: track.name,
903
+ clipCount: track.clips.numItems,
904
+ muted: track.isMuted(),
905
+ locked: track.isLocked()
906
+ });
907
+ }
908
+
909
+ // Audio tracks
910
+ var audioTrackSummary = [];
911
+ for (var t = 0; t < seq.audioTracks.numTracks; t++) {
912
+ var track = seq.audioTracks[t];
913
+ for (var c = 0; c < track.clips.numItems; c++) {
914
+ totalAudioClips++;
915
+ var aClip = track.clips[c];
916
+ try {
917
+ if (aClip.projectItem) {
918
+ var srcName = aClip.projectItem.name;
919
+ if (!usedMedia[srcName]) usedMedia[srcName] = 0;
920
+ usedMedia[srcName]++;
921
+ }
922
+ } catch(e) {}
923
+ }
924
+ audioTrackSummary.push({
925
+ index: t,
926
+ name: track.name,
927
+ clipCount: track.clips.numItems,
928
+ muted: track.isMuted(),
929
+ locked: track.isLocked()
930
+ });
931
+ }
932
+
933
+ summary.totalVideoClips = totalVideoClips;
934
+ summary.totalAudioClips = totalAudioClips;
935
+ summary.totalClips = totalVideoClips + totalAudioClips;
936
+ summary.disabledClips = disabledClips;
937
+ summary.videoTrackCount = seq.videoTracks.numTracks;
938
+ summary.audioTrackCount = seq.audioTracks.numTracks;
939
+ summary.videoTracks = videoTrackSummary;
940
+ summary.audioTracks = audioTrackSummary;
941
+
942
+ // V1 coverage
943
+ if (seqEndTicks > 0) {
944
+ summary.v1CoveragePercent = Math.round((totalVideoFilled / seqEndTicks) * 10000) / 100;
945
+ }
946
+
947
+ // Used media
948
+ var mediaList = [];
949
+ for (var name in usedMedia) {
950
+ if (usedMedia.hasOwnProperty(name)) {
951
+ mediaList.push({ name: name, useCount: usedMedia[name] });
952
+ }
953
+ }
954
+ summary.usedMedia = mediaList;
955
+ summary.uniqueMediaCount = mediaList.length;
956
+
957
+ // Effect usage
958
+ var effectList = [];
959
+ for (var name in effectUsage) {
960
+ if (effectUsage.hasOwnProperty(name)) {
961
+ effectList.push({ name: name, count: effectUsage[name] });
962
+ }
963
+ }
964
+ summary.effectUsage = effectList;
965
+
966
+ // Markers
967
+ var markerCount = 0;
968
+ try {
969
+ var m = seq.markers.getFirstMarker();
970
+ while (m) { markerCount++; m = seq.markers.getNextMarker(m); }
971
+ } catch(e) {}
972
+ summary.markerCount = markerCount;
973
+
974
+ return __result(summary);
975
+ `);
976
+ return sendCommand(script, bridgeOptions);
977
+ },
978
+ },
979
+ get_offline_media: {
980
+ description: "Find all offline/missing media in the project with their expected file paths. Essential for diagnosing broken links.",
981
+ parameters: {},
982
+ handler: async () => {
983
+ const script = buildToolScript(`
984
+ var offline = [];
985
+ function scan(bin) {
986
+ for (var i = 0; i < bin.children.numItems; i++) {
987
+ var item = bin.children[i];
988
+ try {
989
+ if (item.isOffline()) {
990
+ var entry = {
991
+ nodeId: item.nodeId,
992
+ name: item.name,
993
+ treePath: item.treePath
994
+ };
995
+ try { entry.mediaPath = item.getMediaPath(); } catch(e) {}
996
+ try { entry.colorLabel = item.getColorLabel(); } catch(e) {}
997
+ offline.push(entry);
998
+ }
999
+ } catch(e) {}
1000
+ if (item.type === 2) scan(item);
1001
+ }
1002
+ }
1003
+ scan(app.project.rootItem);
1004
+ return __result({ offlineCount: offline.length, items: offline });
1005
+ `);
1006
+ return sendCommand(script, bridgeOptions);
1007
+ },
1008
+ },
1009
+ get_used_media_report: {
1010
+ description: "Get a report of all media files used in a sequence: which source files are used, how many times each appears, on which tracks, and whether any sources are offline.",
1011
+ parameters: {
1012
+ type: "object",
1013
+ properties: {
1014
+ sequence_id: {
1015
+ type: "string",
1016
+ description: "Sequence name or ID. Uses active sequence if omitted.",
1017
+ },
1018
+ },
1019
+ },
1020
+ handler: async (args) => {
1021
+ const seqLookup = args.sequence_id
1022
+ ? `var seq = __findSequence("${escapeForExtendScript(args.sequence_id)}"); if (!seq) return __error("Sequence not found");`
1023
+ : `var seq = app.project.activeSequence; if (!seq) return __error("No active sequence");`;
1024
+ const script = buildToolScript(`
1025
+ ${seqLookup}
1026
+
1027
+ var mediaMap = {};
1028
+
1029
+ function scanTracks(tracks, trackType) {
1030
+ for (var t = 0; t < tracks.numTracks; t++) {
1031
+ var track = tracks[t];
1032
+ for (var c = 0; c < track.clips.numItems; c++) {
1033
+ var clip = track.clips[c];
1034
+ try {
1035
+ var src = clip.projectItem;
1036
+ if (!src) continue;
1037
+ var key = src.nodeId;
1038
+ if (!mediaMap[key]) {
1039
+ mediaMap[key] = {
1040
+ nodeId: src.nodeId,
1041
+ name: src.name,
1042
+ mediaPath: "",
1043
+ offline: false,
1044
+ useCount: 0,
1045
+ tracks: []
1046
+ };
1047
+ try { mediaMap[key].mediaPath = src.getMediaPath(); } catch(e) {}
1048
+ try { mediaMap[key].offline = src.isOffline(); } catch(e) {}
1049
+ }
1050
+ mediaMap[key].useCount++;
1051
+ mediaMap[key].tracks.push(trackType + " " + t);
1052
+ } catch(e) {}
1053
+ }
1054
+ }
1055
+ }
1056
+
1057
+ scanTracks(seq.videoTracks, "V");
1058
+ scanTracks(seq.audioTracks, "A");
1059
+
1060
+ var report = [];
1061
+ var offlineUsed = 0;
1062
+ for (var key in mediaMap) {
1063
+ if (mediaMap.hasOwnProperty(key)) {
1064
+ // Deduplicate track list
1065
+ var unique = {};
1066
+ for (var i = 0; i < mediaMap[key].tracks.length; i++) {
1067
+ unique[mediaMap[key].tracks[i]] = true;
1068
+ }
1069
+ var trackList = [];
1070
+ for (var k in unique) { if (unique.hasOwnProperty(k)) trackList.push(k); }
1071
+ mediaMap[key].tracks = trackList;
1072
+ if (mediaMap[key].offline) offlineUsed++;
1073
+ report.push(mediaMap[key]);
1074
+ }
1075
+ }
1076
+
1077
+ return __result({
1078
+ sequenceName: seq.name,
1079
+ uniqueMediaCount: report.length,
1080
+ offlineUsedCount: offlineUsed,
1081
+ media: report
1082
+ });
1083
+ `);
1084
+ return sendCommand(script, bridgeOptions);
1085
+ },
1086
+ },
1087
+ };
1088
+ }
1089
+ //# sourceMappingURL=inspection.js.map