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
This commit is contained in:
+173
@@ -0,0 +1,173 @@
|
||||
(function (root, factory) {
|
||||
const api = factory();
|
||||
if (typeof module === "object" && module.exports) module.exports = api;
|
||||
root.PremiereMcpObjectMaskAuditWorkflows = api;
|
||||
})(typeof globalThis !== "undefined" ? globalThis : this, function () {
|
||||
"use strict";
|
||||
|
||||
// ObjectMaskUtils intentionally exposes only a yes/no answer. This audit
|
||||
// makes that answer useful for bounded review without inventing a mask
|
||||
// count, location, quality, editability, tracking state, or render result.
|
||||
function createObjectMaskAuditWorkflowDefinitions(deps) {
|
||||
const ppro = deps.ppro;
|
||||
const MAX_SEQUENCES = 64;
|
||||
const definitions = {
|
||||
"objectMask.audit": {
|
||||
readOnly: true,
|
||||
targetCapabilityProbe: true,
|
||||
minHostVersion: "26.3.0",
|
||||
probe: canAuditObjectMasks,
|
||||
handler: auditObjectMasks
|
||||
}
|
||||
};
|
||||
|
||||
function canAuditObjectMasks() {
|
||||
return !!(ppro && ppro.Project && typeof ppro.Project.getActiveProject === "function" &&
|
||||
ppro.ObjectMaskUtils && typeof ppro.ObjectMaskUtils.hasObjectMask === "function");
|
||||
}
|
||||
|
||||
async function auditObjectMasks(args) {
|
||||
assertOnlyKeys(args, ["expectedProjectGuid", "sequenceIds"]);
|
||||
const requestedIds = requestedSequenceIds(args.sequenceIds);
|
||||
const project = await activeProject();
|
||||
const projectGuid = requiredGuid(project.guid, "active project GUID");
|
||||
if (args.expectedProjectGuid != null && requiredGuid(args.expectedProjectGuid, "expectedProjectGuid") !== projectGuid) {
|
||||
throw commandError("UXP_STALE_OBJECT_MASK_AUDIT", "The active project differs from expectedProjectGuid; inspect the current object-mask state again");
|
||||
}
|
||||
|
||||
const projectHasObjectMask = objectMaskValue(project);
|
||||
const first = await targetSequences(project, requestedIds);
|
||||
const firstResults = first.map(function (entry) {
|
||||
return { id: entry.id, name: entry.name, hasObjectMask: objectMaskValue(entry.sequence) };
|
||||
});
|
||||
|
||||
const activeAfter = await activeProject();
|
||||
if (requiredGuid(activeAfter.guid, "active project GUID after audit") !== projectGuid) {
|
||||
throw commandError("UXP_STALE_OBJECT_MASK_AUDIT", "The active project changed while object masks were being audited");
|
||||
}
|
||||
const second = await targetSequences(activeAfter, requestedIds);
|
||||
const secondResults = second.map(function (entry) {
|
||||
return { id: entry.id, name: entry.name, hasObjectMask: objectMaskValue(entry.sequence) };
|
||||
});
|
||||
const projectHasObjectMaskAfter = objectMaskValue(activeAfter);
|
||||
if (projectHasObjectMaskAfter !== projectHasObjectMask || !sameAudit(firstResults, secondResults)) {
|
||||
throw commandError("UXP_STALE_OBJECT_MASK_AUDIT", "The project, sequence identities, or object-mask results changed during the audit; retry");
|
||||
}
|
||||
|
||||
const maskedSequenceCount = firstResults.filter(function (entry) { return entry.hasObjectMask; }).length;
|
||||
return {
|
||||
projectGuid,
|
||||
scope: requestedIds ? "explicit_sequences" : "all_project_sequences",
|
||||
projectHasObjectMask,
|
||||
sequenceCount: firstResults.length,
|
||||
maskedSequenceCount,
|
||||
sequences: firstResults,
|
||||
verificationBoundary: "bounded_project_and_sequence_object_mask_double_readback"
|
||||
};
|
||||
}
|
||||
|
||||
async function activeProject() {
|
||||
const project = await ppro.Project.getActiveProject();
|
||||
if (!project) throw commandError("UXP_NO_ACTIVE_PROJECT", "No active project");
|
||||
return project;
|
||||
}
|
||||
|
||||
function requestedSequenceIds(value) {
|
||||
if (value == null) return null;
|
||||
if (!Array.isArray(value) || !value.length || value.length > MAX_SEQUENCES) {
|
||||
throw commandError("UXP_INVALID_ARGUMENT", "sequenceIds must contain 1-" + MAX_SEQUENCES + " exact sequence GUIDs");
|
||||
}
|
||||
const ids = value.map(function (entry, index) {
|
||||
return requiredGuid(entry, "sequenceIds[" + index + "]");
|
||||
});
|
||||
if (new Set(ids).size !== ids.length) throw commandError("UXP_INVALID_ARGUMENT", "sequenceIds must not contain duplicates");
|
||||
return ids.sort();
|
||||
}
|
||||
|
||||
async function targetSequences(project, requestedIds) {
|
||||
const values = requestedIds ? await sequencesById(project, requestedIds) : await allSequences(project);
|
||||
const seen = new Set(), snapshots = [];
|
||||
for (let index = 0; index < values.length; index += 1) {
|
||||
const sequence = values[index];
|
||||
const id = requiredGuid(sequence && sequence.guid, "sequence GUID");
|
||||
if (seen.has(id)) throw commandError("UXP_VERIFICATION_FAILED", "Premiere returned duplicate sequence GUIDs during object-mask audit");
|
||||
seen.add(id);
|
||||
snapshots.push({ sequence, id, name: boundedName(sequence && sequence.name) });
|
||||
}
|
||||
snapshots.sort(function (left, right) { return left.id.localeCompare(right.id); });
|
||||
return snapshots;
|
||||
}
|
||||
|
||||
async function allSequences(project) {
|
||||
if (typeof project.getSequences !== "function") {
|
||||
throw commandError("UXP_COMMAND_UNAVAILABLE", "Premiere does not expose documented project sequence enumeration");
|
||||
}
|
||||
const values = Array.from(await project.getSequences() || []);
|
||||
if (values.length > MAX_SEQUENCES) {
|
||||
throw commandError("UXP_PROJECT_TOO_LARGE", "Object-mask audit is capped at " + MAX_SEQUENCES + " sequences; pass at most " + MAX_SEQUENCES + " exact sequenceIds instead");
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
async function sequencesById(project, ids) {
|
||||
if (!ppro.Guid || typeof ppro.Guid.fromString !== "function" || typeof project.getSequence !== "function") {
|
||||
throw commandError("UXP_COMMAND_UNAVAILABLE", "Premiere does not expose documented exact sequence GUID lookup");
|
||||
}
|
||||
const values = [];
|
||||
for (let index = 0; index < ids.length; index += 1) {
|
||||
let sequence;
|
||||
try { sequence = await project.getSequence(ppro.Guid.fromString(ids[index])); } catch (_) { sequence = null; }
|
||||
if (!sequence || requiredGuid(sequence.guid, "resolved sequence GUID") !== ids[index]) {
|
||||
throw commandError("UXP_STALE_OBJECT_MASK_AUDIT", "A requested sequence no longer resolves to its exact GUID; retry the audit");
|
||||
}
|
||||
values.push(sequence);
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
function objectMaskValue(target) {
|
||||
let value;
|
||||
try { value = ppro.ObjectMaskUtils.hasObjectMask(target); } catch (error) {
|
||||
throw commandError("UXP_VERIFICATION_FAILED", "Premiere could not report object-mask presence: " + (error && error.message || String(error)));
|
||||
}
|
||||
if (typeof value !== "boolean") throw commandError("UXP_VERIFICATION_FAILED", "Premiere returned a non-boolean object-mask result");
|
||||
return value;
|
||||
}
|
||||
|
||||
function sameAudit(left, right) {
|
||||
return left.length === right.length && left.every(function (entry, index) {
|
||||
const other = right[index];
|
||||
return other && entry.id === other.id && entry.name === other.name && entry.hasObjectMask === other.hasObjectMask;
|
||||
});
|
||||
}
|
||||
|
||||
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 requiredGuid(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 boundedName(value) {
|
||||
const name = String(value == null ? "" : value);
|
||||
if (name.length > 255) throw commandError("UXP_VERIFICATION_FAILED", "Premiere returned an overlong sequence name");
|
||||
return name;
|
||||
}
|
||||
|
||||
function commandError(code, message) {
|
||||
const error = new Error(message);
|
||||
error.code = code;
|
||||
return error;
|
||||
}
|
||||
|
||||
return { createObjectMaskAuditWorkflowDefinitions };
|
||||
});
|
||||
Reference in New Issue
Block a user