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,124 @@
|
|
|
1
|
+
// Command: auto-reframe-sequence → ppb_autoReframeSequence
|
|
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
|
+
// PREMIERE_API_NOTES.md's Sequences table documents the 5-arg form:
|
|
7
|
+
// `seq.autoReframeSequence(num, den, motionPreset, newName, nestBool)`
|
|
8
|
+
// ("2020+ only") — that is what this command calls. This differs from
|
|
9
|
+
// leancoderkavy's own advanced.ts auto_reframe_sequence tool, which instead
|
|
10
|
+
// calls a 3-arg `seq.autoReframeSequence(targetWidth, targetHeight, nestBool)`
|
|
11
|
+
// with no motionPreset/newName. The 5-arg form is used here per the task
|
|
12
|
+
// spec and PREMIERE_API_NOTES.md; numerator/denominator are taken to be an
|
|
13
|
+
// ASPECT RATIO (e.g. 9/16 for vertical), not pixel dimensions — UNCONFIRMED
|
|
14
|
+
// against this build (no live test performed).
|
|
15
|
+
//
|
|
16
|
+
// Unlike createSubsequence/clone(), autoReframeSequence takes the new
|
|
17
|
+
// sequence's name directly as an argument — no post-hoc rename step is
|
|
18
|
+
// needed, but the new sequence is still located by a fresh
|
|
19
|
+
// findSequenceByName() call afterward rather than trusting the call's own
|
|
20
|
+
// (undocumented) return value.
|
|
21
|
+
function ppb_autoReframeSequence(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 (!app.project) {
|
|
31
|
+
return JSON.stringify({ ok: false, error: "no project open" });
|
|
32
|
+
}
|
|
33
|
+
if (typeof args.numerator !== "number" || args.numerator <= 0) {
|
|
34
|
+
return JSON.stringify({ ok: false, error: "numerator must be a positive number" });
|
|
35
|
+
}
|
|
36
|
+
if (typeof args.denominator !== "number" || args.denominator <= 0) {
|
|
37
|
+
return JSON.stringify({ ok: false, error: "denominator must be a positive number" });
|
|
38
|
+
}
|
|
39
|
+
var validPresets = { slower: true, "default": true, faster: true };
|
|
40
|
+
if (!args.motionPreset || !validPresets[args.motionPreset]) {
|
|
41
|
+
return JSON.stringify({ ok: false, error: "motionPreset must be one of slower, default, faster" });
|
|
42
|
+
}
|
|
43
|
+
if (!args.newName || typeof args.newName !== "string") {
|
|
44
|
+
return JSON.stringify({ ok: false, error: "newName is required" });
|
|
45
|
+
}
|
|
46
|
+
if (typeof args.nest !== "boolean") {
|
|
47
|
+
return JSON.stringify({ ok: false, error: "nest (boolean) is required" });
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
var seq;
|
|
51
|
+
if (args.sequenceName && typeof args.sequenceName === "string") {
|
|
52
|
+
seq = findSequenceByName(args.sequenceName);
|
|
53
|
+
if (!seq) {
|
|
54
|
+
return JSON.stringify({ ok: false, error: "no sequence named \"" + args.sequenceName + "\" is open" });
|
|
55
|
+
}
|
|
56
|
+
} else {
|
|
57
|
+
seq = app.project.activeSequence;
|
|
58
|
+
if (!seq) {
|
|
59
|
+
return JSON.stringify({ ok: false, error: "no active sequence, and no sequenceName given" });
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
var numSequencesBefore = app.project.sequences.numSequences;
|
|
64
|
+
|
|
65
|
+
var callError = null;
|
|
66
|
+
try {
|
|
67
|
+
seq.autoReframeSequence(args.numerator, args.denominator, args.motionPreset, args.newName, args.nest);
|
|
68
|
+
} catch (e) {
|
|
69
|
+
callError = e.toString();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
var numSequencesAfter = app.project.sequences.numSequences;
|
|
73
|
+
|
|
74
|
+
if (callError !== null) {
|
|
75
|
+
return JSON.stringify({
|
|
76
|
+
ok: false,
|
|
77
|
+
error: "seq.autoReframeSequence() failed: " + callError,
|
|
78
|
+
numSequencesBefore: numSequencesBefore,
|
|
79
|
+
numSequencesAfter: numSequencesAfter
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
var newSeq = findSequenceByName(args.newName);
|
|
84
|
+
if (!newSeq) {
|
|
85
|
+
return JSON.stringify({
|
|
86
|
+
ok: false,
|
|
87
|
+
error: "autoReframeSequence() did not throw, but no sequence named \"" + args.newName + "\" was found afterward",
|
|
88
|
+
numSequencesBefore: numSequencesBefore,
|
|
89
|
+
numSequencesAfter: numSequencesAfter
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
var newWidth = null;
|
|
94
|
+
var newHeight = null;
|
|
95
|
+
try {
|
|
96
|
+
var s = newSeq.getSettings();
|
|
97
|
+
newWidth = s.videoFrameWidth;
|
|
98
|
+
newHeight = s.videoFrameHeight;
|
|
99
|
+
} catch (e) {
|
|
100
|
+
newWidth = null;
|
|
101
|
+
newHeight = null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return JSON.stringify({
|
|
105
|
+
ok: true,
|
|
106
|
+
result: {
|
|
107
|
+
sourceSequenceName: seq.name,
|
|
108
|
+
numerator: args.numerator,
|
|
109
|
+
denominator: args.denominator,
|
|
110
|
+
motionPreset: args.motionPreset,
|
|
111
|
+
nest: args.nest,
|
|
112
|
+
numSequencesBefore: numSequencesBefore,
|
|
113
|
+
numSequencesAfter: numSequencesAfter,
|
|
114
|
+
newSequenceName: newSeq.name,
|
|
115
|
+
newSequenceID: newSeq.sequenceID,
|
|
116
|
+
newWidth: newWidth,
|
|
117
|
+
newHeight: newHeight,
|
|
118
|
+
note: "numerator/denominator are treated as an aspect ratio per PREMIERE_API_NOTES.md's 5-arg signature — UNCONFIRMED against this build; newWidth/newHeight are reported so a caller can verify the actual result"
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
} catch (e) {
|
|
122
|
+
return JSON.stringify({ ok: false, error: e.toString() });
|
|
123
|
+
}
|
|
124
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
// Command: batch-add-transitions → ppb_batchAddTransitions
|
|
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
|
+
// Port of the reference project's batch_add_transitions, generalized from
|
|
8
|
+
// its fixed "at every cut point on a track" behavior to the same atEnd
|
|
9
|
+
// semantic as add-transition: applies the SAME transition to every clip
|
|
10
|
+
// on one video track (default atEnd: true, i.e. at each clip's end/the
|
|
11
|
+
// cut point between it and its successor — matching the reference tool's
|
|
12
|
+
// own loop). Capped at 100 clips per call. Reuses the shared
|
|
13
|
+
// ppbApplyTransitionToClip helper per clip, so the disputed addTransition
|
|
14
|
+
// signature and transitions-count verification are identical to
|
|
15
|
+
// add-transition's — see that file's header and PREMIERE_API_NOTES.md's
|
|
16
|
+
// "Transitions (QE only)" section.
|
|
17
|
+
var PPB_BATCH_ADD_TRANSITIONS_CAP = 100;
|
|
18
|
+
|
|
19
|
+
function ppb_batchAddTransitions(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
|
+
var trackType = args.trackType || "video";
|
|
29
|
+
if (trackType !== "video") {
|
|
30
|
+
return JSON.stringify({ ok: false, error: "trackType must be \"video\" — no reference API demonstrates adding a transition to an audio track" });
|
|
31
|
+
}
|
|
32
|
+
var trackIndex = 0;
|
|
33
|
+
if (typeof args.trackIndex === "number") {
|
|
34
|
+
if (args.trackIndex < 0 || Math.floor(args.trackIndex) !== args.trackIndex) {
|
|
35
|
+
return JSON.stringify({ ok: false, error: "trackIndex must be a non-negative integer" });
|
|
36
|
+
}
|
|
37
|
+
trackIndex = args.trackIndex;
|
|
38
|
+
}
|
|
39
|
+
var atEnd = true;
|
|
40
|
+
if (typeof args.atEnd === "boolean") {
|
|
41
|
+
atEnd = args.atEnd;
|
|
42
|
+
} else if (args.atEnd !== undefined && args.atEnd !== null) {
|
|
43
|
+
return JSON.stringify({ ok: false, error: "atEnd must be a boolean if given (default true)" });
|
|
44
|
+
}
|
|
45
|
+
var transitionName = null;
|
|
46
|
+
if (typeof args.transitionName === "string" && args.transitionName.length > 0) {
|
|
47
|
+
transitionName = args.transitionName;
|
|
48
|
+
} else if (args.transitionName !== undefined && args.transitionName !== null) {
|
|
49
|
+
return JSON.stringify({ ok: false, error: "transitionName must be a non-empty string, or omitted/null for the default transition" });
|
|
50
|
+
}
|
|
51
|
+
var durationSeconds = 1.0;
|
|
52
|
+
if (typeof args.durationSeconds === "number") {
|
|
53
|
+
if (args.durationSeconds <= 0) {
|
|
54
|
+
return JSON.stringify({ ok: false, error: "durationSeconds must be a positive number" });
|
|
55
|
+
}
|
|
56
|
+
durationSeconds = args.durationSeconds;
|
|
57
|
+
} else if (args.durationSeconds !== undefined && args.durationSeconds !== null) {
|
|
58
|
+
return JSON.stringify({ ok: false, error: "durationSeconds must be a number" });
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (!app.project) {
|
|
62
|
+
return JSON.stringify({ ok: false, error: "no project open" });
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
var seq = null;
|
|
66
|
+
if (args.sequenceName && typeof args.sequenceName === "string") {
|
|
67
|
+
seq = findSequenceByName(args.sequenceName);
|
|
68
|
+
if (!seq) {
|
|
69
|
+
return JSON.stringify({ ok: false, error: "no sequence named \"" + args.sequenceName + "\" is open" });
|
|
70
|
+
}
|
|
71
|
+
} else {
|
|
72
|
+
seq = app.project.activeSequence;
|
|
73
|
+
if (!seq) {
|
|
74
|
+
return JSON.stringify({ ok: false, error: "no active sequence, and no sequenceName given" });
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
var numTracks;
|
|
79
|
+
try { numTracks = seq.videoTracks.numTracks; } catch (eNt) { numTracks = 0; }
|
|
80
|
+
if (trackIndex >= numTracks) {
|
|
81
|
+
return JSON.stringify({ ok: false, error: "trackIndex " + trackIndex + " is out of range — sequence has " + numTracks + " video track(s)" });
|
|
82
|
+
}
|
|
83
|
+
var numClips;
|
|
84
|
+
try { numClips = seq.videoTracks[trackIndex].clips.numItems; } catch (eNc) { numClips = 0; }
|
|
85
|
+
if (numClips < 2) {
|
|
86
|
+
return JSON.stringify({ ok: true, result: { sequenceName: seq.name, trackType: "video", trackIndex: trackIndex, totalClipsOnTrack: numClips, attempted: 0, succeeded: 0, cappedAtLimit: false, results: [] } });
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
try {
|
|
90
|
+
ensureQEEnabled();
|
|
91
|
+
activateSequenceForQE(seq);
|
|
92
|
+
} catch (e2) {
|
|
93
|
+
return JSON.stringify({ ok: false, error: "app.enableQE()/sequence activation failed: " + e2.toString() });
|
|
94
|
+
}
|
|
95
|
+
if (typeof qe === "undefined" || !qe.project) {
|
|
96
|
+
return JSON.stringify({ ok: false, error: "QE DOM not available after enableQE()" });
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
var transitionQE = null;
|
|
100
|
+
if (transitionName !== null) {
|
|
101
|
+
try {
|
|
102
|
+
if (qe.project.getVideoTransitionByName) {
|
|
103
|
+
transitionQE = qe.project.getVideoTransitionByName(transitionName);
|
|
104
|
+
}
|
|
105
|
+
} catch (e3) {
|
|
106
|
+
transitionQE = null;
|
|
107
|
+
}
|
|
108
|
+
if (!transitionQE) {
|
|
109
|
+
return JSON.stringify({ ok: false, error: "transition not found: \"" + transitionName + "\" (try list-available-transitions)" });
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// One transition per cut point: clips 0..numClips-2 (the last clip has
|
|
114
|
+
// no successor to transition into), same range the reference tool's
|
|
115
|
+
// own loop uses.
|
|
116
|
+
var cutPointCount = numClips - 1;
|
|
117
|
+
var cappedAtLimit = cutPointCount > PPB_BATCH_ADD_TRANSITIONS_CAP;
|
|
118
|
+
var attemptCount = cappedAtLimit ? PPB_BATCH_ADD_TRANSITIONS_CAP : cutPointCount;
|
|
119
|
+
|
|
120
|
+
var results = [];
|
|
121
|
+
var succeeded = 0;
|
|
122
|
+
for (var c = 0; c < attemptCount; c++) {
|
|
123
|
+
var applyResult = ppbApplyTransitionToClip(seq, trackIndex, c, transitionQE, atEnd, durationSeconds);
|
|
124
|
+
results.push({
|
|
125
|
+
clipIndex: c,
|
|
126
|
+
clipName: applyResult.clipName,
|
|
127
|
+
previousTransitionCount: applyResult.previousCount,
|
|
128
|
+
newTransitionCount: applyResult.newCount,
|
|
129
|
+
addedTransitionName: applyResult.addedTransitionName,
|
|
130
|
+
verified: applyResult.verified,
|
|
131
|
+
succeededWithArgs: applyResult.succeededWithArgs,
|
|
132
|
+
error: applyResult.verified ? null : applyResult.error
|
|
133
|
+
});
|
|
134
|
+
if (applyResult.verified) {
|
|
135
|
+
succeeded++;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return JSON.stringify({
|
|
140
|
+
ok: true,
|
|
141
|
+
result: {
|
|
142
|
+
sequenceName: seq.name,
|
|
143
|
+
trackType: "video",
|
|
144
|
+
trackIndex: trackIndex,
|
|
145
|
+
transitionName: transitionName,
|
|
146
|
+
atEnd: atEnd,
|
|
147
|
+
durationSeconds: durationSeconds,
|
|
148
|
+
totalClipsOnTrack: numClips,
|
|
149
|
+
attempted: attemptCount,
|
|
150
|
+
succeeded: succeeded,
|
|
151
|
+
cappedAtLimit: cappedAtLimit,
|
|
152
|
+
results: results
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
} catch (e) {
|
|
156
|
+
return JSON.stringify({ ok: false, error: e.toString() });
|
|
157
|
+
}
|
|
158
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
// Command: batch-apply-effect → ppb_batchApplyEffect
|
|
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, resolveQeClip,
|
|
5
|
+
// serializeClipComponents, findQeVideoEffectByName,
|
|
6
|
+
// findQeAudioEffectByName, ...) are already defined there.
|
|
7
|
+
//
|
|
8
|
+
// Ports leancoderkavy's clipboard.ts batch_apply_effect's "track"/"all"
|
|
9
|
+
// targets (there is no "selected" target here — combine with
|
|
10
|
+
// select-clips-by-name/nameContains instead, same pattern
|
|
11
|
+
// batch-set-clips-enabled uses). Matches clips across the given track
|
|
12
|
+
// type(s) (default "both"), optionally narrowed to one trackIndex and/or a
|
|
13
|
+
// case-insensitive nameContains substring, capped at 100 clips per call.
|
|
14
|
+
// Each matching clip gets the effect applied via the standard QE dance
|
|
15
|
+
// (getVideoEffectByName/getAudioEffectByName + addVideoEffect/
|
|
16
|
+
// addAudioEffect chosen by THAT clip's own track type, not a single global
|
|
17
|
+
// effect object — a video effect and an audio effect with the same name are
|
|
18
|
+
// different catalog entries), verified individually by that clip's own
|
|
19
|
+
// clip.components.numItems increasing by 1.
|
|
20
|
+
function ppb_batchApplyEffect(argsJson) {
|
|
21
|
+
try {
|
|
22
|
+
var args;
|
|
23
|
+
try {
|
|
24
|
+
args = JSON.parse(argsJson);
|
|
25
|
+
} catch (e) {
|
|
26
|
+
return JSON.stringify({ ok: false, error: "invalid args JSON: " + e.toString() });
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (typeof args.effectName !== "string" || !args.effectName) {
|
|
30
|
+
return JSON.stringify({ ok: false, error: "effectName must be a non-empty string" });
|
|
31
|
+
}
|
|
32
|
+
var trackTypeFilter = args.trackType || "both";
|
|
33
|
+
if (trackTypeFilter !== "video" && trackTypeFilter !== "audio" && trackTypeFilter !== "both") {
|
|
34
|
+
return JSON.stringify({ ok: false, error: "trackType must be \"video\", \"audio\", or \"both\"" });
|
|
35
|
+
}
|
|
36
|
+
if (args.trackIndex !== undefined && args.trackIndex !== null) {
|
|
37
|
+
if (typeof args.trackIndex !== "number" || args.trackIndex < 0 || Math.floor(args.trackIndex) !== args.trackIndex) {
|
|
38
|
+
return JSON.stringify({ ok: false, error: "trackIndex must be a non-negative integer if given" });
|
|
39
|
+
}
|
|
40
|
+
if (trackTypeFilter === "both") {
|
|
41
|
+
return JSON.stringify({ ok: false, error: "trackIndex requires trackType to be \"video\" or \"audio\" (not \"both\")" });
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
var nameContains = (typeof args.nameContains === "string" && args.nameContains) ? args.nameContains.toLowerCase() : null;
|
|
45
|
+
|
|
46
|
+
if (!app.project) {
|
|
47
|
+
return JSON.stringify({ ok: false, error: "no project open" });
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
var seq = null;
|
|
51
|
+
if (args.sequenceName && typeof args.sequenceName === "string") {
|
|
52
|
+
seq = findSequenceByName(args.sequenceName);
|
|
53
|
+
if (!seq) {
|
|
54
|
+
return JSON.stringify({ ok: false, error: "no sequence named \"" + args.sequenceName + "\" is open" });
|
|
55
|
+
}
|
|
56
|
+
} else {
|
|
57
|
+
seq = app.project.activeSequence;
|
|
58
|
+
if (!seq) {
|
|
59
|
+
return JSON.stringify({ ok: false, error: "no active sequence, and no sequenceName given" });
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Collect every matching clip's standard-DOM addressing BEFORE any QE
|
|
64
|
+
// mutation — mutating one clip never shifts another's trackIndex/
|
|
65
|
+
// clipIndex, but we still want a stable, fully-enumerated list up front.
|
|
66
|
+
var matches = [];
|
|
67
|
+
function collectFrom(trackCollection, trackTypeName) {
|
|
68
|
+
var numTracks = trackCollection.numTracks;
|
|
69
|
+
for (var t = 0; t < numTracks; t++) {
|
|
70
|
+
if (args.trackIndex !== undefined && args.trackIndex !== null && t !== args.trackIndex) {
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
var track = trackCollection[t];
|
|
74
|
+
var numClips = track.clips.numItems;
|
|
75
|
+
for (var c = 0; c < numClips; c++) {
|
|
76
|
+
var clip = track.clips[c];
|
|
77
|
+
var name = null;
|
|
78
|
+
try { name = clip.name; } catch (e) { name = null; }
|
|
79
|
+
if (nameContains && (!name || name.toLowerCase().indexOf(nameContains) === -1)) {
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
matches.push({ trackType: trackTypeName, trackIndex: t, clipIndex: c, clipName: name, clip: clip });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (trackTypeFilter === "video" || trackTypeFilter === "both") {
|
|
87
|
+
collectFrom(seq.videoTracks, "video");
|
|
88
|
+
}
|
|
89
|
+
if (trackTypeFilter === "audio" || trackTypeFilter === "both") {
|
|
90
|
+
collectFrom(seq.audioTracks, "audio");
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
var totalMatched = matches.length;
|
|
94
|
+
var cappedAtLimit = totalMatched > 100;
|
|
95
|
+
if (cappedAtLimit) {
|
|
96
|
+
matches = matches.slice(0, 100);
|
|
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
|
+
|
|
116
|
+
var results = [];
|
|
117
|
+
var appliedCount = 0;
|
|
118
|
+
|
|
119
|
+
for (var m = 0; m < matches.length; m++) {
|
|
120
|
+
var match = matches[m];
|
|
121
|
+
var previousCount = 0;
|
|
122
|
+
try { previousCount = match.clip.components.numItems; } catch (e) { previousCount = 0; }
|
|
123
|
+
|
|
124
|
+
var qeResolved = resolveQeClip(qeSeq, match.trackType, match.trackIndex, match.clipIndex);
|
|
125
|
+
if (qeResolved.error) {
|
|
126
|
+
results.push({ trackType: match.trackType, trackIndex: match.trackIndex, clipIndex: match.clipIndex, clipName: match.clipName, applied: false, error: qeResolved.error });
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
var fx = match.trackType === "video" ? findQeVideoEffectByName(args.effectName) : findQeAudioEffectByName(args.effectName);
|
|
131
|
+
if (!fx) {
|
|
132
|
+
results.push({ trackType: match.trackType, trackIndex: match.trackIndex, clipIndex: match.clipIndex, clipName: match.clipName, applied: false, error: "effect not found in this track type's QE catalog: \"" + args.effectName + "\"" });
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
try {
|
|
137
|
+
if (match.trackType === "video") {
|
|
138
|
+
qeResolved.qeClip.addVideoEffect(fx);
|
|
139
|
+
} else {
|
|
140
|
+
qeResolved.qeClip.addAudioEffect(fx);
|
|
141
|
+
}
|
|
142
|
+
} catch (e2) {
|
|
143
|
+
results.push({ trackType: match.trackType, trackIndex: match.trackIndex, clipIndex: match.clipIndex, clipName: match.clipName, applied: false, error: "addVideoEffect/addAudioEffect failed: " + e2.toString() });
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
var newCount = 0;
|
|
148
|
+
try { newCount = match.clip.components.numItems; } catch (e3) { newCount = 0; }
|
|
149
|
+
var verified = newCount === previousCount + 1;
|
|
150
|
+
if (verified) {
|
|
151
|
+
appliedCount++;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
results.push({
|
|
155
|
+
trackType: match.trackType,
|
|
156
|
+
trackIndex: match.trackIndex,
|
|
157
|
+
clipIndex: match.clipIndex,
|
|
158
|
+
clipName: match.clipName,
|
|
159
|
+
applied: true,
|
|
160
|
+
verified: verified,
|
|
161
|
+
previousComponentCount: previousCount,
|
|
162
|
+
newComponentCount: newCount
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return JSON.stringify({
|
|
167
|
+
ok: true,
|
|
168
|
+
result: {
|
|
169
|
+
sequenceName: seq.name,
|
|
170
|
+
effectName: args.effectName,
|
|
171
|
+
trackType: trackTypeFilter,
|
|
172
|
+
nameContains: args.nameContains || null,
|
|
173
|
+
totalMatched: totalMatched,
|
|
174
|
+
cappedAtLimit: cappedAtLimit,
|
|
175
|
+
appliedCount: appliedCount,
|
|
176
|
+
results: results
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
} catch (e) {
|
|
180
|
+
return JSON.stringify({ ok: false, error: e.toString() });
|
|
181
|
+
}
|
|
182
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
// Command: batch-rename-clips → ppb_batchRenameClips
|
|
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, qeFindNthNonEmptyClip, ...) are
|
|
5
|
+
// already defined there.
|
|
6
|
+
//
|
|
7
|
+
// Ported from leancoderkavy's premiere-pro-mcp `batch_rename_clips` tool
|
|
8
|
+
// (track-targeting.ts), which renames via QE `qeClip.setName()` only (no
|
|
9
|
+
// standard-DOM fallback, unlike the single-clip rename-clip command) —
|
|
10
|
+
// matched here for fidelity to the reference's actual renaming path.
|
|
11
|
+
// `newNameTemplate` supports "{n}" (sequential counter, starting at
|
|
12
|
+
// `startNumber`, default 1) and "{name}" (the clip's existing name),
|
|
13
|
+
// applied via simple split/join (no regex). `nameContains` optionally
|
|
14
|
+
// filters which clips on the track are renamed (case-insensitive substring
|
|
15
|
+
// match against the clip's CURRENT name, checked before the counter
|
|
16
|
+
// advances so skipped clips don't consume a number). Capped at 200 renames
|
|
17
|
+
// per call to bound QE call volume. Every clip's rename is verified
|
|
18
|
+
// individually via a clip.name read-back — never trusted from setName()'s
|
|
19
|
+
// own return value.
|
|
20
|
+
var BATCH_RENAME_CLIPS_MAX = 200;
|
|
21
|
+
|
|
22
|
+
function ppb_batchRenameClips(argsJson) {
|
|
23
|
+
try {
|
|
24
|
+
var args;
|
|
25
|
+
try {
|
|
26
|
+
args = JSON.parse(argsJson);
|
|
27
|
+
} catch (e) {
|
|
28
|
+
return JSON.stringify({ ok: false, error: "invalid args JSON: " + e.toString() });
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (args.trackType !== "video" && args.trackType !== "audio") {
|
|
32
|
+
return JSON.stringify({ ok: false, error: "trackType must be \"video\" or \"audio\"" });
|
|
33
|
+
}
|
|
34
|
+
if (typeof args.trackIndex !== "number" || args.trackIndex < 0 || Math.floor(args.trackIndex) !== args.trackIndex) {
|
|
35
|
+
return JSON.stringify({ ok: false, error: "trackIndex must be a non-negative integer" });
|
|
36
|
+
}
|
|
37
|
+
if (typeof args.newNameTemplate !== "string" || args.newNameTemplate.length === 0) {
|
|
38
|
+
return JSON.stringify({ ok: false, error: "newNameTemplate must be a non-empty string" });
|
|
39
|
+
}
|
|
40
|
+
var startNumber = 1;
|
|
41
|
+
if (typeof args.startNumber === "number" && !isNaN(args.startNumber)) {
|
|
42
|
+
startNumber = args.startNumber;
|
|
43
|
+
}
|
|
44
|
+
var nameContains = null;
|
|
45
|
+
if (typeof args.nameContains === "string" && args.nameContains.length > 0) {
|
|
46
|
+
nameContains = args.nameContains.toLowerCase();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (!app.project) {
|
|
50
|
+
return JSON.stringify({ ok: false, error: "no project open" });
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
var seq = null;
|
|
54
|
+
if (args.sequenceName && typeof args.sequenceName === "string") {
|
|
55
|
+
seq = findSequenceByName(args.sequenceName);
|
|
56
|
+
if (!seq) {
|
|
57
|
+
return JSON.stringify({ ok: false, error: "no sequence named \"" + args.sequenceName + "\" is open" });
|
|
58
|
+
}
|
|
59
|
+
} else {
|
|
60
|
+
seq = app.project.activeSequence;
|
|
61
|
+
if (!seq) {
|
|
62
|
+
return JSON.stringify({ ok: false, error: "no active sequence, and no sequenceName given" });
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
var trackCollection = args.trackType === "video" ? seq.videoTracks : seq.audioTracks;
|
|
67
|
+
var numTracks = trackCollection.numTracks;
|
|
68
|
+
if (args.trackIndex >= numTracks) {
|
|
69
|
+
return JSON.stringify({
|
|
70
|
+
ok: false,
|
|
71
|
+
error: "trackIndex " + args.trackIndex + " is out of range — sequence has " + numTracks + " " + args.trackType + " track(s)"
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
var track = trackCollection[args.trackIndex];
|
|
76
|
+
var numClips = track.clips.numItems;
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
ensureQEEnabled();
|
|
80
|
+
activateSequenceForQE(seq);
|
|
81
|
+
} catch (e) {
|
|
82
|
+
return JSON.stringify({ ok: false, error: "app.enableQE()/sequence activation failed: " + e.toString() });
|
|
83
|
+
}
|
|
84
|
+
if (typeof qe === "undefined" || !qe.project) {
|
|
85
|
+
return JSON.stringify({ ok: false, error: "QE DOM not available after enableQE()" });
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
var qeSeq;
|
|
89
|
+
try {
|
|
90
|
+
qeSeq = qe.project.getActiveSequence();
|
|
91
|
+
} catch (e) {
|
|
92
|
+
return JSON.stringify({ ok: false, error: "qe.project.getActiveSequence() failed: " + e.toString() });
|
|
93
|
+
}
|
|
94
|
+
var qeTrack;
|
|
95
|
+
try {
|
|
96
|
+
qeTrack = args.trackType === "video" ? qeSeq.getVideoTrackAt(args.trackIndex) : qeSeq.getAudioTrackAt(args.trackIndex);
|
|
97
|
+
} catch (e) {
|
|
98
|
+
return JSON.stringify({ ok: false, error: "QE track lookup failed: " + e.toString() });
|
|
99
|
+
}
|
|
100
|
+
if (!qeTrack) {
|
|
101
|
+
return JSON.stringify({ ok: false, error: "QE track not found at index " + args.trackIndex });
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
var results = [];
|
|
105
|
+
var renamed = 0;
|
|
106
|
+
var skippedByFilter = 0;
|
|
107
|
+
var cappedAtLimit = false;
|
|
108
|
+
var num = startNumber;
|
|
109
|
+
|
|
110
|
+
for (var c = 0; c < numClips; c++) {
|
|
111
|
+
if (renamed >= BATCH_RENAME_CLIPS_MAX) {
|
|
112
|
+
cappedAtLimit = true;
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
var clip = track.clips[c];
|
|
117
|
+
var oldName = null;
|
|
118
|
+
try { oldName = clip.name; } catch (e) { oldName = null; }
|
|
119
|
+
|
|
120
|
+
if (nameContains !== null) {
|
|
121
|
+
var haystack = oldName === null ? "" : String(oldName).toLowerCase();
|
|
122
|
+
if (haystack.indexOf(nameContains) === -1) {
|
|
123
|
+
skippedByFilter++;
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
var newName = args.newNameTemplate.split("{n}").join(String(num)).split("{name}").join(oldName === null ? "" : oldName);
|
|
129
|
+
|
|
130
|
+
var entry = { clipIndex: c, previousValue: oldName, requestedValue: newName, newValue: null, verified: false };
|
|
131
|
+
|
|
132
|
+
try {
|
|
133
|
+
var qeClip = qeFindNthNonEmptyClip(qeTrack, c);
|
|
134
|
+
if (!qeClip) {
|
|
135
|
+
entry.error = "could not locate corresponding QE clip";
|
|
136
|
+
} else {
|
|
137
|
+
qeClip.setName(newName);
|
|
138
|
+
}
|
|
139
|
+
} catch (e) {
|
|
140
|
+
entry.error = e.toString();
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
try { entry.newValue = clip.name; } catch (e) { entry.newValue = null; }
|
|
144
|
+
entry.verified = entry.newValue === newName;
|
|
145
|
+
if (entry.verified) {
|
|
146
|
+
renamed++;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
num++;
|
|
150
|
+
results.push(entry);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
var result = {
|
|
154
|
+
sequenceName: seq.name,
|
|
155
|
+
trackType: args.trackType,
|
|
156
|
+
trackIndex: args.trackIndex,
|
|
157
|
+
newNameTemplate: args.newNameTemplate,
|
|
158
|
+
nameContains: args.nameContains || null,
|
|
159
|
+
totalClipsOnTrack: numClips,
|
|
160
|
+
skippedByFilter: skippedByFilter,
|
|
161
|
+
renamed: renamed,
|
|
162
|
+
cappedAtLimit: cappedAtLimit,
|
|
163
|
+
results: results
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
return JSON.stringify({ ok: true, result: result });
|
|
167
|
+
} catch (e) {
|
|
168
|
+
return JSON.stringify({ ok: false, error: e.toString() });
|
|
169
|
+
}
|
|
170
|
+
}
|