feat: alinhada a remoção da engine Python ao fluxo ripple do execu
- alinhada a remoção da engine Python ao fluxo ripple do executor CEP para cortar vídeo e áudio - criada remoção atômica para manter vídeo e áudio vinculados sincronizados - criada aba de configuração de legendas offline e inclusão das preferências no pacote para a IA - criado gerador offline de blocos de legenda sincronizados com palavras da transcrição e cortes aprovados - criada uma versão base portátil da skill edit-video-by-voice para testes em outros agentes de IA - incorporado perfil editorial opcional à skill portátil para orientar decisões de edição Resumo: - 14 arquivos alterados - 4 novos - 10 modificados - 0 removidos 10 files changed, 278 insertions(+), 33 deletions(-) Arquivos: - code/cep-plugin/index.html - code/cep-plugin/main.js - code/docs/supported-actions.md - code/engine/editor/aplicador_de_plano_de_edicao.py - code/engine/editor/escrita/escrita_no_editor.py - code/engine/testes/duplos_de_premiere.py - code/engine/testes/test_aplicador_de_plano_de_edicao.py - code/src/tools/timeline.ts - code/tests/tools/structural-verification.test.ts - code/tests/tools/tool-modules.test.ts - code/engine/arquitetura/legendas.md - code/engine/editor/legendas/ - code/engine/testes/test_gerador_de_legendas.py - skills/
This commit is contained in:
@@ -144,6 +144,56 @@ export function getTimelineTools(bridgeOptions: BridgeOptions) {
|
||||
},
|
||||
},
|
||||
|
||||
remove_linked_from_timeline: {
|
||||
description:
|
||||
"Remove um par lógico de vídeo e áudio da timeline em uma única operação, fechando o espaço nos dois lados.",
|
||||
parameters: {
|
||||
type: "object" as const,
|
||||
properties: {
|
||||
node_ids: {
|
||||
type: "array",
|
||||
items: { type: "string" },
|
||||
minItems: 2,
|
||||
maxItems: 2,
|
||||
description: "IDs dos dois clipes correspondentes de vídeo e áudio.",
|
||||
},
|
||||
},
|
||||
required: ["node_ids"],
|
||||
},
|
||||
handler: async (args: { node_ids: string[] }) => {
|
||||
if (!Array.isArray(args.node_ids) || args.node_ids.length !== 2 || args.node_ids.some((id) => typeof id !== "string" || !id)) {
|
||||
return { success: false, error: "node_ids deve conter exatamente dois IDs de clipes." };
|
||||
}
|
||||
const ids = args.node_ids.map(escapeForExtendScript);
|
||||
const script = buildToolScript(`
|
||||
var primeiro = __findClip("${ids[0]}");
|
||||
var segundo = __findClip("${ids[1]}");
|
||||
if (!primeiro || !segundo) return __error("Um dos clipes do par não foi encontrado; nenhuma remoção foi confirmada.");
|
||||
if (primeiro.trackType === segundo.trackType) return __error("O par precisa conter um clipe de vídeo e um de áudio.");
|
||||
|
||||
var primeiroInicio = parseFloat(primeiro.clip.start.ticks);
|
||||
var segundoInicio = parseFloat(segundo.clip.start.ticks);
|
||||
var primeiroFim = parseFloat(primeiro.clip.end.ticks);
|
||||
var segundoFim = parseFloat(segundo.clip.end.ticks);
|
||||
if (Math.abs(primeiroInicio - segundoInicio) > 1 || Math.abs(primeiroFim - segundoFim) > 1) {
|
||||
return __error("Os clipes de vídeo e áudio não ocupam o mesmo intervalo; nenhuma remoção foi feita.");
|
||||
}
|
||||
|
||||
// alignToVideo=false evita que a remoção do áudio seja reposicionada
|
||||
// pelo estado anterior do vídeo. As duas remoções são feitas nesta
|
||||
// mesma chamada, depois da validação do par.
|
||||
primeiro.clip.remove(true, false);
|
||||
segundo.clip.remove(true, false);
|
||||
|
||||
if (__findClip("${ids[0]}") || __findClip("${ids[1]}")) {
|
||||
return __error("O Premiere não confirmou a remoção dos dois clipes vinculados.");
|
||||
}
|
||||
return __result({ removed: true, verified: true, nodeIds: ["${ids[0]}", "${ids[1]}"] });
|
||||
`);
|
||||
return sendCommand(script, bridgeOptions);
|
||||
},
|
||||
},
|
||||
|
||||
move_clip: {
|
||||
description: "Move a clip to a new position on the timeline",
|
||||
parameters: {
|
||||
|
||||
Reference in New Issue
Block a user