premiere-cli 0.2.0__py3-none-any.whl

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 (247) hide show
  1. premiere_cli/__init__.py +11 -0
  2. premiere_cli/cli.py +3664 -0
  3. premiere_cli/log.py +66 -0
  4. premiere_cli/panel/.debug +8 -0
  5. premiere_cli/panel/CSXS/manifest.xml +52 -0
  6. premiere_cli/panel/css/style.css +53 -0
  7. premiere_cli/panel/host/commands/add-adjustment-layer.jsx +190 -0
  8. premiere_cli/panel/host/commands/add-audio-keyframes.jsx +176 -0
  9. premiere_cli/panel/host/commands/add-custom-metadata-field.jsx +63 -0
  10. premiere_cli/panel/host/commands/add-keyframe.jsx +181 -0
  11. premiere_cli/panel/host/commands/add-marker-to-project-item.jsx +194 -0
  12. premiere_cli/panel/host/commands/add-marker.jsx +129 -0
  13. premiere_cli/panel/host/commands/add-text-overlay.jsx +101 -0
  14. premiere_cli/panel/host/commands/add-to-render-queue.jsx +120 -0
  15. premiere_cli/panel/host/commands/add-to-timeline.jsx +300 -0
  16. premiere_cli/panel/host/commands/add-track.jsx +188 -0
  17. premiere_cli/panel/host/commands/add-transition.jsx +134 -0
  18. premiere_cli/panel/host/commands/adjust-audio-levels.jsx +132 -0
  19. premiere_cli/panel/host/commands/apply-audio-effect.jsx +118 -0
  20. premiere_cli/panel/host/commands/apply-effect.jsx +122 -0
  21. premiere_cli/panel/host/commands/apply-lut.jsx +107 -0
  22. premiere_cli/panel/host/commands/attach-custom-property.jsx +66 -0
  23. premiere_cli/panel/host/commands/auto-reframe-sequence.jsx +124 -0
  24. premiere_cli/panel/host/commands/batch-add-transitions.jsx +158 -0
  25. premiere_cli/panel/host/commands/batch-apply-effect.jsx +182 -0
  26. premiere_cli/panel/host/commands/batch-rename-clips.jsx +170 -0
  27. premiere_cli/panel/host/commands/batch-set-clips-enabled.jsx +145 -0
  28. premiere_cli/panel/host/commands/clear-item-in-out.jsx +117 -0
  29. premiere_cli/panel/host/commands/clear-sequence-in-out.jsx +100 -0
  30. premiere_cli/panel/host/commands/close-all-source-clips.jsx +32 -0
  31. premiere_cli/panel/host/commands/close-sequence.jsx +80 -0
  32. premiere_cli/panel/host/commands/close-source-monitor.jsx +32 -0
  33. premiere_cli/panel/host/commands/color-correct.jsx +164 -0
  34. premiere_cli/panel/host/commands/copy-effect-values.jsx +183 -0
  35. premiere_cli/panel/host/commands/copy-effects-between-clips.jsx +234 -0
  36. premiere_cli/panel/host/commands/create-bars-and-tone.jsx +152 -0
  37. premiere_cli/panel/host/commands/create-bin.jsx +76 -0
  38. premiere_cli/panel/host/commands/create-caption-track.jsx +159 -0
  39. premiere_cli/panel/host/commands/create-sequence-from-clips.jsx +170 -0
  40. premiere_cli/panel/host/commands/create-sequence-from-preset.jsx +90 -0
  41. premiere_cli/panel/host/commands/create-sequence.jsx +166 -0
  42. premiere_cli/panel/host/commands/create-smart-bin.jsx +80 -0
  43. premiere_cli/panel/host/commands/create-subclip.jsx +248 -0
  44. premiere_cli/panel/host/commands/create-subsequence.jsx +136 -0
  45. premiere_cli/panel/host/commands/debug-qe-inspect.jsx +248 -0
  46. premiere_cli/panel/host/commands/debug-qe-try-mutate.jsx +305 -0
  47. premiere_cli/panel/host/commands/delete-marker.jsx +178 -0
  48. premiere_cli/panel/host/commands/deselect-all-clips.jsx +98 -0
  49. premiere_cli/panel/host/commands/detach-proxy.jsx +116 -0
  50. premiere_cli/panel/host/commands/duplicate-clip.jsx +140 -0
  51. premiere_cli/panel/host/commands/duplicate-sequence.jsx +150 -0
  52. premiere_cli/panel/host/commands/encode-file.jsx +86 -0
  53. premiere_cli/panel/host/commands/encode-project-item.jsx +145 -0
  54. premiere_cli/panel/host/commands/export-aaf.jsx +77 -0
  55. premiere_cli/panel/host/commands/export-fcp-xml.jsx +65 -0
  56. premiere_cli/panel/host/commands/export-frame.jsx +381 -0
  57. premiere_cli/panel/host/commands/export-omf.jsx +95 -0
  58. premiere_cli/panel/host/commands/export-sequence-as-project.jsx +65 -0
  59. premiere_cli/panel/host/commands/export-sequence.jsx +100 -0
  60. premiere_cli/panel/host/commands/extract-audio-track.jsx +181 -0
  61. premiere_cli/panel/host/commands/extract-selection.jsx +113 -0
  62. premiere_cli/panel/host/commands/find-items-by-media-path.jsx +112 -0
  63. premiere_cli/panel/host/commands/freeze-frame.jsx +215 -0
  64. premiere_cli/panel/host/commands/get-active-sequence.jsx +103 -0
  65. premiere_cli/panel/host/commands/get-all-project-paths.jsx +72 -0
  66. premiere_cli/panel/host/commands/get-bin-contents.jsx +176 -0
  67. premiere_cli/panel/host/commands/get-clip-adjustment-layer.jsx +83 -0
  68. premiere_cli/panel/host/commands/get-clip-at-playhead.jsx +99 -0
  69. premiere_cli/panel/host/commands/get-clip-at-position.jsx +120 -0
  70. premiere_cli/panel/host/commands/get-clip-links.jsx +135 -0
  71. premiere_cli/panel/host/commands/get-clip-markers.jsx +144 -0
  72. premiere_cli/panel/host/commands/get-clip-speed.jsx +69 -0
  73. premiere_cli/panel/host/commands/get-color-label.jsx +89 -0
  74. premiere_cli/panel/host/commands/get-color-space.jsx +99 -0
  75. premiere_cli/panel/host/commands/get-duplicate-media.jsx +78 -0
  76. premiere_cli/panel/host/commands/get-effect-properties.jsx +129 -0
  77. premiere_cli/panel/host/commands/get-encoder-presets.jsx +131 -0
  78. premiere_cli/panel/host/commands/get-export-file-extension.jsx +56 -0
  79. premiere_cli/panel/host/commands/get-footage-interpretation.jsx +113 -0
  80. premiere_cli/panel/host/commands/get-full-clip-info.jsx +162 -0
  81. premiere_cli/panel/host/commands/get-full-project-overview.jsx +193 -0
  82. premiere_cli/panel/host/commands/get-full-sequence-info.jsx +184 -0
  83. premiere_cli/panel/host/commands/get-graphics-white-luminance.jsx +31 -0
  84. premiere_cli/panel/host/commands/get-insertion-bin.jsx +35 -0
  85. premiere_cli/panel/host/commands/get-item-metadata.jsx +94 -0
  86. premiere_cli/panel/host/commands/get-keyframes.jsx +158 -0
  87. premiere_cli/panel/host/commands/get-mogrt-component.jsx +111 -0
  88. premiere_cli/panel/host/commands/get-next-edit-point.jsx +131 -0
  89. premiere_cli/panel/host/commands/get-offline-media.jsx +67 -0
  90. premiere_cli/panel/host/commands/get-open-projects.jsx +50 -0
  91. premiere_cli/panel/host/commands/get-premiere-state.jsx +164 -0
  92. premiere_cli/panel/host/commands/get-project-info.jsx +63 -0
  93. premiere_cli/panel/host/commands/get-project-item-info.jsx +247 -0
  94. premiere_cli/panel/host/commands/get-project-panel-metadata.jsx +30 -0
  95. premiere_cli/panel/host/commands/get-qe-clip-info.jsx +198 -0
  96. premiere_cli/panel/host/commands/get-render-queue-status.jsx +35 -0
  97. premiere_cli/panel/host/commands/get-sequence-count.jsx +24 -0
  98. premiere_cli/panel/host/commands/get-sequence-in-out.jsx +76 -0
  99. premiere_cli/panel/host/commands/get-sequence-markers-by-type.jsx +89 -0
  100. premiere_cli/panel/host/commands/get-source-monitor-info.jsx +34 -0
  101. premiere_cli/panel/host/commands/get-source-monitor-position.jsx +30 -0
  102. premiere_cli/panel/host/commands/get-target-tracks.jsx +76 -0
  103. premiere_cli/panel/host/commands/get-timeline-gaps.jsx +124 -0
  104. premiere_cli/panel/host/commands/get-timeline-summary.jsx +154 -0
  105. premiere_cli/panel/host/commands/get-total-clip-count.jsx +67 -0
  106. premiere_cli/panel/host/commands/get-track-info.jsx +117 -0
  107. premiere_cli/panel/host/commands/get-unused-media.jsx +99 -0
  108. premiere_cli/panel/host/commands/get-used-media-report.jsx +125 -0
  109. premiere_cli/panel/host/commands/get-value-at-time.jsx +135 -0
  110. premiere_cli/panel/host/commands/get-version-info.jsx +33 -0
  111. premiere_cli/panel/host/commands/get-work-area.jsx +52 -0
  112. premiere_cli/panel/host/commands/get-workspaces.jsx +29 -0
  113. premiere_cli/panel/host/commands/get-xmp-metadata.jsx +107 -0
  114. premiere_cli/panel/host/commands/import-ae-comps.jsx +129 -0
  115. premiere_cli/panel/host/commands/import-fcp-xml.jsx +103 -0
  116. premiere_cli/panel/host/commands/import-folder.jsx +193 -0
  117. premiere_cli/panel/host/commands/import-image-sequence.jsx +171 -0
  118. premiere_cli/panel/host/commands/import-media.jsx +217 -0
  119. premiere_cli/panel/host/commands/import-mogrt-from-library.jsx +105 -0
  120. premiere_cli/panel/host/commands/import-mogrt.jsx +111 -0
  121. premiere_cli/panel/host/commands/import-sequences-from-project.jsx +80 -0
  122. premiere_cli/panel/host/commands/insert-from-source.jsx +162 -0
  123. premiere_cli/panel/host/commands/inspect-dom-object.jsx +162 -0
  124. premiere_cli/panel/host/commands/invert-selection.jsx +114 -0
  125. premiere_cli/panel/host/commands/is-work-area-enabled.jsx +88 -0
  126. premiere_cli/panel/host/commands/lift-selection.jsx +106 -0
  127. premiere_cli/panel/host/commands/link-selection.jsx +111 -0
  128. premiere_cli/panel/host/commands/list-available-audio-effects.jsx +33 -0
  129. premiere_cli/panel/host/commands/list-available-audio-transitions.jsx +35 -0
  130. premiere_cli/panel/host/commands/list-available-effects.jsx +33 -0
  131. premiere_cli/panel/host/commands/list-available-transitions.jsx +74 -0
  132. premiere_cli/panel/host/commands/list-markers.jsx +69 -0
  133. premiere_cli/panel/host/commands/list-project-items.jsx +105 -0
  134. premiere_cli/panel/host/commands/lock-track.jsx +114 -0
  135. premiere_cli/panel/host/commands/manage-proxies.jsx +176 -0
  136. premiere_cli/panel/host/commands/match-frame.jsx +141 -0
  137. premiere_cli/panel/host/commands/move-clip-to-track.jsx +220 -0
  138. premiere_cli/panel/host/commands/move-clip.jsx +97 -0
  139. premiere_cli/panel/host/commands/move-items-to-bin.jsx +194 -0
  140. premiere_cli/panel/host/commands/move-playhead-to-edit.jsx +174 -0
  141. premiere_cli/panel/host/commands/move-playhead.jsx +137 -0
  142. premiere_cli/panel/host/commands/nest-clips.jsx +179 -0
  143. premiere_cli/panel/host/commands/open-in-source.jsx +117 -0
  144. premiere_cli/panel/host/commands/open-project.jsx +61 -0
  145. premiere_cli/panel/host/commands/overwrite-clip-at.jsx +350 -0
  146. premiere_cli/panel/host/commands/overwrite-from-source.jsx +150 -0
  147. premiere_cli/panel/host/commands/play-source-monitor.jsx +36 -0
  148. premiere_cli/panel/host/commands/play-timeline.jsx +24 -0
  149. premiere_cli/panel/host/commands/razor-all-tracks.jsx +171 -0
  150. premiere_cli/panel/host/commands/redo.jsx +69 -0
  151. premiere_cli/panel/host/commands/refresh-media.jsx +101 -0
  152. premiere_cli/panel/host/commands/relink-media.jsx +132 -0
  153. premiere_cli/panel/host/commands/remove-all-effects.jsx +114 -0
  154. premiere_cli/panel/host/commands/remove-effect-by-name.jsx +143 -0
  155. premiere_cli/panel/host/commands/remove-effect.jsx +122 -0
  156. premiere_cli/panel/host/commands/remove-from-timeline.jsx +93 -0
  157. premiere_cli/panel/host/commands/remove-keyframe-range.jsx +173 -0
  158. premiere_cli/panel/host/commands/remove-keyframe.jsx +192 -0
  159. premiere_cli/panel/host/commands/remove-selected-clips.jsx +126 -0
  160. premiere_cli/panel/host/commands/remove-track-intervals.jsx +254 -0
  161. premiere_cli/panel/host/commands/remove-transition.jsx +164 -0
  162. premiere_cli/panel/host/commands/rename-bin.jsx +88 -0
  163. premiere_cli/panel/host/commands/rename-clip.jsx +127 -0
  164. premiere_cli/panel/host/commands/rename-project-item.jsx +111 -0
  165. premiere_cli/panel/host/commands/rename-track.jsx +84 -0
  166. premiere_cli/panel/host/commands/replace-clip-media.jsx +140 -0
  167. premiere_cli/panel/host/commands/replace-clip.jsx +188 -0
  168. premiere_cli/panel/host/commands/reverse-clip.jsx +187 -0
  169. premiere_cli/panel/host/commands/ripple-delete-clip.jsx +156 -0
  170. premiere_cli/panel/host/commands/roll-edit.jsx +162 -0
  171. premiere_cli/panel/host/commands/save-project-as.jsx +55 -0
  172. premiere_cli/panel/host/commands/save-project.jsx +57 -0
  173. premiere_cli/panel/host/commands/scene-edit-detection.jsx +128 -0
  174. premiere_cli/panel/host/commands/search-project-items.jsx +180 -0
  175. premiere_cli/panel/host/commands/select-all-clips.jsx +107 -0
  176. premiere_cli/panel/host/commands/select-clips-by-color.jsx +142 -0
  177. premiere_cli/panel/host/commands/select-clips-by-name.jsx +133 -0
  178. premiere_cli/panel/host/commands/select-clips-in-range.jsx +133 -0
  179. premiere_cli/panel/host/commands/select-disabled-clips.jsx +127 -0
  180. premiere_cli/panel/host/commands/select-project-item.jsx +115 -0
  181. premiere_cli/panel/host/commands/set-active-project.jsx +184 -0
  182. premiere_cli/panel/host/commands/set-active-sequence.jsx +87 -0
  183. premiere_cli/panel/host/commands/set-all-tracks-targeted.jsx +112 -0
  184. premiere_cli/panel/host/commands/set-anti-alias-quality.jsx +125 -0
  185. premiere_cli/panel/host/commands/set-blend-mode.jsx +111 -0
  186. premiere_cli/panel/host/commands/set-clip-anchor-point.jsx +102 -0
  187. premiere_cli/panel/host/commands/set-clip-enabled.jsx +127 -0
  188. premiere_cli/panel/host/commands/set-clip-opacity.jsx +102 -0
  189. premiere_cli/panel/host/commands/set-clip-pan.jsx +107 -0
  190. premiere_cli/panel/host/commands/set-clip-position.jsx +103 -0
  191. premiere_cli/panel/host/commands/set-clip-properties.jsx +221 -0
  192. premiere_cli/panel/host/commands/set-clip-rotation.jsx +102 -0
  193. premiere_cli/panel/host/commands/set-clip-scale.jsx +102 -0
  194. premiere_cli/panel/host/commands/set-clip-selection.jsx +144 -0
  195. premiere_cli/panel/host/commands/set-clip-speed.jsx +160 -0
  196. premiere_cli/panel/host/commands/set-clip-volume.jsx +128 -0
  197. premiere_cli/panel/host/commands/set-color-label.jsx +123 -0
  198. premiere_cli/panel/host/commands/set-color-value.jsx +155 -0
  199. premiere_cli/panel/host/commands/set-effect-property.jsx +124 -0
  200. premiere_cli/panel/host/commands/set-footage-interpretation.jsx +173 -0
  201. premiere_cli/panel/host/commands/set-frame-blend.jsx +145 -0
  202. premiere_cli/panel/host/commands/set-graphics-white-luminance.jsx +73 -0
  203. premiere_cli/panel/host/commands/set-item-in-out.jsx +180 -0
  204. premiere_cli/panel/host/commands/set-item-metadata.jsx +134 -0
  205. premiere_cli/panel/host/commands/set-item-offline.jsx +109 -0
  206. premiere_cli/panel/host/commands/set-item-start-time.jsx +139 -0
  207. premiere_cli/panel/host/commands/set-keyframe-interpolation.jsx +186 -0
  208. premiere_cli/panel/host/commands/set-override-frame-rate.jsx +123 -0
  209. premiere_cli/panel/host/commands/set-override-pixel-aspect-ratio.jsx +129 -0
  210. premiere_cli/panel/host/commands/set-poster-frame.jsx +192 -0
  211. premiere_cli/panel/host/commands/set-project-panel-metadata.jsx +69 -0
  212. premiere_cli/panel/host/commands/set-scale-to-frame-size.jsx +101 -0
  213. premiere_cli/panel/host/commands/set-scale-width-height.jsx +140 -0
  214. premiere_cli/panel/host/commands/set-sequence-in-out.jsx +65 -0
  215. premiere_cli/panel/host/commands/set-sequence-settings.jsx +184 -0
  216. premiere_cli/panel/host/commands/set-source-in-out.jsx +98 -0
  217. premiere_cli/panel/host/commands/set-target-track.jsx +91 -0
  218. premiere_cli/panel/host/commands/set-time-interpolation.jsx +141 -0
  219. premiere_cli/panel/host/commands/set-track-mute.jsx +91 -0
  220. premiere_cli/panel/host/commands/set-track-visibility.jsx +94 -0
  221. premiere_cli/panel/host/commands/set-transcode-on-ingest.jsx +78 -0
  222. premiere_cli/panel/host/commands/set-uniform-scale.jsx +104 -0
  223. premiere_cli/panel/host/commands/set-work-area.jsx +76 -0
  224. premiere_cli/panel/host/commands/set-workspace.jsx +35 -0
  225. premiere_cli/panel/host/commands/set-xmp-metadata.jsx +157 -0
  226. premiere_cli/panel/host/commands/slide-edit.jsx +162 -0
  227. premiere_cli/panel/host/commands/slip-edit.jsx +167 -0
  228. premiere_cli/panel/host/commands/split-clip.jsx +144 -0
  229. premiere_cli/panel/host/commands/stabilize-clip.jsx +139 -0
  230. premiere_cli/panel/host/commands/stop-playback.jsx +23 -0
  231. premiere_cli/panel/host/commands/trim-clip.jsx +111 -0
  232. premiere_cli/panel/host/commands/undo.jsx +114 -0
  233. premiere_cli/panel/host/commands/unlink-selection.jsx +106 -0
  234. premiere_cli/panel/host/commands/unnest-sequence.jsx +207 -0
  235. premiere_cli/panel/host/commands/update-marker.jsx +120 -0
  236. premiere_cli/panel/host/index.jsx +1227 -0
  237. premiere_cli/panel/host/presets/default-4k-25fps.sqpreset +63 -0
  238. premiere_cli/panel/index.html +16 -0
  239. premiere_cli/panel/js/cs-interface.js +11 -0
  240. premiere_cli/panel/js/main.js +409 -0
  241. premiere_cli/panel_install.py +220 -0
  242. premiere_cli-0.2.0.dist-info/METADATA +127 -0
  243. premiere_cli-0.2.0.dist-info/RECORD +247 -0
  244. premiere_cli-0.2.0.dist-info/WHEEL +5 -0
  245. premiere_cli-0.2.0.dist-info/entry_points.txt +3 -0
  246. premiere_cli-0.2.0.dist-info/licenses/LICENSE +428 -0
  247. premiere_cli-0.2.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,140 @@
1
+ // Command: replace-clip-media → ppb_replaceClipMedia
2
+ // Loaded lazily by ppb_dispatch (host/index.jsx) on first use.
3
+ //
4
+ // Named after leancoderkavy's premiere-pro-mcp clipboard.ts
5
+ // replace_clip_media, but DELIBERATELY implemented differently: that
6
+ // reference tool's replace_clip_media is actually just a timeline
7
+ // seq.overwriteClip() at a clip's start time with a different project
8
+ // item — functionally identical to this panel's own pre-existing
9
+ // replace-clip command (host/commands/replace-clip.jsx), which already
10
+ // covers that behavior. Porting it again under a new name would just
11
+ // duplicate replace-clip.
12
+ //
13
+ // Instead this command does what its name actually promises — replaces a
14
+ // PROJECT ITEM's underlying media FILE via the item-level API
15
+ // PREMIERE_API_NOTES.md documents: item.canChangeMediaPath() /
16
+ // item.changeMediaPath(newPath, /*overrideChecks*/true). This swaps the
17
+ // source file for every clip referencing that project item throughout the
18
+ // whole project (not just one timeline clip), which is a materially
19
+ // different and more powerful operation than replace-clip's per-clip
20
+ // swap.
21
+ //
22
+ // Destructive-ish: undo is non-functional on this build (see the `undo`
23
+ // command's notes) — previousMediaPath is the only path to manually
24
+ // revert via a second call.
25
+ function ppbFindItemReplaceClipMedia_walk(item, args, depth) {
26
+ if (depth > 32) {
27
+ return null;
28
+ }
29
+ var isBin = false;
30
+ try {
31
+ isBin = typeof ProjectItemType !== "undefined" && item.type === ProjectItemType.BIN;
32
+ } catch (e) {
33
+ isBin = false;
34
+ }
35
+ var matched = false;
36
+ if (args.nodeId !== null) {
37
+ try { matched = item.nodeId === args.nodeId; } catch (e) { matched = false; }
38
+ } else if (args.name !== null) {
39
+ try { matched = item.name === args.name; } catch (e) { matched = false; }
40
+ }
41
+ if (matched) {
42
+ return item;
43
+ }
44
+ if (isBin && item.children) {
45
+ for (var i = 0; i < item.children.numItems; i++) {
46
+ var found = ppbFindItemReplaceClipMedia_walk(item.children[i], args, depth + 1);
47
+ if (found) {
48
+ return found;
49
+ }
50
+ }
51
+ }
52
+ return null;
53
+ }
54
+
55
+ function ppbFindItemReplaceClipMedia_resolve(args) {
56
+ var root = app.project.rootItem;
57
+ for (var i = 0; i < root.children.numItems; i++) {
58
+ var found = ppbFindItemReplaceClipMedia_walk(root.children[i], args, 1);
59
+ if (found) {
60
+ return found;
61
+ }
62
+ }
63
+ return null;
64
+ }
65
+
66
+ function ppb_replaceClipMedia(argsJson) {
67
+ try {
68
+ var args;
69
+ try {
70
+ args = JSON.parse(argsJson);
71
+ } catch (e) {
72
+ return JSON.stringify({ ok: false, error: "invalid args JSON: " + e.toString() });
73
+ }
74
+
75
+ var hasNodeId = typeof args.nodeId === "string" && args.nodeId.length > 0;
76
+ var hasName = typeof args.name === "string" && args.name.length > 0;
77
+ if (!hasNodeId && !hasName) {
78
+ return JSON.stringify({ ok: false, error: "either nodeId or name is required to identify the project item" });
79
+ }
80
+ if (typeof args.newMediaPath !== "string" || !args.newMediaPath) {
81
+ return JSON.stringify({ ok: false, error: "newMediaPath is required" });
82
+ }
83
+
84
+ if (!app.project) {
85
+ return JSON.stringify({ ok: false, error: "no project open" });
86
+ }
87
+
88
+ var item = ppbFindItemReplaceClipMedia_resolve({
89
+ nodeId: hasNodeId ? args.nodeId : null,
90
+ name: hasNodeId ? null : args.name
91
+ });
92
+ if (!item) {
93
+ return JSON.stringify({ ok: false, error: "no project item found matching the given nodeId/name" });
94
+ }
95
+
96
+ var itemName = null;
97
+ var itemNodeId = null;
98
+ try { itemName = item.name; } catch (e2) { itemName = null; }
99
+ try { itemNodeId = item.nodeId; } catch (e3) { itemNodeId = null; }
100
+
101
+ var canChange = null;
102
+ try {
103
+ canChange = item.canChangeMediaPath();
104
+ } catch (e4) {
105
+ canChange = null;
106
+ }
107
+ if (canChange === false) {
108
+ return JSON.stringify({ ok: false, error: "item.canChangeMediaPath() returned false — this item's media path cannot be changed", name: itemName, nodeId: itemNodeId });
109
+ }
110
+
111
+ var previousMediaPath = null;
112
+ try { previousMediaPath = item.getMediaPath(); } catch (e5) { previousMediaPath = null; }
113
+
114
+ try {
115
+ item.changeMediaPath(args.newMediaPath, true);
116
+ } catch (e6) {
117
+ return JSON.stringify({ ok: false, error: "item.changeMediaPath() failed: " + e6.toString(), name: itemName, nodeId: itemNodeId, previousMediaPath: previousMediaPath });
118
+ }
119
+
120
+ var newMediaPath = null;
121
+ try { newMediaPath = item.getMediaPath(); } catch (e7) { newMediaPath = null; }
122
+
123
+ var verified = newMediaPath !== null && newMediaPath === args.newMediaPath;
124
+
125
+ return JSON.stringify({
126
+ ok: true,
127
+ result: {
128
+ name: itemName,
129
+ nodeId: itemNodeId,
130
+ canChangeMediaPath: canChange,
131
+ previousMediaPath: previousMediaPath,
132
+ requestedMediaPath: args.newMediaPath,
133
+ newMediaPath: newMediaPath,
134
+ verified: verified
135
+ }
136
+ });
137
+ } catch (e) {
138
+ return JSON.stringify({ ok: false, error: e.toString() });
139
+ }
140
+ }
@@ -0,0 +1,188 @@
1
+ // Command: replace-clip → ppb_replaceClip
2
+ // Loaded lazily by ppb_dispatch (host/index.jsx) on first use, into the
3
+ // same global ExtendScript context — shared helpers (findSequenceByName,
4
+ // resolveTimelineClip, serializeTrackItem, secondsToTicksString,
5
+ // timeValueToSeconds, ...) are already defined there.
6
+ //
7
+ // DESTRUCTIVE: removes the addressed clip (clip.remove(false, false) —
8
+ // lift, not ripple, so the timeline position it occupied is preserved)
9
+ // and re-inserts the replacement projectItem at that same start time via
10
+ // seq.insertClip() — ported from leancoderkavy's premiere-pro-mcp
11
+ // replace_clip tool, re-addressed to this panel's
12
+ // trackType/trackIndex/clipIndex convention and given its own project-
13
+ // item addressing (nodeId/name) for the replacement, matching
14
+ // get-item-metadata's convention. Undo is NON-FUNCTIONAL on this build —
15
+ // if the replacement projectItem is wrong, the ORIGINAL clip's own
16
+ // details (returned as `before`) are the only path to manually reinsert
17
+ // it.
18
+ //
19
+ // MUTATION RULE: verified by re-finding a clip at the same start time on
20
+ // the same track afterward and confirming its projectItem's name/media
21
+ // path changed to the replacement's.
22
+
23
+ function ppbFindItemReplaceClip_walk(item, args, depth) {
24
+ if (depth > 32) {
25
+ return null;
26
+ }
27
+ var isBin = false;
28
+ try {
29
+ isBin = typeof ProjectItemType !== "undefined" && item.type === ProjectItemType.BIN;
30
+ } catch (e) {
31
+ isBin = false;
32
+ }
33
+ var matched = false;
34
+ if (args.nodeId !== null) {
35
+ try { matched = item.nodeId === args.nodeId; } catch (e) { matched = false; }
36
+ } else if (args.name !== null) {
37
+ try { matched = item.name === args.name; } catch (e) { matched = false; }
38
+ }
39
+ if (matched) {
40
+ return item;
41
+ }
42
+ if (isBin && item.children) {
43
+ for (var i = 0; i < item.children.numItems; i++) {
44
+ var found = ppbFindItemReplaceClip_walk(item.children[i], args, depth + 1);
45
+ if (found) {
46
+ return found;
47
+ }
48
+ }
49
+ }
50
+ return null;
51
+ }
52
+
53
+ function ppbFindItemReplaceClip_resolve(args) {
54
+ var root = app.project.rootItem;
55
+ for (var i = 0; i < root.children.numItems; i++) {
56
+ var found = ppbFindItemReplaceClip_walk(root.children[i], args, 1);
57
+ if (found) {
58
+ return found;
59
+ }
60
+ }
61
+ return null;
62
+ }
63
+
64
+ function ppb_replaceClip(argsJson) {
65
+ try {
66
+ var args;
67
+ try {
68
+ args = JSON.parse(argsJson);
69
+ } catch (e) {
70
+ return JSON.stringify({ ok: false, error: "invalid args JSON: " + e.toString() });
71
+ }
72
+
73
+ if (args.trackType !== "video" && args.trackType !== "audio") {
74
+ return JSON.stringify({ ok: false, error: "trackType must be \"video\" or \"audio\"" });
75
+ }
76
+ if (typeof args.trackIndex !== "number" || typeof args.clipIndex !== "number") {
77
+ return JSON.stringify({ ok: false, error: "trackIndex and clipIndex are required" });
78
+ }
79
+ var hasNodeId = typeof args.replacementNodeId === "string" && args.replacementNodeId.length > 0;
80
+ var hasName = typeof args.replacementName === "string" && args.replacementName.length > 0;
81
+ if (!hasNodeId && !hasName) {
82
+ return JSON.stringify({ ok: false, error: "either replacementNodeId or replacementName is required" });
83
+ }
84
+
85
+ if (!app.project) {
86
+ return JSON.stringify({ ok: false, error: "no project open" });
87
+ }
88
+
89
+ var seq = null;
90
+ if (args.sequenceName && typeof args.sequenceName === "string") {
91
+ seq = findSequenceByName(args.sequenceName);
92
+ if (!seq) {
93
+ return JSON.stringify({ ok: false, error: "no sequence named \"" + args.sequenceName + "\" is open" });
94
+ }
95
+ } else {
96
+ seq = app.project.activeSequence;
97
+ if (!seq) {
98
+ return JSON.stringify({ ok: false, error: "no active sequence, and no sequenceName given" });
99
+ }
100
+ }
101
+
102
+ var resolved = resolveTimelineClip(seq, args.trackType, args.trackIndex, args.clipIndex);
103
+ if (resolved.error) {
104
+ return JSON.stringify({ ok: false, error: resolved.error });
105
+ }
106
+ var clip = resolved.clip;
107
+
108
+ var replacementItem = ppbFindItemReplaceClip_resolve({
109
+ nodeId: hasNodeId ? args.replacementNodeId : null,
110
+ name: hasNodeId ? null : args.replacementName
111
+ });
112
+ if (!replacementItem) {
113
+ return JSON.stringify({ ok: false, error: "no replacement project item found matching the given replacementNodeId/replacementName" });
114
+ }
115
+
116
+ var before = serializeTrackItem(clip, args.trackIndex, args.clipIndex);
117
+ if (before.startSeconds === null) {
118
+ return JSON.stringify({ ok: false, error: "could not read the clip's own start time — refusing to remove it without a safe reinsertion point", removedClip: null });
119
+ }
120
+ var startSeconds = before.startSeconds;
121
+
122
+ var trackCollection = args.trackType === "video" ? seq.videoTracks : seq.audioTracks;
123
+ var track = trackCollection[args.trackIndex];
124
+
125
+ try {
126
+ clip.remove(false, false);
127
+ } catch (e2) {
128
+ return JSON.stringify({ ok: false, error: "clip.remove() failed while removing the original clip: " + e2.toString(), previousValue: before });
129
+ }
130
+
131
+ var vIdx = args.trackType === "video" ? args.trackIndex : -1;
132
+ var aIdx = args.trackType === "audio" ? args.trackIndex : -1;
133
+ var startTicks = secondsToTicksString(startSeconds);
134
+
135
+ try {
136
+ seq.insertClip(replacementItem, startTicks, vIdx, aIdx);
137
+ } catch (e3) {
138
+ return JSON.stringify({
139
+ ok: false,
140
+ error: "the original clip was already removed but seq.insertClip() of the replacement failed: " + e3.toString() + " — the track now has a gap; previousValue below is the only path to manually reinsert the original",
141
+ previousValue: before
142
+ });
143
+ }
144
+
145
+ var replacementNodeId = null;
146
+ try { replacementNodeId = replacementItem.nodeId; } catch (e4) { replacementNodeId = null; }
147
+
148
+ var newClip = null;
149
+ var toleranceSeconds = 0.1;
150
+ try {
151
+ for (var c = 0; c < track.clips.numItems; c++) {
152
+ var candidate = track.clips[c];
153
+ var candNodeId = null;
154
+ try { candNodeId = candidate.projectItem.nodeId; } catch (e5) { candNodeId = null; }
155
+ if (candNodeId !== replacementNodeId) {
156
+ continue;
157
+ }
158
+ var candStart = null;
159
+ try { candStart = timeValueToSeconds(candidate.start); } catch (e6) { candStart = null; }
160
+ if (candStart !== null && Math.abs(candStart - startSeconds) <= toleranceSeconds) {
161
+ newClip = serializeTrackItem(candidate, args.trackIndex, c);
162
+ break;
163
+ }
164
+ }
165
+ } catch (e7) {
166
+ newClip = null;
167
+ }
168
+
169
+ var verified = newClip !== null && newClip.mediaPath !== null &&
170
+ (function () { try { return newClip.mediaPath === replacementItem.getMediaPath(); } catch (e) { return false; } })();
171
+
172
+ return JSON.stringify({
173
+ ok: true,
174
+ result: {
175
+ sequenceName: seq.name,
176
+ trackType: args.trackType,
177
+ trackIndex: args.trackIndex,
178
+ clipIndex: args.clipIndex,
179
+ previousValue: before,
180
+ newValue: newClip,
181
+ replacementItem: { name: (function () { try { return replacementItem.name; } catch (e) { return null; } })(), nodeId: replacementNodeId },
182
+ verified: verified
183
+ }
184
+ });
185
+ } catch (e) {
186
+ return JSON.stringify({ ok: false, error: e.toString() });
187
+ }
188
+ }
@@ -0,0 +1,187 @@
1
+ // Command: reverse-clip → ppb_reverseClip
2
+ // Loaded lazily by ppb_dispatch (host/index.jsx) on first use, into the
3
+ // same global ExtendScript context — shared helpers (findSequenceByName,
4
+ // ensureQEEnabled, activateSequenceForQE, resolveTimelineClip,
5
+ // resolveQeClip, TICKS_PER_SECOND, ...) are already defined there.
6
+ //
7
+ // Reverses (or un-reverses) a clip's playback direction. REWRITTEN
8
+ // 2026-07-17 after the probe session (see BUILD_FINDINGS.md corrections):
9
+ // the working route on this build is the live-calibrated 5-arg
10
+ // qeClip.setSpeed(speedMultiplier, ticksString, reverse, false, false)
11
+ // whose THIRD argument is the reverse flag (verified via
12
+ // isSpeedReversed()). Negative speeds do NOT reverse — they clamp to a
13
+ // degenerate state — so the current speed magnitude is preserved and
14
+ // only the flag flips. setReverse() is still tried first in case a
15
+ // future build grows it (it did not exist on 26.3.0).
16
+ //
17
+ // getSpeed()/setSpeed speeds are MULTIPLIERS on this build (1 = 100%).
18
+ // Verified via the standard DOM's clip.isSpeedReversed() read-back —
19
+ // never via either QE call's own return value.
20
+
21
+ function ppb_reverseClip(argsJson) {
22
+ try {
23
+ var args;
24
+ try {
25
+ args = JSON.parse(argsJson);
26
+ } catch (e) {
27
+ return JSON.stringify({ ok: false, error: "invalid args JSON: " + e.toString() });
28
+ }
29
+
30
+ if (args.trackType !== "video" && args.trackType !== "audio") {
31
+ return JSON.stringify({ ok: false, error: "trackType must be \"video\" or \"audio\"" });
32
+ }
33
+ if (typeof args.trackIndex !== "number" || args.trackIndex < 0 || Math.floor(args.trackIndex) !== args.trackIndex) {
34
+ return JSON.stringify({ ok: false, error: "trackIndex must be a non-negative integer" });
35
+ }
36
+ if (typeof args.clipIndex !== "number" || args.clipIndex < 0 || Math.floor(args.clipIndex) !== args.clipIndex) {
37
+ return JSON.stringify({ ok: false, error: "clipIndex must be a non-negative integer" });
38
+ }
39
+ var reverse = args.reverse !== false; // default true, matching the reference tool
40
+
41
+ if (!app.project) {
42
+ return JSON.stringify({ ok: false, error: "no project open" });
43
+ }
44
+
45
+ var seq = null;
46
+ if (args.sequenceName && typeof args.sequenceName === "string") {
47
+ seq = findSequenceByName(args.sequenceName);
48
+ if (!seq) {
49
+ return JSON.stringify({ ok: false, error: "no sequence named \"" + args.sequenceName + "\" is open" });
50
+ }
51
+ } else {
52
+ seq = app.project.activeSequence;
53
+ if (!seq) {
54
+ return JSON.stringify({ ok: false, error: "no active sequence, and no sequenceName given" });
55
+ }
56
+ }
57
+
58
+ var resolved = resolveTimelineClip(seq, args.trackType, args.trackIndex, args.clipIndex);
59
+ if (resolved.error) {
60
+ return JSON.stringify({ ok: false, error: resolved.error });
61
+ }
62
+ var clip = resolved.clip;
63
+ var clipName = null;
64
+ try { clipName = clip.name; } catch (e) { clipName = null; }
65
+
66
+ var previousReversed = null;
67
+ try { previousReversed = clip.isSpeedReversed() == 1; } catch (e) { previousReversed = null; }
68
+ var previousSpeed = null;
69
+ try { previousSpeed = clip.getSpeed(); } catch (e) { previousSpeed = null; }
70
+
71
+ if (previousReversed === reverse) {
72
+ return JSON.stringify({
73
+ ok: true,
74
+ result: {
75
+ sequenceName: seq.name,
76
+ trackType: args.trackType,
77
+ trackIndex: args.trackIndex,
78
+ clipIndex: args.clipIndex,
79
+ clipName: clipName,
80
+ requestedReversed: reverse,
81
+ previousReversed: previousReversed,
82
+ newReversed: previousReversed,
83
+ alreadyInRequestedState: true,
84
+ verified: true
85
+ }
86
+ });
87
+ }
88
+
89
+ // setSpeed's ticks argument must be a plausible value — the clip's
90
+ // own current timeline duration (garbage there collapses the clip to
91
+ // one frame, per the calibration notes).
92
+ var durTicks = null;
93
+ try {
94
+ durTicks = String(Math.round((clip.end.seconds - clip.start.seconds) * TICKS_PER_SECOND));
95
+ } catch (e) {
96
+ return JSON.stringify({ ok: false, error: "could not read the clip's duration for setSpeed's ticks argument: " + e.toString() });
97
+ }
98
+
99
+ try {
100
+ ensureQEEnabled();
101
+ activateSequenceForQE(seq);
102
+ } catch (e) {
103
+ return JSON.stringify({ ok: false, error: "app.enableQE()/sequence activation failed: " + e.toString() });
104
+ }
105
+ if (typeof qe === "undefined" || !qe.project) {
106
+ return JSON.stringify({ ok: false, error: "QE DOM not available after enableQE()" });
107
+ }
108
+
109
+ var qeSeq;
110
+ try {
111
+ qeSeq = qe.project.getActiveSequence();
112
+ } catch (e) {
113
+ return JSON.stringify({ ok: false, error: "qe.project.getActiveSequence() failed: " + e.toString() });
114
+ }
115
+ var qeResolved = resolveQeClip(qeSeq, args.trackType, args.trackIndex, args.clipIndex);
116
+ if (qeResolved.error) {
117
+ return JSON.stringify({ ok: false, error: qeResolved.error });
118
+ }
119
+ var qeClip = qeResolved.qeClip;
120
+
121
+ var attempts = [];
122
+ var succeeded = false;
123
+ var formUsed = null;
124
+
125
+ // setReverse() did not exist on 26.3.0 — kept as a cheap first probe
126
+ // in case a future build grows it.
127
+ if (typeof qeClip.setReverse === "function") {
128
+ try {
129
+ qeClip.setReverse(reverse);
130
+ attempts.push({ form: "setReverse(bool)", success: true });
131
+ succeeded = true;
132
+ formUsed = "setReverse(bool)";
133
+ } catch (e) {
134
+ attempts.push({ form: "setReverse(bool)", success: false, error: e.toString() });
135
+ }
136
+ }
137
+
138
+ if (!succeeded && typeof qeClip.setSpeed === "function") {
139
+ // preserve the current speed magnitude; getSpeed() is a multiplier
140
+ var magnitude = (previousSpeed !== null && previousSpeed !== 0) ? Math.abs(previousSpeed) : 1;
141
+ try {
142
+ qeClip.setSpeed(magnitude, durTicks, reverse, false, false);
143
+ attempts.push({ form: "setSpeed(multiplier, ticksString, reverse, false, false)", success: true });
144
+ succeeded = true;
145
+ formUsed = "setSpeed(multiplier, ticksString, reverse, false, false)";
146
+ } catch (e) {
147
+ attempts.push({ form: "setSpeed(multiplier, ticksString, reverse, false, false)", success: false, error: e.toString() });
148
+ }
149
+ }
150
+
151
+ if (!succeeded) {
152
+ return JSON.stringify({
153
+ ok: false,
154
+ error: "could not reverse the clip with setReverse() or the calibrated setSpeed() form",
155
+ attempts: attempts
156
+ });
157
+ }
158
+
159
+ var newReversed = null;
160
+ try { newReversed = clip.isSpeedReversed() == 1; } catch (e) { newReversed = null; }
161
+ var newSpeed = null;
162
+ try { newSpeed = clip.getSpeed(); } catch (e) { newSpeed = null; }
163
+
164
+ var result = {
165
+ sequenceName: seq.name,
166
+ trackType: args.trackType,
167
+ trackIndex: args.trackIndex,
168
+ clipIndex: args.clipIndex,
169
+ clipName: clipName,
170
+ requestedReversed: reverse,
171
+ formUsed: formUsed,
172
+ attempts: attempts,
173
+ previousReversed: previousReversed,
174
+ newReversed: newReversed,
175
+ previousSpeed: previousSpeed,
176
+ newSpeed: newSpeed,
177
+ verified: newReversed === reverse
178
+ };
179
+ if (newReversed === null) {
180
+ result.note = "clip.isSpeedReversed() was not readable on this build — the QE call did not throw, but the result is unverified.";
181
+ }
182
+
183
+ return JSON.stringify({ ok: true, result: result });
184
+ } catch (e) {
185
+ return JSON.stringify({ ok: false, error: e.toString() });
186
+ }
187
+ }
@@ -0,0 +1,156 @@
1
+ // Command: ripple-delete-clip → ppb_rippleDeleteClip
2
+ // Loaded lazily by ppb_dispatch (host/index.jsx) on first use, into the
3
+ // same global ExtendScript context — shared helpers (findSequenceByName,
4
+ // ensureQEEnabled, activateSequenceForQE, resolveTimelineClip,
5
+ // resolveQeClip, serializeTrackItem, ...) are already defined there.
6
+ //
7
+ // **Destructive.** Ripple-deletes ONE clip (removes it and closes the
8
+ // gap, shifting later clips earlier) — the single-clip counterpart of
9
+ // remove-track-intervals' batch interval removal. Ported from
10
+ // leancoderkavy's premiere-pro-mcp `ripple_delete` tool (advanced.ts),
11
+ // which calls `qeClip.rippleDelete()` unconditionally and trusts it.
12
+ // Per this bridge's MUTATION RULE, success is instead judged the same way
13
+ // remove-track-intervals' findAndRemoveInRange does: an actual numItems
14
+ // drop on the QE track, with a `remove(bool, bool)` attempts fallback if
15
+ // rippleDelete() didn't move the needle (both calls are unreliable in
16
+ // practice on this build — neither's own return value nor the absence of
17
+ // a thrown exception is trustworthy). Undo is non-functional on this
18
+ // build (see README), so there is no way back from this command short of
19
+ // re-inserting the clip's source media by hand.
20
+
21
+ function ppb_rippleDeleteClip(argsJson) {
22
+ try {
23
+ var args;
24
+ try {
25
+ args = JSON.parse(argsJson);
26
+ } catch (e) {
27
+ return JSON.stringify({ ok: false, error: "invalid args JSON: " + e.toString() });
28
+ }
29
+
30
+ if (args.trackType !== "video" && args.trackType !== "audio") {
31
+ return JSON.stringify({ ok: false, error: "trackType must be \"video\" or \"audio\"" });
32
+ }
33
+ if (typeof args.trackIndex !== "number" || args.trackIndex < 0 || Math.floor(args.trackIndex) !== args.trackIndex) {
34
+ return JSON.stringify({ ok: false, error: "trackIndex must be a non-negative integer" });
35
+ }
36
+ if (typeof args.clipIndex !== "number" || args.clipIndex < 0 || Math.floor(args.clipIndex) !== args.clipIndex) {
37
+ return JSON.stringify({ ok: false, error: "clipIndex must be a non-negative integer" });
38
+ }
39
+
40
+ if (!app.project) {
41
+ return JSON.stringify({ ok: false, error: "no project open" });
42
+ }
43
+
44
+ var seq = null;
45
+ if (args.sequenceName && typeof args.sequenceName === "string") {
46
+ seq = findSequenceByName(args.sequenceName);
47
+ if (!seq) {
48
+ return JSON.stringify({ ok: false, error: "no sequence named \"" + args.sequenceName + "\" is open" });
49
+ }
50
+ } else {
51
+ seq = app.project.activeSequence;
52
+ if (!seq) {
53
+ return JSON.stringify({ ok: false, error: "no active sequence, and no sequenceName given" });
54
+ }
55
+ }
56
+
57
+ var resolved = resolveTimelineClip(seq, args.trackType, args.trackIndex, args.clipIndex);
58
+ if (resolved.error) {
59
+ return JSON.stringify({ ok: false, error: resolved.error });
60
+ }
61
+ var before = serializeTrackItem(resolved.clip, args.trackIndex, args.clipIndex);
62
+
63
+ try {
64
+ ensureQEEnabled();
65
+ activateSequenceForQE(seq);
66
+ } catch (e) {
67
+ return JSON.stringify({ ok: false, error: "app.enableQE()/sequence activation failed: " + e.toString() });
68
+ }
69
+ if (typeof qe === "undefined" || !qe.project) {
70
+ return JSON.stringify({ ok: false, error: "QE DOM not available after enableQE()" });
71
+ }
72
+
73
+ var qeSeq;
74
+ try {
75
+ qeSeq = qe.project.getActiveSequence();
76
+ } catch (e) {
77
+ return JSON.stringify({ ok: false, error: "qe.project.getActiveSequence() failed: " + e.toString() });
78
+ }
79
+ var qeResolved = resolveQeClip(qeSeq, args.trackType, args.trackIndex, args.clipIndex);
80
+ if (qeResolved.error) {
81
+ return JSON.stringify({ ok: false, error: qeResolved.error });
82
+ }
83
+ var qeClip = qeResolved.qeClip;
84
+ var qeTrack = qeResolved.qeTrack;
85
+
86
+ var previousItemCount = qeTrack.numItems;
87
+ var removed = false;
88
+ var method = null;
89
+
90
+ if (typeof qeClip.rippleDelete === "function") {
91
+ try {
92
+ qeClip.rippleDelete();
93
+ } catch (e) {
94
+ // fall through to the numItems check regardless — this call's
95
+ // own return value/thrown-ness is not trustworthy on this build
96
+ }
97
+ removed = qeTrack.numItems < previousItemCount;
98
+ if (removed) {
99
+ method = "rippleDelete";
100
+ }
101
+ }
102
+
103
+ if (!removed && typeof qeClip.remove === "function") {
104
+ // remove()'s two boolean params are undocumented — try plausible
105
+ // combinations, same fallback order as remove-track-intervals.
106
+ var attempts = [[true, false], [true, true], [false, true], [false, false]];
107
+ for (var a = 0; a < attempts.length && !removed; a++) {
108
+ var countBeforeAttempt = qeTrack.numItems;
109
+ try {
110
+ qeClip.remove(attempts[a][0], attempts[a][1]);
111
+ } catch (e) {
112
+ // fall through to the numItems check below regardless
113
+ }
114
+ if (qeTrack.numItems < countBeforeAttempt) {
115
+ removed = true;
116
+ method = "remove(" + attempts[a][0] + ", " + attempts[a][1] + ")";
117
+ }
118
+ }
119
+ }
120
+
121
+ var newItemCount = qeTrack.numItems;
122
+
123
+ if (!removed) {
124
+ return JSON.stringify({
125
+ ok: false,
126
+ error: "clip could not be ripple-deleted — neither rippleDelete() nor remove() with any boolean combination reduced the QE track's item count",
127
+ result: {
128
+ sequenceName: seq.name,
129
+ trackType: args.trackType,
130
+ trackIndex: args.trackIndex,
131
+ clipIndex: args.clipIndex,
132
+ clip: before,
133
+ previousItemCount: previousItemCount,
134
+ newItemCount: newItemCount
135
+ }
136
+ });
137
+ }
138
+
139
+ return JSON.stringify({
140
+ ok: true,
141
+ result: {
142
+ sequenceName: seq.name,
143
+ trackType: args.trackType,
144
+ trackIndex: args.trackIndex,
145
+ clipIndex: args.clipIndex,
146
+ removedClip: before,
147
+ method: method,
148
+ previousItemCount: previousItemCount,
149
+ newItemCount: newItemCount,
150
+ verified: newItemCount < previousItemCount
151
+ }
152
+ });
153
+ } catch (e) {
154
+ return JSON.stringify({ ok: false, error: e.toString() });
155
+ }
156
+ }