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,220 @@
1
+ // Command: move-clip-to-track → ppb_moveClipToTrack
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, timeValueToSeconds, ...) are
6
+ // already defined there.
7
+ //
8
+ // Moves a clip to a different track of the SAME media type. REWRITTEN
9
+ // 2026-07-17 after the probe session (see BUILD_FINDINGS.md
10
+ // corrections): the working signature on this build — found in the
11
+ // antipaster/Adobe-Premiere-Pro-MCP repo — is the 4-arg
12
+ // qeClip.moveToTrack(videoTrackOffset, audioTrackOffset,
13
+ // timeShiftTicksString, bool)
14
+ // with RELATIVE track offsets (target − source) and the time shift as a
15
+ // ticks string ("0" = keep the clip's time position). Live-verified: it
16
+ // is a TRUE move — per-clip effects/keyframes survive (tested with a
17
+ // Gaussian Blur) and the start time is preserved. This replaces the old
18
+ // LOSSY remove()+overwrite() re-add workaround entirely.
19
+ //
20
+ // The video-clip form (offset in arg1) is live-verified; for audio
21
+ // clips the offset is assumed to belong in arg2 by symmetry — if the
22
+ // arg2 attempt produces no observable change, arg1 is tried as a
23
+ // fallback, with every attempt recorded.
24
+ //
25
+ // MUTATION RULE: verified by re-finding the clip on the TARGET track by
26
+ // name/media path near its original start time, plus per-track clip
27
+ // counts — never by trusting moveToTrack()'s return value.
28
+
29
+ function ppbMoveClipToTrack_trackCounts(trackCollection) {
30
+ var counts = [];
31
+ for (var t = 0; t < trackCollection.numTracks; t++) {
32
+ counts.push(trackCollection[t].clips.numItems);
33
+ }
34
+ return counts;
35
+ }
36
+
37
+ function ppbMoveClipToTrack_findByNameNear(trackCollection, trackIndex, name, mediaPath, nearSeconds) {
38
+ if (trackIndex >= trackCollection.numTracks) {
39
+ return null;
40
+ }
41
+ var track = trackCollection[trackIndex];
42
+ var numClips = track.clips.numItems;
43
+ var best = null;
44
+ var bestDelta = null;
45
+ for (var i = 0; i < numClips; i++) {
46
+ var c = track.clips[i];
47
+ var cName = null;
48
+ var cPath = null;
49
+ try { cName = c.name; } catch (e) { cName = null; }
50
+ try { cPath = c.projectItem.getMediaPath(); } catch (e2) { cPath = null; }
51
+ if (cName === name || (mediaPath !== null && cPath === mediaPath)) {
52
+ var startSeconds = null;
53
+ try { startSeconds = timeValueToSeconds(c.start); } catch (e3) { startSeconds = null; }
54
+ var delta = (startSeconds !== null && nearSeconds !== null) ? Math.abs(startSeconds - nearSeconds) : 0;
55
+ if (best === null || delta < bestDelta) {
56
+ best = { clip: c, index: i };
57
+ bestDelta = delta;
58
+ }
59
+ }
60
+ }
61
+ return best;
62
+ }
63
+
64
+ function ppb_moveClipToTrack(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" || args.trackIndex < 0 || Math.floor(args.trackIndex) !== args.trackIndex) {
77
+ return JSON.stringify({ ok: false, error: "trackIndex must be a non-negative integer" });
78
+ }
79
+ if (typeof args.clipIndex !== "number" || args.clipIndex < 0 || Math.floor(args.clipIndex) !== args.clipIndex) {
80
+ return JSON.stringify({ ok: false, error: "clipIndex must be a non-negative integer" });
81
+ }
82
+ if (typeof args.targetTrackIndex !== "number" || args.targetTrackIndex < 0 || Math.floor(args.targetTrackIndex) !== args.targetTrackIndex) {
83
+ return JSON.stringify({ ok: false, error: "targetTrackIndex must be a non-negative integer" });
84
+ }
85
+ if (args.targetTrackIndex === args.trackIndex) {
86
+ return JSON.stringify({ ok: false, error: "targetTrackIndex is the same as trackIndex — nothing to move" });
87
+ }
88
+
89
+ if (!app.project) {
90
+ return JSON.stringify({ ok: false, error: "no project open" });
91
+ }
92
+
93
+ var seq = null;
94
+ if (args.sequenceName && typeof args.sequenceName === "string") {
95
+ seq = findSequenceByName(args.sequenceName);
96
+ if (!seq) {
97
+ return JSON.stringify({ ok: false, error: "no sequence named \"" + args.sequenceName + "\" is open" });
98
+ }
99
+ } else {
100
+ seq = app.project.activeSequence;
101
+ if (!seq) {
102
+ return JSON.stringify({ ok: false, error: "no active sequence, and no sequenceName given" });
103
+ }
104
+ }
105
+
106
+ var trackCollection = args.trackType === "video" ? seq.videoTracks : seq.audioTracks;
107
+ if (args.targetTrackIndex >= trackCollection.numTracks) {
108
+ return JSON.stringify({
109
+ ok: false,
110
+ error: "targetTrackIndex " + args.targetTrackIndex + " is out of range — sequence has " + trackCollection.numTracks + " " + args.trackType + " track(s)"
111
+ });
112
+ }
113
+
114
+ var resolved = resolveTimelineClip(seq, args.trackType, args.trackIndex, args.clipIndex);
115
+ if (resolved.error) {
116
+ return JSON.stringify({ ok: false, error: resolved.error });
117
+ }
118
+ var before = serializeTrackItem(resolved.clip, args.trackIndex, args.clipIndex);
119
+
120
+ try {
121
+ ensureQEEnabled();
122
+ activateSequenceForQE(seq);
123
+ } catch (e) {
124
+ return JSON.stringify({ ok: false, error: "app.enableQE()/sequence activation failed: " + e.toString() });
125
+ }
126
+ if (typeof qe === "undefined" || !qe.project) {
127
+ return JSON.stringify({ ok: false, error: "QE DOM not available after enableQE()" });
128
+ }
129
+
130
+ var qeSeq;
131
+ try {
132
+ qeSeq = qe.project.getActiveSequence();
133
+ } catch (e) {
134
+ return JSON.stringify({ ok: false, error: "qe.project.getActiveSequence() failed: " + e.toString() });
135
+ }
136
+ var qeResolved = resolveQeClip(qeSeq, args.trackType, args.trackIndex, args.clipIndex);
137
+ if (qeResolved.error) {
138
+ return JSON.stringify({ ok: false, error: qeResolved.error });
139
+ }
140
+ var qeClip = qeResolved.qeClip;
141
+ if (typeof qeClip.moveToTrack !== "function") {
142
+ return JSON.stringify({ ok: false, error: "qeClip.moveToTrack is not a function on this build" });
143
+ }
144
+
145
+ var offset = args.targetTrackIndex - args.trackIndex;
146
+
147
+ // Arg order: (videoTrackOffset, audioTrackOffset, timeShiftTicks,
148
+ // bool). The video slot is live-verified; the audio slot is assumed
149
+ // by symmetry with an arg1 fallback.
150
+ var forms = [];
151
+ if (args.trackType === "video") {
152
+ forms.push({ label: "moveToTrack(offset, 0, '0', false)", a: [offset, 0, "0", false] });
153
+ } else {
154
+ forms.push({ label: "moveToTrack(0, offset, '0', false)", a: [0, offset, "0", false] });
155
+ forms.push({ label: "moveToTrack(offset, 0, '0', false) [audio fallback]", a: [offset, 0, "0", false] });
156
+ }
157
+
158
+ var attempts = [];
159
+ var moved = false;
160
+ var formUsed = null;
161
+ for (var f = 0; f < forms.length && !moved; f++) {
162
+ var countsBefore = ppbMoveClipToTrack_trackCounts(trackCollection);
163
+ var att = { form: forms[f].label };
164
+ try {
165
+ qeClip.moveToTrack.apply(qeClip, forms[f].a);
166
+ att.threw = false;
167
+ } catch (e2) {
168
+ att.threw = true;
169
+ att.error = e2.toString();
170
+ }
171
+ var countsAfter = ppbMoveClipToTrack_trackCounts(trackCollection);
172
+ att.effective = false;
173
+ for (var t = 0; t < countsAfter.length; t++) {
174
+ if (countsAfter[t] !== countsBefore[t]) { att.effective = true; }
175
+ }
176
+ attempts.push(att);
177
+ if (att.effective) {
178
+ moved = true;
179
+ formUsed = forms[f].label;
180
+ }
181
+ }
182
+
183
+ if (!moved) {
184
+ return JSON.stringify({
185
+ ok: false,
186
+ error: "moveToTrack() produced no observable track change with any argument form tried — the clip was left in place",
187
+ attempts: attempts,
188
+ before: before
189
+ });
190
+ }
191
+
192
+ var found = ppbMoveClipToTrack_findByNameNear(trackCollection, args.targetTrackIndex, before.name, before.mediaPath, before.startSeconds);
193
+ var after = found ? serializeTrackItem(found.clip, args.targetTrackIndex, found.index) : null;
194
+
195
+ var startDelta = (after && after.startSeconds !== null && before.startSeconds !== null)
196
+ ? (after.startSeconds - before.startSeconds)
197
+ : null;
198
+
199
+ return JSON.stringify({
200
+ ok: true,
201
+ result: {
202
+ sequenceName: seq.name,
203
+ trackType: args.trackType,
204
+ sourceTrackIndex: args.trackIndex,
205
+ sourceClipIndex: args.clipIndex,
206
+ targetTrackIndex: args.targetTrackIndex,
207
+ formUsed: formUsed,
208
+ attempts: attempts,
209
+ before: before,
210
+ after: after,
211
+ placedOnTarget: after !== null,
212
+ startSecondsDelta: startDelta,
213
+ positionPreserved: startDelta !== null && Math.abs(startDelta) < 0.5,
214
+ note: "TRUE move via qeClip.moveToTrack() — per-clip effects/keyframes survive (live-verified 2026-07-17), unlike the previous remove+overwrite workaround"
215
+ }
216
+ });
217
+ } catch (e) {
218
+ return JSON.stringify({ ok: false, error: e.toString() });
219
+ }
220
+ }
@@ -0,0 +1,97 @@
1
+ // Command: move-clip → ppb_moveClip
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, tryTimeForms, ...) are already
5
+ // defined there.
6
+ //
7
+ // Moves a clip to a new ABSOLUTE start time on the SAME track via
8
+ // clip.start assignment (PREMIERE_API_NOTES.md: "assign clip.start =
9
+ // newStartTicksString ... neither standard-DOM path changes track" —
10
+ // this command does not attempt a cross-track move, matching that
11
+ // documented limitation). Key-argument form is disputed across builds
12
+ // (ticks string vs Time object vs raw seconds), so tryTimeForms() tries
13
+ // each in turn.
14
+ //
15
+ // MUTATION RULE: verified via a clip.start read-back after the
16
+ // assignment, compared against the requested startSeconds with a
17
+ // tolerance (frame-quantization is expected).
18
+
19
+ function ppb_moveClip(argsJson) {
20
+ try {
21
+ var args;
22
+ try {
23
+ args = JSON.parse(argsJson);
24
+ } catch (e) {
25
+ return JSON.stringify({ ok: false, error: "invalid args JSON: " + e.toString() });
26
+ }
27
+
28
+ if (args.trackType !== "video" && args.trackType !== "audio") {
29
+ return JSON.stringify({ ok: false, error: "trackType must be \"video\" or \"audio\"" });
30
+ }
31
+ if (typeof args.trackIndex !== "number" || typeof args.clipIndex !== "number") {
32
+ return JSON.stringify({ ok: false, error: "trackIndex and clipIndex are required" });
33
+ }
34
+ if (typeof args.startSeconds !== "number") {
35
+ return JSON.stringify({ ok: false, error: "startSeconds is required" });
36
+ }
37
+
38
+ if (!app.project) {
39
+ return JSON.stringify({ ok: false, error: "no project open" });
40
+ }
41
+
42
+ var seq = null;
43
+ if (args.sequenceName && typeof args.sequenceName === "string") {
44
+ seq = findSequenceByName(args.sequenceName);
45
+ if (!seq) {
46
+ return JSON.stringify({ ok: false, error: "no sequence named \"" + args.sequenceName + "\" is open" });
47
+ }
48
+ } else {
49
+ seq = app.project.activeSequence;
50
+ if (!seq) {
51
+ return JSON.stringify({ ok: false, error: "no active sequence, and no sequenceName given" });
52
+ }
53
+ }
54
+
55
+ var resolved = resolveTimelineClip(seq, args.trackType, args.trackIndex, args.clipIndex);
56
+ if (resolved.error) {
57
+ return JSON.stringify({ ok: false, error: resolved.error });
58
+ }
59
+ var clip = resolved.clip;
60
+
61
+ var before = serializeTrackItem(clip, args.trackIndex, args.clipIndex);
62
+
63
+ var attemptResult = tryTimeForms(args.startSeconds, function (timeArg) {
64
+ clip.start = timeArg;
65
+ });
66
+
67
+ if (!attemptResult.success) {
68
+ return JSON.stringify({
69
+ ok: false,
70
+ error: "could not set clip.start with any known argument form (ticksString, Time object, seconds) — see attempts",
71
+ attempts: attemptResult.attempts,
72
+ previousValue: before
73
+ });
74
+ }
75
+
76
+ var after = serializeTrackItem(clip, args.trackIndex, args.clipIndex);
77
+ var verified = after.startSeconds !== null && Math.abs(after.startSeconds - args.startSeconds) <= 0.05;
78
+
79
+ return JSON.stringify({
80
+ ok: true,
81
+ result: {
82
+ sequenceName: seq.name,
83
+ trackType: args.trackType,
84
+ trackIndex: args.trackIndex,
85
+ clipIndex: args.clipIndex,
86
+ requestedStartSeconds: args.startSeconds,
87
+ previousValue: before,
88
+ newValue: after,
89
+ verified: verified,
90
+ formUsed: attemptResult.formUsed,
91
+ attempts: attemptResult.attempts
92
+ }
93
+ });
94
+ } catch (e) {
95
+ return JSON.stringify({ ok: false, error: e.toString() });
96
+ }
97
+ }
@@ -0,0 +1,194 @@
1
+ // Command: move-items-to-bin → ppb_moveItemsToBin
2
+ // Loaded lazily by ppb_dispatch (host/index.jsx) on first use, into the
3
+ // same global ExtendScript context — shared helpers (TICKS_PER_SECOND, ...)
4
+ // are already defined there.
5
+ //
6
+ // MERGES the reference project's two near-duplicate media.ts/track-
7
+ // targeting.ts tools — move_item_to_bin (single item) and
8
+ // move_items_to_bin (array) — into one command: pass either nodeIds (a
9
+ // JSON array string) OR a single nodeId/name, plus targetBinPath. Item
10
+ // resolution uses children-presence recursion (NOT an isBin() gate) per
11
+ // get-project-item-info.jsx's live-debugged finding that the ROOT item's
12
+ // type is ROOT, not BIN — an isBin() gate would never descend past the
13
+ // root. targetBinPath is a '/'-separated bin path (same convention as
14
+ // create-sequence's bin arg) resolved but never auto-created here — call
15
+ // create-bin first if it might not exist. Each move verified by re-reading
16
+ // the item's own treePath afterward and confirming it now starts with the
17
+ // target bin's treePath.
18
+
19
+ function ppbMoveItemsToBin_findByNodeId(item, nodeId, depth) {
20
+ if (depth > 32) {
21
+ return null;
22
+ }
23
+ try {
24
+ if (item.nodeId === nodeId) {
25
+ return item;
26
+ }
27
+ } catch (e) {
28
+ // fall through
29
+ }
30
+ if (item.children && item.children.numItems > 0) {
31
+ for (var i = 0; i < item.children.numItems; i++) {
32
+ var found = ppbMoveItemsToBin_findByNodeId(item.children[i], nodeId, depth + 1);
33
+ if (found) {
34
+ return found;
35
+ }
36
+ }
37
+ }
38
+ return null;
39
+ }
40
+
41
+ function ppbMoveItemsToBin_findByName(item, name, depth) {
42
+ if (depth > 32) {
43
+ return null;
44
+ }
45
+ try {
46
+ if (item.name === name) {
47
+ return item;
48
+ }
49
+ } catch (e) {
50
+ // fall through
51
+ }
52
+ if (item.children && item.children.numItems > 0) {
53
+ for (var i = 0; i < item.children.numItems; i++) {
54
+ var found = ppbMoveItemsToBin_findByName(item.children[i], name, depth + 1);
55
+ if (found) {
56
+ return found;
57
+ }
58
+ }
59
+ }
60
+ return null;
61
+ }
62
+
63
+ function ppbMoveItemsToBin_resolveBinPath(binPath) {
64
+ var segments = binPath.split("/").filter(function (s) { return s.length > 0; });
65
+ if (segments.length === 0) {
66
+ return { error: "targetBinPath must contain at least one non-empty segment" };
67
+ }
68
+ var current = app.project.rootItem;
69
+ for (var i = 0; i < segments.length; i++) {
70
+ var found = null;
71
+ for (var j = 0; j < current.children.numItems; j++) {
72
+ var child = current.children[j];
73
+ if (child.name === segments[i] && typeof ProjectItemType !== "undefined" && child.type === ProjectItemType.BIN) {
74
+ found = child;
75
+ break;
76
+ }
77
+ }
78
+ if (!found) {
79
+ return { error: "bin path segment \"" + segments[i] + "\" not found in \"" + binPath + "\" (target bin must already exist — call create-bin first)" };
80
+ }
81
+ current = found;
82
+ }
83
+ return { bin: current };
84
+ }
85
+
86
+ function ppb_moveItemsToBin(argsJson) {
87
+ try {
88
+ var args;
89
+ try {
90
+ args = JSON.parse(argsJson);
91
+ } catch (e) {
92
+ return JSON.stringify({ ok: false, error: "invalid args JSON: " + e.toString() });
93
+ }
94
+
95
+ var identifiers = [];
96
+ if (typeof args.nodeIds === "string" && args.nodeIds.length > 0) {
97
+ var parsed;
98
+ try {
99
+ parsed = JSON.parse(args.nodeIds);
100
+ } catch (e) {
101
+ return JSON.stringify({ ok: false, error: "nodeIds must be a JSON array string: " + e.toString() });
102
+ }
103
+ if (!(parsed instanceof Array)) {
104
+ return JSON.stringify({ ok: false, error: "nodeIds must parse to a JSON array" });
105
+ }
106
+ for (var i = 0; i < parsed.length; i++) {
107
+ identifiers.push({ nodeId: parsed[i], name: null });
108
+ }
109
+ } else if (typeof args.nodeId === "string" && args.nodeId.length > 0) {
110
+ identifiers.push({ nodeId: args.nodeId, name: null });
111
+ } else if (typeof args.name === "string" && args.name.length > 0) {
112
+ identifiers.push({ nodeId: null, name: args.name });
113
+ }
114
+
115
+ if (identifiers.length === 0) {
116
+ return JSON.stringify({ ok: false, error: "one of nodeIds (JSON array), nodeId, or name is required" });
117
+ }
118
+
119
+ if (!args.targetBinPath || typeof args.targetBinPath !== "string") {
120
+ return JSON.stringify({ ok: false, error: "targetBinPath is required" });
121
+ }
122
+
123
+ if (!app.project) {
124
+ return JSON.stringify({ ok: false, error: "no project open" });
125
+ }
126
+
127
+ var binResolved = ppbMoveItemsToBin_resolveBinPath(args.targetBinPath);
128
+ if (binResolved.error) {
129
+ return JSON.stringify({ ok: false, error: binResolved.error });
130
+ }
131
+ var targetBin = binResolved.bin;
132
+ var targetTreePath = null;
133
+ try { targetTreePath = targetBin.treePath; } catch (e) { targetTreePath = null; }
134
+
135
+ var results = [];
136
+ var movedCount = 0;
137
+
138
+ for (var k = 0; k < identifiers.length; k++) {
139
+ var ident = identifiers[k];
140
+ var item = ident.nodeId
141
+ ? ppbMoveItemsToBin_findByNodeId(app.project.rootItem, ident.nodeId, 0)
142
+ : ppbMoveItemsToBin_findByName(app.project.rootItem, ident.name, 0);
143
+
144
+ if (!item) {
145
+ results.push({
146
+ nodeId: ident.nodeId,
147
+ name: ident.name,
148
+ moved: false,
149
+ error: "no project item found matching the given nodeId/name"
150
+ });
151
+ continue;
152
+ }
153
+
154
+ var itemName = null;
155
+ try { itemName = item.name; } catch (e) { itemName = null; }
156
+ var itemNodeId = null;
157
+ try { itemNodeId = item.nodeId; } catch (e) { itemNodeId = null; }
158
+
159
+ try {
160
+ item.moveBin(targetBin);
161
+ } catch (e) {
162
+ results.push({ nodeId: itemNodeId, name: itemName, moved: false, error: "moveBin() failed: " + e.toString() });
163
+ continue;
164
+ }
165
+
166
+ var newTreePath = null;
167
+ try { newTreePath = item.treePath; } catch (e) { newTreePath = null; }
168
+ var verified = targetTreePath !== null && newTreePath !== null && newTreePath.indexOf(targetTreePath) === 0;
169
+
170
+ results.push({
171
+ nodeId: itemNodeId,
172
+ name: itemName,
173
+ moved: true,
174
+ newTreePath: newTreePath,
175
+ verified: verified
176
+ });
177
+ if (verified) {
178
+ movedCount++;
179
+ }
180
+ }
181
+
182
+ return JSON.stringify({
183
+ ok: true,
184
+ result: {
185
+ targetBinPath: args.targetBinPath,
186
+ requestedCount: identifiers.length,
187
+ movedCount: movedCount,
188
+ results: results
189
+ }
190
+ });
191
+ } catch (e) {
192
+ return JSON.stringify({ ok: false, error: e.toString() });
193
+ }
194
+ }
@@ -0,0 +1,174 @@
1
+ // Command: move-playhead-to-edit → ppb_movePlayheadToEdit
2
+ // Loaded lazily by ppb_dispatch (host/index.jsx) on first use, into the
3
+ // same global ExtendScript context — shared helpers (TICKS_PER_SECOND,
4
+ // findSequenceByName, ...) are already defined there.
5
+ //
6
+ // Ported from leancoderkavy's premiere-pro-mcp move_playhead_to_edit tool
7
+ // (utility.ts), combining its edit-point-search logic (same one backing
8
+ // get-next-edit-point.jsx: collect every clip start/end tick across all
9
+ // video/audio tracks, sort, dedupe, pick the nearest one before/after the
10
+ // playhead) with move-playhead.jsx's proven seek pattern for the actual
11
+ // move (ticksString first, Time-object fallback, recording each attempt).
12
+ // Standard DOM only, no QE needed.
13
+ //
14
+ // MUTATION RULE: verified by reading back seq.getPlayerPosition() after
15
+ // the seek and reporting it alongside the edit point that was targeted —
16
+ // never trusting the seek call's own (lack of a) return value.
17
+
18
+ function ppbMovePlayheadToEdit_collectPoints(seq) {
19
+ var points = [];
20
+
21
+ function collect(tracks) {
22
+ for (var t = 0; t < tracks.numTracks; t++) {
23
+ for (var c = 0; c < tracks[t].clips.numItems; c++) {
24
+ var clip = tracks[t].clips[c];
25
+ try { points.push(parseFloat(clip.start.ticks)); } catch (e) { /* skip */ }
26
+ try { points.push(parseFloat(clip.end.ticks)); } catch (e) { /* skip */ }
27
+ }
28
+ }
29
+ }
30
+
31
+ collect(seq.videoTracks);
32
+ collect(seq.audioTracks);
33
+
34
+ points.sort(function (a, b) { return a - b; });
35
+
36
+ var unique = [];
37
+ for (var i = 0; i < points.length; i++) {
38
+ if (unique.length === 0 || points[i] !== unique[unique.length - 1]) {
39
+ unique.push(points[i]);
40
+ }
41
+ }
42
+ return unique;
43
+ }
44
+
45
+ function ppb_movePlayheadToEdit(argsJson) {
46
+ try {
47
+ var args;
48
+ try {
49
+ args = JSON.parse(argsJson);
50
+ } catch (e) {
51
+ return JSON.stringify({ ok: false, error: "invalid args JSON: " + e.toString() });
52
+ }
53
+
54
+ var direction = args.direction === "previous" ? "previous" : "next";
55
+ if (args.direction !== undefined && args.direction !== null && args.direction !== "next" && args.direction !== "previous") {
56
+ return JSON.stringify({ ok: false, error: "direction must be \"next\" or \"previous\"" });
57
+ }
58
+
59
+ if (!app.project) {
60
+ return JSON.stringify({ ok: false, error: "no project open" });
61
+ }
62
+
63
+ var seq = null;
64
+ if (args.sequenceName && typeof args.sequenceName === "string") {
65
+ seq = findSequenceByName(args.sequenceName);
66
+ if (!seq) {
67
+ return JSON.stringify({ ok: false, error: "no sequence named \"" + args.sequenceName + "\" is open" });
68
+ }
69
+ } else {
70
+ seq = app.project.activeSequence;
71
+ if (!seq) {
72
+ return JSON.stringify({ ok: false, error: "no active sequence, and no sequenceName given" });
73
+ }
74
+ }
75
+
76
+ if (app.project.activeSequence !== seq) {
77
+ app.project.activeSequence = seq;
78
+ }
79
+
80
+ var posTicks = null;
81
+ try {
82
+ posTicks = parseFloat(seq.getPlayerPosition().ticks);
83
+ } catch (e) {
84
+ return JSON.stringify({ ok: false, error: "could not read the current playhead position: " + e.toString() });
85
+ }
86
+
87
+ var editPoints = ppbMovePlayheadToEdit_collectPoints(seq);
88
+
89
+ var found = null;
90
+ if (direction === "next") {
91
+ for (var i = 0; i < editPoints.length; i++) {
92
+ if (editPoints[i] > posTicks + 1) {
93
+ found = editPoints[i];
94
+ break;
95
+ }
96
+ }
97
+ } else {
98
+ for (var j = editPoints.length - 1; j >= 0; j--) {
99
+ if (editPoints[j] < posTicks - 1) {
100
+ found = editPoints[j];
101
+ break;
102
+ }
103
+ }
104
+ }
105
+
106
+ if (found === null) {
107
+ return JSON.stringify({ ok: false, error: "no " + direction + " edit point found from the current playhead position" });
108
+ }
109
+
110
+ var targetSeconds = found / TICKS_PER_SECOND;
111
+ var ticksString = String(Math.round(found));
112
+ var timeObj = null;
113
+ try {
114
+ timeObj = new Time();
115
+ timeObj.seconds = targetSeconds;
116
+ } catch (e) {
117
+ timeObj = null;
118
+ }
119
+
120
+ var positionAttempts = [];
121
+ var positionSet = false;
122
+ try {
123
+ seq.setPlayerPosition(ticksString);
124
+ positionAttempts.push({ form: "ticksString", success: true });
125
+ positionSet = true;
126
+ } catch (e) {
127
+ positionAttempts.push({ form: "ticksString", success: false, error: e.toString() });
128
+ }
129
+ if (!positionSet && timeObj !== null) {
130
+ try {
131
+ seq.setPlayerPosition(timeObj);
132
+ positionAttempts.push({ form: "TimeObject", success: true });
133
+ positionSet = true;
134
+ } catch (e) {
135
+ positionAttempts.push({ form: "TimeObject", success: false, error: e.toString() });
136
+ }
137
+ }
138
+
139
+ if (!positionSet) {
140
+ return JSON.stringify({
141
+ ok: false,
142
+ error: "could not move the playhead to the found edit point with any known argument form (ticks string, Time object)",
143
+ attempts: positionAttempts
144
+ });
145
+ }
146
+
147
+ var playheadSeconds = null;
148
+ try {
149
+ var currentPosition = seq.getPlayerPosition();
150
+ if (currentPosition !== null && typeof currentPosition !== "undefined") {
151
+ if (typeof currentPosition.seconds === "number") {
152
+ playheadSeconds = currentPosition.seconds;
153
+ } else {
154
+ playheadSeconds = Number(currentPosition.ticks) / TICKS_PER_SECOND;
155
+ }
156
+ }
157
+ } catch (e) {
158
+ playheadSeconds = null;
159
+ }
160
+
161
+ return JSON.stringify({
162
+ ok: true,
163
+ result: {
164
+ sequenceName: seq.name,
165
+ direction: direction,
166
+ editPointSeconds: targetSeconds,
167
+ playheadSeconds: playheadSeconds,
168
+ attempts: positionAttempts
169
+ }
170
+ });
171
+ } catch (e) {
172
+ return JSON.stringify({ ok: false, error: e.toString() });
173
+ }
174
+ }