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:
Executable
+129
@@ -0,0 +1,129 @@
|
||||
/* Dedicated AE CEP file bridge. It deliberately uses a different directory
|
||||
* from the Premiere connector so simultaneous Adobe hosts cannot claim each
|
||||
* other's ExtendScript commands. */
|
||||
(function () {
|
||||
var cs = new CSInterface();
|
||||
var fs = nodeRequire("fs");
|
||||
var path = nodeRequire("path");
|
||||
var os = nodeRequire("os");
|
||||
var pollTimer = null;
|
||||
var heartbeatTimer = null;
|
||||
var running = false;
|
||||
var tempDir = defaultBridgeDirectory();
|
||||
var engineId = Math.random().toString(36).slice(2, 8);
|
||||
|
||||
function nodeRequire(moduleName) {
|
||||
if (typeof require !== "undefined") return require(moduleName);
|
||||
var node = typeof cep_node !== "undefined" ? cep_node : window.cep_node;
|
||||
if (node && typeof node.require === "function") return node.require(moduleName);
|
||||
throw new Error("Node.js is unavailable. Confirm --enable-nodejs in the CEP manifest, then fully restart After Effects.");
|
||||
}
|
||||
|
||||
function defaultBridgeDirectory() {
|
||||
try {
|
||||
var process = nodeRequire("process");
|
||||
var configured = process && process.env && process.env.AFTER_EFFECTS_MCP_TEMP_DIR;
|
||||
if (typeof configured === "string" && configured.trim()) return configured.trim();
|
||||
} catch (ignored) {}
|
||||
return path.join(os.tmpdir(), "after-effects-mcp-bridge");
|
||||
}
|
||||
|
||||
function setStatus(value, active) {
|
||||
document.getElementById("status").textContent = value;
|
||||
document.getElementById("status").style.color = active ? "#86efac" : "#fca5a5";
|
||||
document.getElementById("toggle").textContent = active ? "Stop connector" : "Start connector";
|
||||
}
|
||||
|
||||
function writeFileAtomic(filePath, text) {
|
||||
var staged = filePath + "." + engineId + ".staged";
|
||||
try {
|
||||
fs.writeFileSync(staged, text, "utf8");
|
||||
fs.renameSync(staged, filePath);
|
||||
return true;
|
||||
} catch (error) {
|
||||
try { if (fs.existsSync(staged)) fs.unlinkSync(staged); } catch (ignored) {}
|
||||
setStatus("Connector needs attention", false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function heartbeat() {
|
||||
if (!tempDir) return;
|
||||
writeFileAtomic(path.join(tempDir, "bridge-heartbeat.json"), JSON.stringify({ protocolVersion: 1, state: running ? "running" : "waiting" }));
|
||||
}
|
||||
|
||||
function readCommandFiles() {
|
||||
try {
|
||||
return fs.readdirSync(tempDir).filter(function (entry) {
|
||||
return entry.indexOf("cmd_") === 0 && entry.slice(-4) === ".jsx";
|
||||
}).sort();
|
||||
} catch (ignored) { return []; }
|
||||
}
|
||||
|
||||
function replyFor(result) {
|
||||
if (!result || result === "undefined" || result === "null") {
|
||||
return JSON.stringify({ success: false, error: "The After Effects connector received an empty evalScript result. Reopen the panel and retry once." });
|
||||
}
|
||||
try { return JSON.stringify(JSON.parse(result)); }
|
||||
catch (ignored) {
|
||||
return result.indexOf("Error") === 0
|
||||
? JSON.stringify({ success: false, error: result })
|
||||
: JSON.stringify({ success: true, data: result });
|
||||
}
|
||||
}
|
||||
|
||||
function processOne(fileName) {
|
||||
var source = path.join(tempDir, fileName);
|
||||
var claim = source + "." + engineId + ".claimed";
|
||||
try { fs.renameSync(source, claim); } catch (ignored) { return; }
|
||||
var script;
|
||||
try { script = fs.readFileSync(claim, "utf8"); } catch (error) { script = null; }
|
||||
try { if (fs.existsSync(claim)) fs.unlinkSync(claim); } catch (ignored) {}
|
||||
if (!script) return;
|
||||
var id = fileName.replace("cmd_", "").replace(".jsx", "");
|
||||
var busy = path.join(tempDir, "busy_" + id + ".json");
|
||||
var started = Date.now();
|
||||
var busyTimer = setInterval(function () {
|
||||
try { fs.writeFileSync(busy, JSON.stringify({ id: id, elapsedMs: Date.now() - started }), "utf8"); } catch (ignored) {}
|
||||
}, 2000);
|
||||
cs.evalScript(script, function (result) {
|
||||
clearInterval(busyTimer);
|
||||
try { if (fs.existsSync(busy)) fs.unlinkSync(busy); } catch (ignored) {}
|
||||
writeFileAtomic(path.join(tempDir, "res_" + id + ".json"), replyFor(String(result || "")));
|
||||
});
|
||||
}
|
||||
|
||||
function processCommands() {
|
||||
var files = readCommandFiles();
|
||||
for (var index = 0; index < files.length; index++) processOne(files[index]);
|
||||
}
|
||||
|
||||
function start() {
|
||||
tempDir = document.getElementById("tempDir").value.trim();
|
||||
if (!tempDir) { setStatus("Set a bridge directory", false); return; }
|
||||
try { fs.mkdirSync(tempDir, { recursive: true, mode: 0o700 }); }
|
||||
catch (error) { setStatus("Cannot create bridge directory", false); return; }
|
||||
running = true;
|
||||
heartbeat();
|
||||
if (pollTimer) clearInterval(pollTimer);
|
||||
if (heartbeatTimer) clearInterval(heartbeatTimer);
|
||||
pollTimer = setInterval(processCommands, 200);
|
||||
heartbeatTimer = setInterval(heartbeat, 1000);
|
||||
try { localStorage.setItem("after_effects_mcp_temp_dir", tempDir); } catch (ignored) {}
|
||||
setStatus("Connector running", true);
|
||||
}
|
||||
|
||||
function stop() {
|
||||
running = false;
|
||||
heartbeat();
|
||||
if (pollTimer) clearInterval(pollTimer);
|
||||
if (heartbeatTimer) clearInterval(heartbeatTimer);
|
||||
pollTimer = null;
|
||||
heartbeatTimer = null;
|
||||
setStatus("Stopped", false);
|
||||
}
|
||||
|
||||
var field = document.getElementById("tempDir");
|
||||
try { field.value = localStorage.getItem("after_effects_mcp_temp_dir") || tempDir; } catch (ignored) { field.value = tempDir; }
|
||||
document.getElementById("toggle").onclick = function () { if (running) stop(); else start(); };
|
||||
}());
|
||||
Reference in New Issue
Block a user