Files
jhonny-editor/code/uxp-plugin/effect-parameter-catalog-workflows.cjs
T
João Henrique b541f502ba feat: initial commit - Jhonny Editor
- Adicionado estrutura completa do projeto
- Configurado MCP server para Premiere Pro
- Adicionado documentação e skills
- Configurado Gitignore para o projeto
2026-09-08 09:59:31 -04:00

230 lines
12 KiB
JavaScript
Executable File

(function (root, factory) {
const api = factory();
if (typeof module === "object" && module.exports) module.exports = api;
root.PremiereMcpEffectParameterCatalogWorkflows = api;
})(typeof globalThis !== "undefined" ? globalThis : this, function () {
"use strict";
// A caller can already inspect a known parameter through the automation
// workflow. This catalog fills the discovery gap without returning parameter
// values, which may be arbitrary PointF/Color objects or user content.
function createEffectParameterCatalogWorkflowDefinitions(deps) {
const ppro = deps.ppro;
const MAX_TRACK_ITEMS = 512;
const MAX_COMPONENTS = 512;
const MAX_PARAMETERS = 64;
const definitions = {
"parameters.catalog.inspect": {
readOnly: true,
targetCapabilityProbe: true,
minHostVersion: "25.6.0",
probe: canInspectParameterCatalog,
handler: inspectParameterCatalog
}
};
function canInspectParameterCatalog() {
return !!(ppro && ppro.Project && typeof ppro.Project.getActiveProject === "function");
}
async function inspectParameterCatalog(args) {
assertOnlyKeys(args, ["mediaType", "trackIndex", "clipIndex", "componentIndex", "expectedSequenceGuid", "expectedComponentId"]);
const input = coordinateInput(args);
const first = await catalogSnapshot(input);
assertExpected(input.expectedSequenceGuid, first.sequenceGuid, "UXP_STALE_PARAMETER_CATALOG", "Active sequence GUID");
assertExpected(input.expectedComponentId, first.component.id, "UXP_STALE_PARAMETER_CATALOG", "Component identity");
const second = await catalogSnapshot(input);
if (!sameSnapshot(first, second)) {
throw commandError("UXP_STALE_PARAMETER_CATALOG", "The project, active sequence, component, or parameter descriptors changed while being inspected; retry the catalog inspection");
}
return {
projectGuid: first.projectGuid,
sequenceGuid: first.sequenceGuid,
mediaType: input.mediaType,
trackIndex: input.trackIndex,
clipIndex: input.clipIndex,
componentIndex: input.componentIndex,
component: first.component,
parameterCount: first.parameters.length,
parameters: first.parameters,
verificationBoundary: "bounded_effect_parameter_catalog_double_readback"
};
}
function coordinateInput(args) {
return {
mediaType: enumValue(args.mediaType, "mediaType", ["video", "audio"]),
trackIndex: boundedIndex(args.trackIndex, "trackIndex"),
clipIndex: boundedIndex(args.clipIndex, "clipIndex"),
componentIndex: boundedIndex(args.componentIndex, "componentIndex"),
expectedSequenceGuid: args.expectedSequenceGuid == null ? null : requestedGuid(args.expectedSequenceGuid, "expectedSequenceGuid"),
expectedComponentId: args.expectedComponentId == null ? null : boundedText(args.expectedComponentId, "expectedComponentId", 512, false)
};
}
async function catalogSnapshot(input) {
const project = await activeProject();
const projectGuid = requiredGuid(project.guid, "active project GUID");
if (typeof project.getActiveSequence !== "function") {
throw commandError("UXP_COMMAND_UNAVAILABLE", "Premiere does not expose the active sequence for parameter catalog inspection");
}
const sequence = await project.getActiveSequence();
if (!sequence) throw commandError("UXP_NO_ACTIVE_SEQUENCE", "No active sequence");
const sequenceGuid = requiredGuid(sequence.guid, "active sequence GUID");
const item = await trackItemAt(sequence, input.mediaType, input.trackIndex, input.clipIndex);
const component = await componentAt(item, input.componentIndex);
const componentSnapshot = await componentDescriptor(component);
const parameterCount = boundedCount(component.getParamCount && component.getParamCount(), "component parameter count", MAX_PARAMETERS);
if (typeof component.getParam !== "function") {
throw commandError("UXP_COMMAND_UNAVAILABLE", "Premiere does not expose component parameter lookup");
}
const parameters = [];
for (let index = 0; index < parameterCount; index += 1) {
parameters.push(await parameterDescriptor(component.getParam(index), index));
}
return { projectGuid, sequenceGuid, component: componentSnapshot, parameters };
}
async function activeProject() {
const project = await ppro.Project.getActiveProject();
if (!project) throw commandError("UXP_NO_ACTIVE_PROJECT", "No active project");
return project;
}
async function trackItemAt(sequence, mediaType, trackIndex, clipIndex) {
const countMethod = mediaType === "video" ? "getVideoTrackCount" : "getAudioTrackCount";
const trackMethod = mediaType === "video" ? "getVideoTrack" : "getAudioTrack";
if (typeof sequence[countMethod] !== "function" || typeof sequence[trackMethod] !== "function") {
throw commandError("UXP_COMMAND_UNAVAILABLE", "Premiere does not expose documented " + mediaType + " track access");
}
const count = boundedCount(await sequence[countMethod](), mediaType + " track count", MAX_TRACK_ITEMS);
if (trackIndex >= count) throw commandError("UXP_TARGET_NOT_FOUND", "trackIndex is out of range");
const track = await sequence[trackMethod](trackIndex);
const itemType = ppro.Constants && ppro.Constants.TrackItemType;
if (!track || !itemType || itemType.CLIP == null || typeof track.getTrackItems !== "function") {
throw commandError("UXP_COMMAND_UNAVAILABLE", "Premiere does not expose documented clip-item access on the requested track");
}
const items = Array.from(await track.getTrackItems(itemType.CLIP, false) || []);
if (items.length > MAX_TRACK_ITEMS) {
throw commandError("UXP_TARGET_TOO_LARGE", "The requested track has more than " + MAX_TRACK_ITEMS + " clip items");
}
if (!items[clipIndex]) throw commandError("UXP_TARGET_NOT_FOUND", "clipIndex is out of range");
return items[clipIndex];
}
async function componentAt(item, componentIndex) {
if (!item || typeof item.getComponentChain !== "function") {
throw commandError("UXP_COMMAND_UNAVAILABLE", "The requested clip does not expose a documented component chain");
}
const chain = await item.getComponentChain();
if (!chain || typeof chain.getComponentCount !== "function" || typeof chain.getComponentAtIndex !== "function") {
throw commandError("UXP_COMMAND_UNAVAILABLE", "Premiere did not return a documented component chain");
}
const count = boundedCount(chain.getComponentCount(), "component count", MAX_COMPONENTS);
if (componentIndex >= count) throw commandError("UXP_TARGET_NOT_FOUND", "componentIndex is out of range");
const component = chain.getComponentAtIndex(componentIndex);
if (!component) throw commandError("UXP_TARGET_NOT_FOUND", "Premiere did not return the requested component");
return component;
}
async function componentDescriptor(component) {
if (typeof component.getMatchName !== "function" || typeof component.getDisplayName !== "function") {
throw commandError("UXP_COMMAND_UNAVAILABLE", "Premiere does not expose documented component identity getters");
}
const matchName = boundedText(await component.getMatchName(), "component match name", 512, true);
const displayName = boundedText(await component.getDisplayName(), "component display name", 512, true);
const id = matchName || displayName;
if (!id) throw commandError("UXP_VERIFICATION_FAILED", "Premiere returned a component without a match or display name");
return { id, matchName, displayName };
}
async function parameterDescriptor(parameter, index) {
if (!parameter || typeof parameter.displayName !== "string" || typeof parameter.areKeyframesSupported !== "function" || typeof parameter.isTimeVarying !== "function") {
throw commandError("UXP_COMMAND_UNAVAILABLE", "Premiere did not expose documented parameter descriptor access");
}
const displayName = boundedText(parameter.displayName, "parameter display name", 512, true);
const keyframesSupported = await parameter.areKeyframesSupported();
const timeVarying = parameter.isTimeVarying();
if (typeof keyframesSupported !== "boolean" || typeof timeVarying !== "boolean") {
throw commandError("UXP_VERIFICATION_FAILED", "Premiere returned an invalid parameter animation descriptor");
}
return { index, displayName, keyframesSupported, timeVarying };
}
function sameSnapshot(left, right) {
if (!left || !right || left.projectGuid !== right.projectGuid || left.sequenceGuid !== right.sequenceGuid ||
left.component.id !== right.component.id || left.component.matchName !== right.component.matchName ||
left.component.displayName !== right.component.displayName || left.parameters.length !== right.parameters.length) return false;
return left.parameters.every(function (parameter, index) {
const other = right.parameters[index];
return other && parameter.index === other.index && parameter.displayName === other.displayName &&
parameter.keyframesSupported === other.keyframesSupported && parameter.timeVarying === other.timeVarying;
});
}
return definitions;
}
function assertOnlyKeys(value, allowed) {
if (!value || typeof value !== "object" || Array.isArray(value)) throw commandError("UXP_INVALID_ARGUMENT", "arguments must be an object");
const unknown = Object.keys(value).find(function (key) { return !allowed.includes(key); });
if (unknown) throw commandError("UXP_INVALID_ARGUMENT", "Unknown argument: " + unknown);
}
function enumValue(value, name, values) {
if (typeof value !== "string" || !values.includes(value)) throw commandError("UXP_INVALID_ARGUMENT", name + " must be " + values.join(" or "));
return value;
}
function boundedIndex(value, name) {
if (!Number.isInteger(value) || value < 0 || value > 511) throw commandError("UXP_INVALID_ARGUMENT", name + " must be an integer from 0 through 511");
return value;
}
function boundedCount(value, name, maximum) {
const count = Number(value);
if (!Number.isInteger(count) || count < 0) throw commandError("UXP_VERIFICATION_FAILED", "Premiere returned an invalid " + name);
if (count > maximum) throw commandError("UXP_TARGET_TOO_LARGE", "The " + name + " exceeds the " + maximum + " entry limit");
return count;
}
function requiredGuid(value, name) {
const guid = boundedGuid(value, name);
if (!guid) throw commandError("UXP_VERIFICATION_FAILED", name + " must be a bounded non-empty GUID");
return guid;
}
function boundedGuid(value, name) {
let text = "";
try { text = String(value && typeof value.toString === "function" ? value.toString() : value || ""); } catch (_) {}
if (!text || text.length > 512) throw commandError("UXP_VERIFICATION_FAILED", name + " must be a bounded non-empty GUID");
return text;
}
function requestedGuid(value, name) {
let text = "";
try { text = String(value && typeof value.toString === "function" ? value.toString() : value || ""); } catch (_) {}
if (!text || text.length > 512) throw commandError("UXP_INVALID_ARGUMENT", name + " must be a bounded non-empty GUID");
return text;
}
function boundedText(value, name, maximum, allowEmpty) {
if (typeof value !== "string" || value.length > maximum || (!allowEmpty && !value)) {
throw commandError("UXP_VERIFICATION_FAILED", name + " must be a bounded " + (allowEmpty ? "string" : "non-empty string"));
}
return value;
}
function assertExpected(expected, actual, code, name) {
if (expected != null && expected !== actual) throw commandError(code, name + " differs from the inspected value; retry the catalog inspection");
}
function commandError(code, message) {
const error = new Error(message);
error.code = code;
return error;
}
return { createEffectParameterCatalogWorkflowDefinitions };
});