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.
- premiere_cli/__init__.py +11 -0
- premiere_cli/cli.py +3664 -0
- premiere_cli/log.py +66 -0
- premiere_cli/panel/.debug +8 -0
- premiere_cli/panel/CSXS/manifest.xml +52 -0
- premiere_cli/panel/css/style.css +53 -0
- premiere_cli/panel/host/commands/add-adjustment-layer.jsx +190 -0
- premiere_cli/panel/host/commands/add-audio-keyframes.jsx +176 -0
- premiere_cli/panel/host/commands/add-custom-metadata-field.jsx +63 -0
- premiere_cli/panel/host/commands/add-keyframe.jsx +181 -0
- premiere_cli/panel/host/commands/add-marker-to-project-item.jsx +194 -0
- premiere_cli/panel/host/commands/add-marker.jsx +129 -0
- premiere_cli/panel/host/commands/add-text-overlay.jsx +101 -0
- premiere_cli/panel/host/commands/add-to-render-queue.jsx +120 -0
- premiere_cli/panel/host/commands/add-to-timeline.jsx +300 -0
- premiere_cli/panel/host/commands/add-track.jsx +188 -0
- premiere_cli/panel/host/commands/add-transition.jsx +134 -0
- premiere_cli/panel/host/commands/adjust-audio-levels.jsx +132 -0
- premiere_cli/panel/host/commands/apply-audio-effect.jsx +118 -0
- premiere_cli/panel/host/commands/apply-effect.jsx +122 -0
- premiere_cli/panel/host/commands/apply-lut.jsx +107 -0
- premiere_cli/panel/host/commands/attach-custom-property.jsx +66 -0
- premiere_cli/panel/host/commands/auto-reframe-sequence.jsx +124 -0
- premiere_cli/panel/host/commands/batch-add-transitions.jsx +158 -0
- premiere_cli/panel/host/commands/batch-apply-effect.jsx +182 -0
- premiere_cli/panel/host/commands/batch-rename-clips.jsx +170 -0
- premiere_cli/panel/host/commands/batch-set-clips-enabled.jsx +145 -0
- premiere_cli/panel/host/commands/clear-item-in-out.jsx +117 -0
- premiere_cli/panel/host/commands/clear-sequence-in-out.jsx +100 -0
- premiere_cli/panel/host/commands/close-all-source-clips.jsx +32 -0
- premiere_cli/panel/host/commands/close-sequence.jsx +80 -0
- premiere_cli/panel/host/commands/close-source-monitor.jsx +32 -0
- premiere_cli/panel/host/commands/color-correct.jsx +164 -0
- premiere_cli/panel/host/commands/copy-effect-values.jsx +183 -0
- premiere_cli/panel/host/commands/copy-effects-between-clips.jsx +234 -0
- premiere_cli/panel/host/commands/create-bars-and-tone.jsx +152 -0
- premiere_cli/panel/host/commands/create-bin.jsx +76 -0
- premiere_cli/panel/host/commands/create-caption-track.jsx +159 -0
- premiere_cli/panel/host/commands/create-sequence-from-clips.jsx +170 -0
- premiere_cli/panel/host/commands/create-sequence-from-preset.jsx +90 -0
- premiere_cli/panel/host/commands/create-sequence.jsx +166 -0
- premiere_cli/panel/host/commands/create-smart-bin.jsx +80 -0
- premiere_cli/panel/host/commands/create-subclip.jsx +248 -0
- premiere_cli/panel/host/commands/create-subsequence.jsx +136 -0
- premiere_cli/panel/host/commands/debug-qe-inspect.jsx +248 -0
- premiere_cli/panel/host/commands/debug-qe-try-mutate.jsx +305 -0
- premiere_cli/panel/host/commands/delete-marker.jsx +178 -0
- premiere_cli/panel/host/commands/deselect-all-clips.jsx +98 -0
- premiere_cli/panel/host/commands/detach-proxy.jsx +116 -0
- premiere_cli/panel/host/commands/duplicate-clip.jsx +140 -0
- premiere_cli/panel/host/commands/duplicate-sequence.jsx +150 -0
- premiere_cli/panel/host/commands/encode-file.jsx +86 -0
- premiere_cli/panel/host/commands/encode-project-item.jsx +145 -0
- premiere_cli/panel/host/commands/export-aaf.jsx +77 -0
- premiere_cli/panel/host/commands/export-fcp-xml.jsx +65 -0
- premiere_cli/panel/host/commands/export-frame.jsx +381 -0
- premiere_cli/panel/host/commands/export-omf.jsx +95 -0
- premiere_cli/panel/host/commands/export-sequence-as-project.jsx +65 -0
- premiere_cli/panel/host/commands/export-sequence.jsx +100 -0
- premiere_cli/panel/host/commands/extract-audio-track.jsx +181 -0
- premiere_cli/panel/host/commands/extract-selection.jsx +113 -0
- premiere_cli/panel/host/commands/find-items-by-media-path.jsx +112 -0
- premiere_cli/panel/host/commands/freeze-frame.jsx +215 -0
- premiere_cli/panel/host/commands/get-active-sequence.jsx +103 -0
- premiere_cli/panel/host/commands/get-all-project-paths.jsx +72 -0
- premiere_cli/panel/host/commands/get-bin-contents.jsx +176 -0
- premiere_cli/panel/host/commands/get-clip-adjustment-layer.jsx +83 -0
- premiere_cli/panel/host/commands/get-clip-at-playhead.jsx +99 -0
- premiere_cli/panel/host/commands/get-clip-at-position.jsx +120 -0
- premiere_cli/panel/host/commands/get-clip-links.jsx +135 -0
- premiere_cli/panel/host/commands/get-clip-markers.jsx +144 -0
- premiere_cli/panel/host/commands/get-clip-speed.jsx +69 -0
- premiere_cli/panel/host/commands/get-color-label.jsx +89 -0
- premiere_cli/panel/host/commands/get-color-space.jsx +99 -0
- premiere_cli/panel/host/commands/get-duplicate-media.jsx +78 -0
- premiere_cli/panel/host/commands/get-effect-properties.jsx +129 -0
- premiere_cli/panel/host/commands/get-encoder-presets.jsx +131 -0
- premiere_cli/panel/host/commands/get-export-file-extension.jsx +56 -0
- premiere_cli/panel/host/commands/get-footage-interpretation.jsx +113 -0
- premiere_cli/panel/host/commands/get-full-clip-info.jsx +162 -0
- premiere_cli/panel/host/commands/get-full-project-overview.jsx +193 -0
- premiere_cli/panel/host/commands/get-full-sequence-info.jsx +184 -0
- premiere_cli/panel/host/commands/get-graphics-white-luminance.jsx +31 -0
- premiere_cli/panel/host/commands/get-insertion-bin.jsx +35 -0
- premiere_cli/panel/host/commands/get-item-metadata.jsx +94 -0
- premiere_cli/panel/host/commands/get-keyframes.jsx +158 -0
- premiere_cli/panel/host/commands/get-mogrt-component.jsx +111 -0
- premiere_cli/panel/host/commands/get-next-edit-point.jsx +131 -0
- premiere_cli/panel/host/commands/get-offline-media.jsx +67 -0
- premiere_cli/panel/host/commands/get-open-projects.jsx +50 -0
- premiere_cli/panel/host/commands/get-premiere-state.jsx +164 -0
- premiere_cli/panel/host/commands/get-project-info.jsx +63 -0
- premiere_cli/panel/host/commands/get-project-item-info.jsx +247 -0
- premiere_cli/panel/host/commands/get-project-panel-metadata.jsx +30 -0
- premiere_cli/panel/host/commands/get-qe-clip-info.jsx +198 -0
- premiere_cli/panel/host/commands/get-render-queue-status.jsx +35 -0
- premiere_cli/panel/host/commands/get-sequence-count.jsx +24 -0
- premiere_cli/panel/host/commands/get-sequence-in-out.jsx +76 -0
- premiere_cli/panel/host/commands/get-sequence-markers-by-type.jsx +89 -0
- premiere_cli/panel/host/commands/get-source-monitor-info.jsx +34 -0
- premiere_cli/panel/host/commands/get-source-monitor-position.jsx +30 -0
- premiere_cli/panel/host/commands/get-target-tracks.jsx +76 -0
- premiere_cli/panel/host/commands/get-timeline-gaps.jsx +124 -0
- premiere_cli/panel/host/commands/get-timeline-summary.jsx +154 -0
- premiere_cli/panel/host/commands/get-total-clip-count.jsx +67 -0
- premiere_cli/panel/host/commands/get-track-info.jsx +117 -0
- premiere_cli/panel/host/commands/get-unused-media.jsx +99 -0
- premiere_cli/panel/host/commands/get-used-media-report.jsx +125 -0
- premiere_cli/panel/host/commands/get-value-at-time.jsx +135 -0
- premiere_cli/panel/host/commands/get-version-info.jsx +33 -0
- premiere_cli/panel/host/commands/get-work-area.jsx +52 -0
- premiere_cli/panel/host/commands/get-workspaces.jsx +29 -0
- premiere_cli/panel/host/commands/get-xmp-metadata.jsx +107 -0
- premiere_cli/panel/host/commands/import-ae-comps.jsx +129 -0
- premiere_cli/panel/host/commands/import-fcp-xml.jsx +103 -0
- premiere_cli/panel/host/commands/import-folder.jsx +193 -0
- premiere_cli/panel/host/commands/import-image-sequence.jsx +171 -0
- premiere_cli/panel/host/commands/import-media.jsx +217 -0
- premiere_cli/panel/host/commands/import-mogrt-from-library.jsx +105 -0
- premiere_cli/panel/host/commands/import-mogrt.jsx +111 -0
- premiere_cli/panel/host/commands/import-sequences-from-project.jsx +80 -0
- premiere_cli/panel/host/commands/insert-from-source.jsx +162 -0
- premiere_cli/panel/host/commands/inspect-dom-object.jsx +162 -0
- premiere_cli/panel/host/commands/invert-selection.jsx +114 -0
- premiere_cli/panel/host/commands/is-work-area-enabled.jsx +88 -0
- premiere_cli/panel/host/commands/lift-selection.jsx +106 -0
- premiere_cli/panel/host/commands/link-selection.jsx +111 -0
- premiere_cli/panel/host/commands/list-available-audio-effects.jsx +33 -0
- premiere_cli/panel/host/commands/list-available-audio-transitions.jsx +35 -0
- premiere_cli/panel/host/commands/list-available-effects.jsx +33 -0
- premiere_cli/panel/host/commands/list-available-transitions.jsx +74 -0
- premiere_cli/panel/host/commands/list-markers.jsx +69 -0
- premiere_cli/panel/host/commands/list-project-items.jsx +105 -0
- premiere_cli/panel/host/commands/lock-track.jsx +114 -0
- premiere_cli/panel/host/commands/manage-proxies.jsx +176 -0
- premiere_cli/panel/host/commands/match-frame.jsx +141 -0
- premiere_cli/panel/host/commands/move-clip-to-track.jsx +220 -0
- premiere_cli/panel/host/commands/move-clip.jsx +97 -0
- premiere_cli/panel/host/commands/move-items-to-bin.jsx +194 -0
- premiere_cli/panel/host/commands/move-playhead-to-edit.jsx +174 -0
- premiere_cli/panel/host/commands/move-playhead.jsx +137 -0
- premiere_cli/panel/host/commands/nest-clips.jsx +179 -0
- premiere_cli/panel/host/commands/open-in-source.jsx +117 -0
- premiere_cli/panel/host/commands/open-project.jsx +61 -0
- premiere_cli/panel/host/commands/overwrite-clip-at.jsx +350 -0
- premiere_cli/panel/host/commands/overwrite-from-source.jsx +150 -0
- premiere_cli/panel/host/commands/play-source-monitor.jsx +36 -0
- premiere_cli/panel/host/commands/play-timeline.jsx +24 -0
- premiere_cli/panel/host/commands/razor-all-tracks.jsx +171 -0
- premiere_cli/panel/host/commands/redo.jsx +69 -0
- premiere_cli/panel/host/commands/refresh-media.jsx +101 -0
- premiere_cli/panel/host/commands/relink-media.jsx +132 -0
- premiere_cli/panel/host/commands/remove-all-effects.jsx +114 -0
- premiere_cli/panel/host/commands/remove-effect-by-name.jsx +143 -0
- premiere_cli/panel/host/commands/remove-effect.jsx +122 -0
- premiere_cli/panel/host/commands/remove-from-timeline.jsx +93 -0
- premiere_cli/panel/host/commands/remove-keyframe-range.jsx +173 -0
- premiere_cli/panel/host/commands/remove-keyframe.jsx +192 -0
- premiere_cli/panel/host/commands/remove-selected-clips.jsx +126 -0
- premiere_cli/panel/host/commands/remove-track-intervals.jsx +254 -0
- premiere_cli/panel/host/commands/remove-transition.jsx +164 -0
- premiere_cli/panel/host/commands/rename-bin.jsx +88 -0
- premiere_cli/panel/host/commands/rename-clip.jsx +127 -0
- premiere_cli/panel/host/commands/rename-project-item.jsx +111 -0
- premiere_cli/panel/host/commands/rename-track.jsx +84 -0
- premiere_cli/panel/host/commands/replace-clip-media.jsx +140 -0
- premiere_cli/panel/host/commands/replace-clip.jsx +188 -0
- premiere_cli/panel/host/commands/reverse-clip.jsx +187 -0
- premiere_cli/panel/host/commands/ripple-delete-clip.jsx +156 -0
- premiere_cli/panel/host/commands/roll-edit.jsx +162 -0
- premiere_cli/panel/host/commands/save-project-as.jsx +55 -0
- premiere_cli/panel/host/commands/save-project.jsx +57 -0
- premiere_cli/panel/host/commands/scene-edit-detection.jsx +128 -0
- premiere_cli/panel/host/commands/search-project-items.jsx +180 -0
- premiere_cli/panel/host/commands/select-all-clips.jsx +107 -0
- premiere_cli/panel/host/commands/select-clips-by-color.jsx +142 -0
- premiere_cli/panel/host/commands/select-clips-by-name.jsx +133 -0
- premiere_cli/panel/host/commands/select-clips-in-range.jsx +133 -0
- premiere_cli/panel/host/commands/select-disabled-clips.jsx +127 -0
- premiere_cli/panel/host/commands/select-project-item.jsx +115 -0
- premiere_cli/panel/host/commands/set-active-project.jsx +184 -0
- premiere_cli/panel/host/commands/set-active-sequence.jsx +87 -0
- premiere_cli/panel/host/commands/set-all-tracks-targeted.jsx +112 -0
- premiere_cli/panel/host/commands/set-anti-alias-quality.jsx +125 -0
- premiere_cli/panel/host/commands/set-blend-mode.jsx +111 -0
- premiere_cli/panel/host/commands/set-clip-anchor-point.jsx +102 -0
- premiere_cli/panel/host/commands/set-clip-enabled.jsx +127 -0
- premiere_cli/panel/host/commands/set-clip-opacity.jsx +102 -0
- premiere_cli/panel/host/commands/set-clip-pan.jsx +107 -0
- premiere_cli/panel/host/commands/set-clip-position.jsx +103 -0
- premiere_cli/panel/host/commands/set-clip-properties.jsx +221 -0
- premiere_cli/panel/host/commands/set-clip-rotation.jsx +102 -0
- premiere_cli/panel/host/commands/set-clip-scale.jsx +102 -0
- premiere_cli/panel/host/commands/set-clip-selection.jsx +144 -0
- premiere_cli/panel/host/commands/set-clip-speed.jsx +160 -0
- premiere_cli/panel/host/commands/set-clip-volume.jsx +128 -0
- premiere_cli/panel/host/commands/set-color-label.jsx +123 -0
- premiere_cli/panel/host/commands/set-color-value.jsx +155 -0
- premiere_cli/panel/host/commands/set-effect-property.jsx +124 -0
- premiere_cli/panel/host/commands/set-footage-interpretation.jsx +173 -0
- premiere_cli/panel/host/commands/set-frame-blend.jsx +145 -0
- premiere_cli/panel/host/commands/set-graphics-white-luminance.jsx +73 -0
- premiere_cli/panel/host/commands/set-item-in-out.jsx +180 -0
- premiere_cli/panel/host/commands/set-item-metadata.jsx +134 -0
- premiere_cli/panel/host/commands/set-item-offline.jsx +109 -0
- premiere_cli/panel/host/commands/set-item-start-time.jsx +139 -0
- premiere_cli/panel/host/commands/set-keyframe-interpolation.jsx +186 -0
- premiere_cli/panel/host/commands/set-override-frame-rate.jsx +123 -0
- premiere_cli/panel/host/commands/set-override-pixel-aspect-ratio.jsx +129 -0
- premiere_cli/panel/host/commands/set-poster-frame.jsx +192 -0
- premiere_cli/panel/host/commands/set-project-panel-metadata.jsx +69 -0
- premiere_cli/panel/host/commands/set-scale-to-frame-size.jsx +101 -0
- premiere_cli/panel/host/commands/set-scale-width-height.jsx +140 -0
- premiere_cli/panel/host/commands/set-sequence-in-out.jsx +65 -0
- premiere_cli/panel/host/commands/set-sequence-settings.jsx +184 -0
- premiere_cli/panel/host/commands/set-source-in-out.jsx +98 -0
- premiere_cli/panel/host/commands/set-target-track.jsx +91 -0
- premiere_cli/panel/host/commands/set-time-interpolation.jsx +141 -0
- premiere_cli/panel/host/commands/set-track-mute.jsx +91 -0
- premiere_cli/panel/host/commands/set-track-visibility.jsx +94 -0
- premiere_cli/panel/host/commands/set-transcode-on-ingest.jsx +78 -0
- premiere_cli/panel/host/commands/set-uniform-scale.jsx +104 -0
- premiere_cli/panel/host/commands/set-work-area.jsx +76 -0
- premiere_cli/panel/host/commands/set-workspace.jsx +35 -0
- premiere_cli/panel/host/commands/set-xmp-metadata.jsx +157 -0
- premiere_cli/panel/host/commands/slide-edit.jsx +162 -0
- premiere_cli/panel/host/commands/slip-edit.jsx +167 -0
- premiere_cli/panel/host/commands/split-clip.jsx +144 -0
- premiere_cli/panel/host/commands/stabilize-clip.jsx +139 -0
- premiere_cli/panel/host/commands/stop-playback.jsx +23 -0
- premiere_cli/panel/host/commands/trim-clip.jsx +111 -0
- premiere_cli/panel/host/commands/undo.jsx +114 -0
- premiere_cli/panel/host/commands/unlink-selection.jsx +106 -0
- premiere_cli/panel/host/commands/unnest-sequence.jsx +207 -0
- premiere_cli/panel/host/commands/update-marker.jsx +120 -0
- premiere_cli/panel/host/index.jsx +1227 -0
- premiere_cli/panel/host/presets/default-4k-25fps.sqpreset +63 -0
- premiere_cli/panel/index.html +16 -0
- premiere_cli/panel/js/cs-interface.js +11 -0
- premiere_cli/panel/js/main.js +409 -0
- premiere_cli/panel_install.py +220 -0
- premiere_cli-0.2.0.dist-info/METADATA +127 -0
- premiere_cli-0.2.0.dist-info/RECORD +247 -0
- premiere_cli-0.2.0.dist-info/WHEEL +5 -0
- premiere_cli-0.2.0.dist-info/entry_points.txt +3 -0
- premiere_cli-0.2.0.dist-info/licenses/LICENSE +428 -0
- premiere_cli-0.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
// Command: add-transition → ppb_addTransition
|
|
2
|
+
// Loaded lazily by ppb_dispatch (host/index.jsx) on first use, into the
|
|
3
|
+
// same global ExtendScript context — shared helpers (ensureQEEnabled,
|
|
4
|
+
// activateSequenceForQE, qeFindNthNonEmptyClip, ppbApplyTransitionToClip,
|
|
5
|
+
// ...) are already defined there.
|
|
6
|
+
//
|
|
7
|
+
// Merges the reference project's add_transition (track+cut-point
|
|
8
|
+
// addressing) and add_transition_to_clip (node_id+position addressing)
|
|
9
|
+
// into one command, addressed the same way as get-full-clip-info
|
|
10
|
+
// (trackType/trackIndex/clipIndex) rather than either reference's own
|
|
11
|
+
// scheme — video-only (no reference repo demonstrates an audio-transition
|
|
12
|
+
// "add" API). `atEnd: true` applies the transition at the clip's end
|
|
13
|
+
// (matching add_transition_to_clip's "end" position and the cut-point
|
|
14
|
+
// between this clip and its successor); `atEnd: false` applies it at the
|
|
15
|
+
// clip's start. The reference's "both" position is intentionally dropped
|
|
16
|
+
// — call this command twice (once per atEnd value) to reproduce it.
|
|
17
|
+
// `transitionName` omitted/null = the default transition, per ayushozha's
|
|
18
|
+
// addTransition(null, true, "1.0") convention. All the actual apply-and-
|
|
19
|
+
// verify work (disputed addTransition signature, transitions-count
|
|
20
|
+
// verification) lives in the shared ppbApplyTransitionToClip helper.
|
|
21
|
+
function ppb_addTransition(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") {
|
|
31
|
+
return JSON.stringify({ ok: false, error: "trackType must be \"video\" — no reference API demonstrates adding a transition to an audio track" });
|
|
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
|
+
if (typeof args.atEnd !== "boolean") {
|
|
40
|
+
return JSON.stringify({ ok: false, error: "atEnd must be a boolean (true = end of clip, false = start of clip)" });
|
|
41
|
+
}
|
|
42
|
+
var transitionName = null;
|
|
43
|
+
if (typeof args.transitionName === "string" && args.transitionName.length > 0) {
|
|
44
|
+
transitionName = args.transitionName;
|
|
45
|
+
} else if (args.transitionName !== undefined && args.transitionName !== null) {
|
|
46
|
+
return JSON.stringify({ ok: false, error: "transitionName must be a non-empty string, or omitted/null for the default transition" });
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Live-tested 2026-07-17: the null-transition ("default") form
|
|
50
|
+
// no-throws but adds NOTHING on this build, while a NAMED transition
|
|
51
|
+
// works and verifies. Substitute Cross Dissolve (Premiere's factory
|
|
52
|
+
// default) rather than burning attempts on a known-dead path.
|
|
53
|
+
if (transitionName === null) {
|
|
54
|
+
transitionName = "Cross Dissolve";
|
|
55
|
+
}
|
|
56
|
+
var durationSeconds = 1.0;
|
|
57
|
+
if (typeof args.durationSeconds === "number") {
|
|
58
|
+
if (args.durationSeconds <= 0) {
|
|
59
|
+
return JSON.stringify({ ok: false, error: "durationSeconds must be a positive number" });
|
|
60
|
+
}
|
|
61
|
+
durationSeconds = args.durationSeconds;
|
|
62
|
+
} else if (args.durationSeconds !== undefined && args.durationSeconds !== null) {
|
|
63
|
+
return JSON.stringify({ ok: false, error: "durationSeconds must be a number" });
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (!app.project) {
|
|
67
|
+
return JSON.stringify({ ok: false, error: "no project open" });
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
var seq = null;
|
|
71
|
+
if (args.sequenceName && typeof args.sequenceName === "string") {
|
|
72
|
+
seq = findSequenceByName(args.sequenceName);
|
|
73
|
+
if (!seq) {
|
|
74
|
+
return JSON.stringify({ ok: false, error: "no sequence named \"" + args.sequenceName + "\" is open" });
|
|
75
|
+
}
|
|
76
|
+
} else {
|
|
77
|
+
seq = app.project.activeSequence;
|
|
78
|
+
if (!seq) {
|
|
79
|
+
return JSON.stringify({ ok: false, error: "no active sequence, and no sequenceName given" });
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
ensureQEEnabled();
|
|
85
|
+
activateSequenceForQE(seq);
|
|
86
|
+
} catch (e2) {
|
|
87
|
+
return JSON.stringify({ ok: false, error: "app.enableQE()/sequence activation failed: " + e2.toString() });
|
|
88
|
+
}
|
|
89
|
+
if (typeof qe === "undefined" || !qe.project) {
|
|
90
|
+
return JSON.stringify({ ok: false, error: "QE DOM not available after enableQE()" });
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
var transitionQE = null;
|
|
94
|
+
if (transitionName !== null) {
|
|
95
|
+
try {
|
|
96
|
+
if (qe.project.getVideoTransitionByName) {
|
|
97
|
+
transitionQE = qe.project.getVideoTransitionByName(transitionName);
|
|
98
|
+
}
|
|
99
|
+
} catch (e3) {
|
|
100
|
+
transitionQE = null;
|
|
101
|
+
}
|
|
102
|
+
if (!transitionQE) {
|
|
103
|
+
return JSON.stringify({ ok: false, error: "transition not found: \"" + transitionName + "\" (try list-available-transitions)" });
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
var applyResult = ppbApplyTransitionToClip(seq, args.trackIndex, args.clipIndex, transitionQE, args.atEnd, durationSeconds);
|
|
108
|
+
|
|
109
|
+
var result = {
|
|
110
|
+
sequenceName: seq.name,
|
|
111
|
+
trackType: "video",
|
|
112
|
+
trackIndex: args.trackIndex,
|
|
113
|
+
clipIndex: args.clipIndex,
|
|
114
|
+
clipName: applyResult.clipName,
|
|
115
|
+
transitionName: transitionName,
|
|
116
|
+
atEnd: args.atEnd,
|
|
117
|
+
durationSeconds: durationSeconds,
|
|
118
|
+
previousTransitionCount: applyResult.previousCount,
|
|
119
|
+
newTransitionCount: applyResult.newCount,
|
|
120
|
+
addedTransitionName: applyResult.addedTransitionName,
|
|
121
|
+
verified: applyResult.verified,
|
|
122
|
+
attempts: applyResult.attempts,
|
|
123
|
+
succeededWithArgs: applyResult.succeededWithArgs
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
if (!applyResult.verified) {
|
|
127
|
+
return JSON.stringify({ ok: false, error: applyResult.error, result: result });
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return JSON.stringify({ ok: true, result: result });
|
|
131
|
+
} catch (e) {
|
|
132
|
+
return JSON.stringify({ ok: false, error: e.toString() });
|
|
133
|
+
}
|
|
134
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// Command: adjust-audio-levels → ppb_adjustAudioLevels
|
|
2
|
+
// Loaded lazily by ppb_dispatch (host/index.jsx) on first use, into the
|
|
3
|
+
// same global ExtendScript context — shared helpers (findSequenceByName,
|
|
4
|
+
// findVolumeLevelProperty, dbToLinearCalibrated, linearToDbCalibrated, ...)
|
|
5
|
+
// are already defined there.
|
|
6
|
+
//
|
|
7
|
+
// Standard-DOM mutation. Ported from leancoderkavy's premiere-pro-mcp
|
|
8
|
+
// `adjust_audio_levels` tool (audio.ts) — but note that reference handler
|
|
9
|
+
// actually SETS the Level property directly to the raw `level_db` argument
|
|
10
|
+
// (an absolute set, same bug as set_clip_volume's reference, and also not
|
|
11
|
+
// really "adjust" semantics despite the tool's name). This command instead
|
|
12
|
+
// implements true relative-adjustment semantics: read the clip's current
|
|
13
|
+
// linear Level, convert it to an estimated dB via the same calibration
|
|
14
|
+
// formula as set-clip-volume, add the requested `db` delta, convert back to
|
|
15
|
+
// linear, and apply. See set-clip-volume.jsx's header for the calibration-
|
|
16
|
+
// uncertainty caveat — it applies here identically, compounded because this
|
|
17
|
+
// command both reads AND writes through the same unverified formula.
|
|
18
|
+
function ppb_adjustAudioLevels(argsJson) {
|
|
19
|
+
try {
|
|
20
|
+
var args;
|
|
21
|
+
try {
|
|
22
|
+
args = JSON.parse(argsJson);
|
|
23
|
+
} catch (e) {
|
|
24
|
+
return JSON.stringify({ ok: false, error: "invalid args JSON: " + e.toString() });
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (args.trackType !== "video" && args.trackType !== "audio") {
|
|
28
|
+
return JSON.stringify({ ok: false, error: "trackType must be \"video\" or \"audio\"" });
|
|
29
|
+
}
|
|
30
|
+
if (typeof args.trackIndex !== "number" || args.trackIndex < 0 || Math.floor(args.trackIndex) !== args.trackIndex) {
|
|
31
|
+
return JSON.stringify({ ok: false, error: "trackIndex must be a non-negative integer" });
|
|
32
|
+
}
|
|
33
|
+
if (typeof args.clipIndex !== "number" || args.clipIndex < 0 || Math.floor(args.clipIndex) !== args.clipIndex) {
|
|
34
|
+
return JSON.stringify({ ok: false, error: "clipIndex must be a non-negative integer" });
|
|
35
|
+
}
|
|
36
|
+
if (typeof args.db !== "number" || isNaN(args.db)) {
|
|
37
|
+
return JSON.stringify({ ok: false, error: "db must be a number (a DELTA relative to the clip's current level)" });
|
|
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 trackCollection = args.trackType === "video" ? seq.videoTracks : seq.audioTracks;
|
|
58
|
+
var numTracks = trackCollection.numTracks;
|
|
59
|
+
if (args.trackIndex >= numTracks) {
|
|
60
|
+
return JSON.stringify({
|
|
61
|
+
ok: false,
|
|
62
|
+
error: "trackIndex " + args.trackIndex + " is out of range — sequence has " + numTracks + " " + args.trackType + " track(s)"
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
var track = trackCollection[args.trackIndex];
|
|
67
|
+
var numClips = track.clips.numItems;
|
|
68
|
+
if (args.clipIndex >= numClips) {
|
|
69
|
+
return JSON.stringify({
|
|
70
|
+
ok: false,
|
|
71
|
+
error: "clipIndex " + args.clipIndex + " is out of range — track " + args.trackIndex + " has " + numClips + " clip(s)"
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
var clip = track.clips[args.clipIndex];
|
|
76
|
+
var clipName = null;
|
|
77
|
+
try { clipName = clip.name; } catch (e) { clipName = null; }
|
|
78
|
+
|
|
79
|
+
var levelProp = findVolumeLevelProperty(clip);
|
|
80
|
+
if (!levelProp) {
|
|
81
|
+
return JSON.stringify({ ok: false, error: "could not find Volume/Level property on clip — is this an audio clip?" });
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
var previousLinear = null;
|
|
85
|
+
try { previousLinear = levelProp.getValue(); } catch (e) { previousLinear = null; }
|
|
86
|
+
if (previousLinear === null) {
|
|
87
|
+
return JSON.stringify({ ok: false, error: "could not read the clip's current Level value" });
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
var previousDbEstimate = linearToDbCalibrated(previousLinear);
|
|
91
|
+
if (previousDbEstimate === null) {
|
|
92
|
+
return JSON.stringify({ ok: false, error: "current Level value (" + previousLinear + ") is not a valid positive amplitude — cannot estimate its dB" });
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
var requestedDbTotal = previousDbEstimate + args.db;
|
|
96
|
+
var requestedLinear = dbToLinearCalibrated(requestedDbTotal);
|
|
97
|
+
|
|
98
|
+
try {
|
|
99
|
+
levelProp.setValue(requestedLinear, true);
|
|
100
|
+
} catch (e) {
|
|
101
|
+
return JSON.stringify({ ok: false, error: "levelProp.setValue() failed: " + e.toString() });
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
var newLinear = null;
|
|
105
|
+
try { newLinear = levelProp.getValue(); } catch (e) { newLinear = null; }
|
|
106
|
+
|
|
107
|
+
var verified = newLinear !== null && Math.abs(newLinear - requestedLinear) < 0.0005;
|
|
108
|
+
|
|
109
|
+
var result = {
|
|
110
|
+
sequenceName: seq.name,
|
|
111
|
+
trackType: args.trackType,
|
|
112
|
+
trackIndex: args.trackIndex,
|
|
113
|
+
clipIndex: args.clipIndex,
|
|
114
|
+
clipName: clipName,
|
|
115
|
+
requestedDeltaDb: args.db,
|
|
116
|
+
previousValue: previousLinear,
|
|
117
|
+
previousDbEstimate: previousDbEstimate,
|
|
118
|
+
requestedValue: requestedLinear,
|
|
119
|
+
requestedDbTotal: requestedDbTotal,
|
|
120
|
+
newValue: newLinear,
|
|
121
|
+
verified: verified,
|
|
122
|
+
calibrationNote: "Level is LINEAR AMPLITUDE, not dB — this command estimates the clip's " +
|
|
123
|
+
"current dB from its linear value via hetpatel's UNVERIFIED calibration formula " +
|
|
124
|
+
"(linear = 10^((db-15)/20)), adds the requested delta, then converts back. Compounds " +
|
|
125
|
+
"the same calibration uncertainty documented in set-clip-volume.jsx."
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
return JSON.stringify({ ok: true, result: result });
|
|
129
|
+
} catch (e) {
|
|
130
|
+
return JSON.stringify({ ok: false, error: e.toString() });
|
|
131
|
+
}
|
|
132
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
// Command: apply-audio-effect → ppb_applyAudioEffect
|
|
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, serializeClipComponents, findQeAudioEffectByName, ...) are
|
|
6
|
+
// already defined there.
|
|
7
|
+
//
|
|
8
|
+
// Audio counterpart of apply-effect: qe.project.getAudioEffectByName() (or a
|
|
9
|
+
// getAudioEffectList() scan fallback) + qeClip.addAudioEffect(fx). Same
|
|
10
|
+
// verification approach — clip.components.numItems increasing by exactly 1,
|
|
11
|
+
// never trusting addAudioEffect()'s own return value.
|
|
12
|
+
function ppb_applyAudioEffect(argsJson) {
|
|
13
|
+
try {
|
|
14
|
+
var args;
|
|
15
|
+
try {
|
|
16
|
+
args = JSON.parse(argsJson);
|
|
17
|
+
} catch (e) {
|
|
18
|
+
return JSON.stringify({ ok: false, error: "invalid args JSON: " + e.toString() });
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (args.trackType !== "audio") {
|
|
22
|
+
return JSON.stringify({ ok: false, error: "trackType must be \"audio\" — apply-audio-effect applies an AUDIO effect (use apply-effect for video clips)" });
|
|
23
|
+
}
|
|
24
|
+
if (typeof args.trackIndex !== "number" || args.trackIndex < 0 || Math.floor(args.trackIndex) !== args.trackIndex) {
|
|
25
|
+
return JSON.stringify({ ok: false, error: "trackIndex must be a non-negative integer" });
|
|
26
|
+
}
|
|
27
|
+
if (typeof args.clipIndex !== "number" || args.clipIndex < 0 || Math.floor(args.clipIndex) !== args.clipIndex) {
|
|
28
|
+
return JSON.stringify({ ok: false, error: "clipIndex must be a non-negative integer" });
|
|
29
|
+
}
|
|
30
|
+
if (typeof args.effectName !== "string" || !args.effectName) {
|
|
31
|
+
return JSON.stringify({ ok: false, error: "effectName must be a non-empty string" });
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (!app.project) {
|
|
35
|
+
return JSON.stringify({ ok: false, error: "no project open" });
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
var seq = null;
|
|
39
|
+
if (args.sequenceName && typeof args.sequenceName === "string") {
|
|
40
|
+
seq = findSequenceByName(args.sequenceName);
|
|
41
|
+
if (!seq) {
|
|
42
|
+
return JSON.stringify({ ok: false, error: "no sequence named \"" + args.sequenceName + "\" is open" });
|
|
43
|
+
}
|
|
44
|
+
} else {
|
|
45
|
+
seq = app.project.activeSequence;
|
|
46
|
+
if (!seq) {
|
|
47
|
+
return JSON.stringify({ ok: false, error: "no active sequence, and no sequenceName given" });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
var resolvedClip = resolveTimelineClip(seq, "audio", args.trackIndex, args.clipIndex);
|
|
52
|
+
if (resolvedClip.error) {
|
|
53
|
+
return JSON.stringify({ ok: false, error: resolvedClip.error });
|
|
54
|
+
}
|
|
55
|
+
var clip = resolvedClip.clip;
|
|
56
|
+
var clipName = null;
|
|
57
|
+
try { clipName = clip.name; } catch (e) { clipName = null; }
|
|
58
|
+
|
|
59
|
+
var previousComponents = serializeClipComponents(clip);
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
ensureQEEnabled();
|
|
63
|
+
activateSequenceForQE(seq);
|
|
64
|
+
} catch (e) {
|
|
65
|
+
return JSON.stringify({ ok: false, error: "app.enableQE()/sequence activation failed: " + e.toString() });
|
|
66
|
+
}
|
|
67
|
+
if (typeof qe === "undefined" || !qe.project) {
|
|
68
|
+
return JSON.stringify({ ok: false, error: "QE DOM not available after enableQE()" });
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
var qeSeq;
|
|
72
|
+
try {
|
|
73
|
+
qeSeq = qe.project.getActiveSequence();
|
|
74
|
+
} catch (e) {
|
|
75
|
+
return JSON.stringify({ ok: false, error: "qe.project.getActiveSequence() failed: " + e.toString() });
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
var qeResolved = resolveQeClip(qeSeq, "audio", args.trackIndex, args.clipIndex);
|
|
79
|
+
if (qeResolved.error) {
|
|
80
|
+
return JSON.stringify({ ok: false, error: qeResolved.error });
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
var fx = findQeAudioEffectByName(args.effectName);
|
|
84
|
+
if (!fx) {
|
|
85
|
+
return JSON.stringify({ ok: false, error: "audio effect not found in this Premiere install's QE effect catalog: \"" + args.effectName + "\"" });
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
qeResolved.qeClip.addAudioEffect(fx);
|
|
90
|
+
} catch (e) {
|
|
91
|
+
return JSON.stringify({ ok: false, error: "qeClip.addAudioEffect() failed: " + e.toString() });
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
var newComponents = serializeClipComponents(clip);
|
|
95
|
+
var verified = newComponents.length === previousComponents.length + 1;
|
|
96
|
+
var newComponent = verified ? newComponents[newComponents.length - 1] : null;
|
|
97
|
+
|
|
98
|
+
return JSON.stringify({
|
|
99
|
+
ok: true,
|
|
100
|
+
result: {
|
|
101
|
+
sequenceName: seq.name,
|
|
102
|
+
trackType: "audio",
|
|
103
|
+
trackIndex: args.trackIndex,
|
|
104
|
+
clipIndex: args.clipIndex,
|
|
105
|
+
clipName: clipName,
|
|
106
|
+
effectName: args.effectName,
|
|
107
|
+
verified: verified,
|
|
108
|
+
newComponent: newComponent,
|
|
109
|
+
previousComponentCount: previousComponents.length,
|
|
110
|
+
newComponentCount: newComponents.length,
|
|
111
|
+
previousComponents: previousComponents,
|
|
112
|
+
newComponents: newComponents
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
} catch (e) {
|
|
116
|
+
return JSON.stringify({ ok: false, error: e.toString() });
|
|
117
|
+
}
|
|
118
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
// Command: apply-effect → ppb_applyEffect
|
|
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, serializeClipComponents, findQeVideoEffectByName, ...) are
|
|
6
|
+
// already defined there.
|
|
7
|
+
//
|
|
8
|
+
// Applies a video effect to a clip via the "universal dance" documented in
|
|
9
|
+
// PREMIERE_API_NOTES.md's "Effects, transitions, keyframes" section:
|
|
10
|
+
// look up the effect in the QE catalog (getVideoEffectByName, falling back
|
|
11
|
+
// to a getVideoEffectList() scan), call qeClip.addVideoEffect(fx), then read
|
|
12
|
+
// the standard-DOM clip.components back — the new component is appended
|
|
13
|
+
// LAST. Mutation is verified by clip.components.numItems increasing by
|
|
14
|
+
// exactly 1, not by trusting addVideoEffect()'s return value (undocumented
|
|
15
|
+
// on this build, like every other QE mutation call here).
|
|
16
|
+
function ppb_applyEffect(argsJson) {
|
|
17
|
+
try {
|
|
18
|
+
var args;
|
|
19
|
+
try {
|
|
20
|
+
args = JSON.parse(argsJson);
|
|
21
|
+
} catch (e) {
|
|
22
|
+
return JSON.stringify({ ok: false, error: "invalid args JSON: " + e.toString() });
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (args.trackType !== "video") {
|
|
26
|
+
return JSON.stringify({ ok: false, error: "trackType must be \"video\" — apply-effect applies a VIDEO effect (use apply-audio-effect for audio clips)" });
|
|
27
|
+
}
|
|
28
|
+
if (typeof args.trackIndex !== "number" || args.trackIndex < 0 || Math.floor(args.trackIndex) !== args.trackIndex) {
|
|
29
|
+
return JSON.stringify({ ok: false, error: "trackIndex must be a non-negative integer" });
|
|
30
|
+
}
|
|
31
|
+
if (typeof args.clipIndex !== "number" || args.clipIndex < 0 || Math.floor(args.clipIndex) !== args.clipIndex) {
|
|
32
|
+
return JSON.stringify({ ok: false, error: "clipIndex must be a non-negative integer" });
|
|
33
|
+
}
|
|
34
|
+
if (typeof args.effectName !== "string" || !args.effectName) {
|
|
35
|
+
return JSON.stringify({ ok: false, error: "effectName must be a non-empty string" });
|
|
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 resolvedClip = resolveTimelineClip(seq, "video", args.trackIndex, args.clipIndex);
|
|
56
|
+
if (resolvedClip.error) {
|
|
57
|
+
return JSON.stringify({ ok: false, error: resolvedClip.error });
|
|
58
|
+
}
|
|
59
|
+
var clip = resolvedClip.clip;
|
|
60
|
+
var clipName = null;
|
|
61
|
+
try { clipName = clip.name; } catch (e) { clipName = null; }
|
|
62
|
+
|
|
63
|
+
var previousComponents = serializeClipComponents(clip);
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
ensureQEEnabled();
|
|
67
|
+
activateSequenceForQE(seq);
|
|
68
|
+
} catch (e) {
|
|
69
|
+
return JSON.stringify({ ok: false, error: "app.enableQE()/sequence activation failed: " + e.toString() });
|
|
70
|
+
}
|
|
71
|
+
if (typeof qe === "undefined" || !qe.project) {
|
|
72
|
+
return JSON.stringify({ ok: false, error: "QE DOM not available after enableQE()" });
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
var qeSeq;
|
|
76
|
+
try {
|
|
77
|
+
qeSeq = qe.project.getActiveSequence();
|
|
78
|
+
} catch (e) {
|
|
79
|
+
return JSON.stringify({ ok: false, error: "qe.project.getActiveSequence() failed: " + e.toString() });
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
var qeResolved = resolveQeClip(qeSeq, "video", args.trackIndex, args.clipIndex);
|
|
83
|
+
if (qeResolved.error) {
|
|
84
|
+
return JSON.stringify({ ok: false, error: qeResolved.error });
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
var fx = findQeVideoEffectByName(args.effectName);
|
|
88
|
+
if (!fx) {
|
|
89
|
+
return JSON.stringify({ ok: false, error: "video effect not found in this Premiere install's QE effect catalog: \"" + args.effectName + "\"" });
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
try {
|
|
93
|
+
qeResolved.qeClip.addVideoEffect(fx);
|
|
94
|
+
} catch (e) {
|
|
95
|
+
return JSON.stringify({ ok: false, error: "qeClip.addVideoEffect() failed: " + e.toString() });
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
var newComponents = serializeClipComponents(clip);
|
|
99
|
+
var verified = newComponents.length === previousComponents.length + 1;
|
|
100
|
+
var newComponent = verified ? newComponents[newComponents.length - 1] : null;
|
|
101
|
+
|
|
102
|
+
return JSON.stringify({
|
|
103
|
+
ok: true,
|
|
104
|
+
result: {
|
|
105
|
+
sequenceName: seq.name,
|
|
106
|
+
trackType: "video",
|
|
107
|
+
trackIndex: args.trackIndex,
|
|
108
|
+
clipIndex: args.clipIndex,
|
|
109
|
+
clipName: clipName,
|
|
110
|
+
effectName: args.effectName,
|
|
111
|
+
verified: verified,
|
|
112
|
+
newComponent: newComponent,
|
|
113
|
+
previousComponentCount: previousComponents.length,
|
|
114
|
+
newComponentCount: newComponents.length,
|
|
115
|
+
previousComponents: previousComponents,
|
|
116
|
+
newComponents: newComponents
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
} catch (e) {
|
|
120
|
+
return JSON.stringify({ ok: false, error: e.toString() });
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// Command: apply-lut → ppb_applyLut
|
|
2
|
+
// Loaded lazily by ppb_dispatch (host/index.jsx) on first use, into the
|
|
3
|
+
// same global ExtendScript context — shared helpers (findSequenceByName,
|
|
4
|
+
// resolveTimelineClip, serializeClipComponents,
|
|
5
|
+
// ensureLumetriColorComponent, findComponentProperty, ...) are already
|
|
6
|
+
// defined there.
|
|
7
|
+
//
|
|
8
|
+
// Ensures Lumetri Color is applied (adding it if missing, same helper
|
|
9
|
+
// color-correct uses), then sets its "Input LUT" property to a path string
|
|
10
|
+
// per PREMIERE_API_NOTES.md. This is a plain string setValue — no numeric
|
|
11
|
+
// falsy-check concern here, but the same "0 is legit" caveat from
|
|
12
|
+
// color-correct doesn't apply since a LUT path is never numeric.
|
|
13
|
+
function ppb_applyLut(argsJson) {
|
|
14
|
+
try {
|
|
15
|
+
var args;
|
|
16
|
+
try {
|
|
17
|
+
args = JSON.parse(argsJson);
|
|
18
|
+
} catch (e) {
|
|
19
|
+
return JSON.stringify({ ok: false, error: "invalid args JSON: " + e.toString() });
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (args.trackType !== "video") {
|
|
23
|
+
return JSON.stringify({ ok: false, error: "trackType must be \"video\" — Lumetri Color is a video effect" });
|
|
24
|
+
}
|
|
25
|
+
if (typeof args.trackIndex !== "number" || args.trackIndex < 0 || Math.floor(args.trackIndex) !== args.trackIndex) {
|
|
26
|
+
return JSON.stringify({ ok: false, error: "trackIndex must be a non-negative integer" });
|
|
27
|
+
}
|
|
28
|
+
if (typeof args.clipIndex !== "number" || args.clipIndex < 0 || Math.floor(args.clipIndex) !== args.clipIndex) {
|
|
29
|
+
return JSON.stringify({ ok: false, error: "clipIndex must be a non-negative integer" });
|
|
30
|
+
}
|
|
31
|
+
if (typeof args.lutPath !== "string" || !args.lutPath) {
|
|
32
|
+
return JSON.stringify({ ok: false, error: "lutPath must be a non-empty string" });
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (!app.project) {
|
|
36
|
+
return JSON.stringify({ ok: false, error: "no project open" });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
var seq = null;
|
|
40
|
+
if (args.sequenceName && typeof args.sequenceName === "string") {
|
|
41
|
+
seq = findSequenceByName(args.sequenceName);
|
|
42
|
+
if (!seq) {
|
|
43
|
+
return JSON.stringify({ ok: false, error: "no sequence named \"" + args.sequenceName + "\" is open" });
|
|
44
|
+
}
|
|
45
|
+
} else {
|
|
46
|
+
seq = app.project.activeSequence;
|
|
47
|
+
if (!seq) {
|
|
48
|
+
return JSON.stringify({ ok: false, error: "no active sequence, and no sequenceName given" });
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
var resolvedClip = resolveTimelineClip(seq, "video", args.trackIndex, args.clipIndex);
|
|
53
|
+
if (resolvedClip.error) {
|
|
54
|
+
return JSON.stringify({ ok: false, error: resolvedClip.error });
|
|
55
|
+
}
|
|
56
|
+
var clip = resolvedClip.clip;
|
|
57
|
+
var clipName = null;
|
|
58
|
+
try { clipName = clip.name; } catch (e) { clipName = null; }
|
|
59
|
+
|
|
60
|
+
var previousComponents = serializeClipComponents(clip);
|
|
61
|
+
|
|
62
|
+
var lumetri = ensureLumetriColorComponent(seq, "video", args.trackIndex, args.clipIndex, clip);
|
|
63
|
+
if (lumetri.error) {
|
|
64
|
+
return JSON.stringify({ ok: false, error: lumetri.error });
|
|
65
|
+
}
|
|
66
|
+
var lumetriComp = lumetri.component;
|
|
67
|
+
|
|
68
|
+
var prop = findComponentProperty(lumetriComp, ["Input LUT"]);
|
|
69
|
+
if (!prop) {
|
|
70
|
+
return JSON.stringify({ ok: false, error: "could not find an \"Input LUT\" property on Lumetri Color" });
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
var previousValue = null;
|
|
74
|
+
try { previousValue = prop.getValue(); } catch (e) { previousValue = null; }
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
prop.setValue(args.lutPath, true);
|
|
78
|
+
} catch (e) {
|
|
79
|
+
return JSON.stringify({ ok: false, error: "prop.setValue(lutPath) failed: " + e.toString() });
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
var newValue = null;
|
|
83
|
+
try { newValue = prop.getValue(); } catch (e) { newValue = null; }
|
|
84
|
+
|
|
85
|
+
var newComponents = serializeClipComponents(clip);
|
|
86
|
+
|
|
87
|
+
return JSON.stringify({
|
|
88
|
+
ok: true,
|
|
89
|
+
result: {
|
|
90
|
+
sequenceName: seq.name,
|
|
91
|
+
trackType: "video",
|
|
92
|
+
trackIndex: args.trackIndex,
|
|
93
|
+
clipIndex: args.clipIndex,
|
|
94
|
+
clipName: clipName,
|
|
95
|
+
lumetriApplied: lumetri.applied,
|
|
96
|
+
previousValue: previousValue,
|
|
97
|
+
requestedValue: args.lutPath,
|
|
98
|
+
newValue: newValue,
|
|
99
|
+
verified: newValue === args.lutPath,
|
|
100
|
+
previousComponents: previousComponents,
|
|
101
|
+
newComponents: newComponents
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
} catch (e) {
|
|
105
|
+
return JSON.stringify({ ok: false, error: e.toString() });
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// Command: attach-custom-property → ppb_attachCustomProperty
|
|
2
|
+
// Loaded lazily by ppb_dispatch (host/index.jsx) on first use, into the
|
|
3
|
+
// same global ExtendScript context — shared helpers (findSequenceByName,
|
|
4
|
+
// ...) are already defined there.
|
|
5
|
+
//
|
|
6
|
+
// Ported from leancoderkavy/premiere-pro-mcp's sequence.ts
|
|
7
|
+
// attach_custom_property tool: `seq.attachCustomProperty(id, value)` per
|
|
8
|
+
// PREMIERE_API_NOTES.md's Sequences table. No confirmed getter/read-back
|
|
9
|
+
// API exists for custom sequence properties in ANY of the reference
|
|
10
|
+
// repos studied — this command can only confirm the setter call didn't
|
|
11
|
+
// throw, never that the value actually persisted or is retrievable
|
|
12
|
+
// later. value is coerced to a string (attachCustomProperty's own
|
|
13
|
+
// signature is key/value strings per the reference tool).
|
|
14
|
+
function ppb_attachCustomProperty(argsJson) {
|
|
15
|
+
try {
|
|
16
|
+
var args;
|
|
17
|
+
try {
|
|
18
|
+
args = JSON.parse(argsJson);
|
|
19
|
+
} catch (e) {
|
|
20
|
+
return JSON.stringify({ ok: false, error: "invalid args JSON: " + e.toString() });
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (!app.project) {
|
|
24
|
+
return JSON.stringify({ ok: false, error: "no project open" });
|
|
25
|
+
}
|
|
26
|
+
if (!args.propertyId || typeof args.propertyId !== "string") {
|
|
27
|
+
return JSON.stringify({ ok: false, error: "propertyId is required" });
|
|
28
|
+
}
|
|
29
|
+
if (typeof args.value === "undefined" || args.value === null) {
|
|
30
|
+
return JSON.stringify({ ok: false, error: "value is required" });
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
var seq;
|
|
34
|
+
if (args.sequenceName && typeof args.sequenceName === "string") {
|
|
35
|
+
seq = findSequenceByName(args.sequenceName);
|
|
36
|
+
if (!seq) {
|
|
37
|
+
return JSON.stringify({ ok: false, error: "no sequence named \"" + args.sequenceName + "\" is open" });
|
|
38
|
+
}
|
|
39
|
+
} else {
|
|
40
|
+
seq = app.project.activeSequence;
|
|
41
|
+
if (!seq) {
|
|
42
|
+
return JSON.stringify({ ok: false, error: "no active sequence, and no sequenceName given" });
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
var valueString = String(args.value);
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
seq.attachCustomProperty(args.propertyId, valueString);
|
|
50
|
+
} catch (e) {
|
|
51
|
+
return JSON.stringify({ ok: false, error: "seq.attachCustomProperty() failed: " + e.toString() });
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return JSON.stringify({
|
|
55
|
+
ok: true,
|
|
56
|
+
result: {
|
|
57
|
+
sequenceName: seq.name,
|
|
58
|
+
propertyId: args.propertyId,
|
|
59
|
+
value: valueString,
|
|
60
|
+
note: "no confirmed getter exists for custom sequence properties across any reference repo studied — this result confirms only that attachCustomProperty() did not throw, not that the value actually persisted or is retrievable"
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
} catch (e) {
|
|
64
|
+
return JSON.stringify({ ok: false, error: e.toString() });
|
|
65
|
+
}
|
|
66
|
+
}
|