feat: documentada a entrada universal da skill edit-video-by-voice
-documentada a entrada universal da skill edit-video-by-voice e reforçados os limites do contrato de ações 1.0 - corrigida a identificação da mídia na engine Python usando nome do clipe e nome-base de sourceFile Resumo: - 5 arquivos alterados - 1 novos - 4 modificados - 0 removidos 4 files changed, 54 insertions(+), 16 deletions(-) Arquivos: - code/engine/editor/aplicador_de_plano_de_edicao.py - code/engine/testes/test_aplicador_de_plano_de_edicao.py - code/plugins/premiere-pro/skills/edit-video-by-voice/SKILL.md - code/plugins/premiere-pro/skills/edit-video-by-voice/agents/openai.yaml - code/plugins/premiere-pro/skills/edit-video-by-voice/references/input-schema.md
This commit is contained in:
@@ -9,6 +9,7 @@ preserva a validade das posições calculadas — ver docstring de
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from pathlib import Path
|
||||
|
||||
from ..dominio import Clipe, Faixa, Timeline
|
||||
from ..integracoes.premiere.conversores import ConversorDeTimeline
|
||||
@@ -305,8 +306,7 @@ class AplicadorDePlanoDeEdicao:
|
||||
deixou uma faixa de áudio inteira sem ser cortada numa aplicação
|
||||
real deste plano.
|
||||
"""
|
||||
alvo = arquivo_de_origem.strip().lower()
|
||||
nomeados = [clipe for clipe in faixa.clipes if (clipe.nome or "").strip().lower() == alvo]
|
||||
nomeados = [clipe for clipe in faixa.clipes if self._clipe_corresponde_a_fonte(clipe, arquivo_de_origem)]
|
||||
sem_origem = [clipe for clipe in nomeados if clipe.intervalo_na_origem is None]
|
||||
if sem_origem:
|
||||
raise ErroDeMapeamentoDeTempo(
|
||||
@@ -316,6 +316,23 @@ class AplicadorDePlanoDeEdicao:
|
||||
)
|
||||
return sorted(nomeados, key=lambda clipe: clipe.intervalo_na_origem.inicio)
|
||||
|
||||
@staticmethod
|
||||
def _clipe_corresponde_a_fonte(clipe: Clipe, arquivo_de_origem: str) -> bool:
|
||||
"""Compara ``source`` com o nome do clipe ou com o nome-base da mídia real.
|
||||
|
||||
O Premiere pode devolver no campo ``name`` um nome editorial diferente
|
||||
do arquivo de mídia, enquanto ``sourceFile`` contém o caminho real.
|
||||
A comparação por nome-base mantém o plano portátil entre máquinas e
|
||||
evita exigir que a IA conheça caminhos locais. A busca continua
|
||||
restrita à faixa atual; nenhuma fonte é escolhida por aproximação.
|
||||
"""
|
||||
fonte_informada = Path(str(arquivo_de_origem).strip()).name.casefold()
|
||||
if not fonte_informada:
|
||||
return False
|
||||
nome_do_clipe = (clipe.nome or "").strip().casefold()
|
||||
caminho_da_midia = Path(str(clipe.arquivo or "").strip()).name.casefold()
|
||||
return fonte_informada in {nome_do_clipe, caminho_da_midia}
|
||||
|
||||
def _borda_de_entrada(self, clipe: Clipe, inicio_de_origem: float) -> float:
|
||||
"""Posição de entrada do corte na timeline: a borda do clipe se o corte começa antes dele."""
|
||||
if inicio_de_origem <= clipe.intervalo_na_origem.inicio:
|
||||
|
||||
@@ -10,6 +10,7 @@ do clipe lido ao vivo, nunca de um tempo fixo.
|
||||
import unittest
|
||||
from typing import Any
|
||||
|
||||
from engine.dominio import Clipe, Faixa, IntervaloDeTempo
|
||||
from engine.editor.aplicador_de_plano_de_edicao import AplicadorDePlanoDeEdicao
|
||||
from engine.editor.escrita import EscritaNoEditor
|
||||
from engine.editor.mapeamento import MapeadorDeTempoDeOrigemParaTimeline
|
||||
@@ -44,6 +45,20 @@ class TesteAplicadorDePlanoDeEdicao(unittest.TestCase):
|
||||
{"clipe_a_video", "clipe_a_audio"},
|
||||
)
|
||||
|
||||
def test_localiza_fonte_por_nome_base_do_source_file(self):
|
||||
"""Aceita um source completo quando o Premiere retorna outro nome de clipe."""
|
||||
faixa = Faixa("video_0", "Vídeo 1", "video", 0, [
|
||||
Clipe(
|
||||
identificador="clipe_real",
|
||||
nome="Câmera 01",
|
||||
intervalo_na_timeline=IntervaloDeTempo(0.0, 20.0),
|
||||
intervalo_na_origem=IntervaloDeTempo(0.0, 20.0),
|
||||
arquivo="/Volumes/Mídia/0E6A8290.MP4",
|
||||
)
|
||||
])
|
||||
clipes = self.aplicador._clipes_do_arquivo(faixa, "/Volumes/OutroLugar/0e6a8290.mp4")
|
||||
self.assertEqual([clipe.identificador for clipe in clipes], ["clipe_real"])
|
||||
|
||||
def test_marcador_e_zoom_sao_aplicados_depois_dos_cortes(self):
|
||||
# Zoom muda uma propriedade do clipe inteiro (set_clip_properties),
|
||||
# não de um trecho dele. Se o zoom rodasse antes do corte, cairia no
|
||||
|
||||
Reference in New Issue
Block a user