Files
gart/code/Engine/README.md
T
João HenriqueandClaude Sonnet 5 7b5aed79ee feat(voz): legenda por ênfase, forced align, IA local e correções de zoom/revisão
Trabalho da branch feat/revisao-enfases: pipeline de edição por voz ganha
alinhamento forçado (whisperx), roteirização por LLM local (Ollama), e a
etapa 5 (revisão de frases) passa a refletir de verdade o que é aplicado.

- generate_subtitles_by_emphasis: legenda comum cobre o clipe inteiro,
  legenda dinâmica só nas frases de ênfase, e a comum é desativada
  (enabled="0") onde a dinâmica cobre, em vez de nunca ser gerada ali.
- validate_subtitle_layout ignora títulos com enabled="0" — corrige falso
  positivo de colisão contra o que está desativado no lugar dele.
- Corrige zoom/marcador sendo descartado quando a borda encosta exatamente
  no início de um corte.
- Etapa 5 do Assistente: recarrega quando as decisões da IA mudam (com
  fresh=true, ignorando a revisão salva antiga) — resolve a dessincronia
  entre "ativa" na tela e o que já foi cortado no FCPXML.
- Etapa "Processar" reaplica as decisões da revisão (_phrase_actions.json)
  antes da cadeia de remoção de silêncio/legendas — antes, desativar uma
  frase na etapa 5 não tinha efeito nenhum no vídeo final.
- Etapa "Concluído" fundida em "Processar" — abrir no Final Cut/Finder
  aparece assim que termina, sem slide extra.
- Palavra clicável na etapa 5 agora funciona como toggle (clique de novo
  desfaz) e mostra a própria ênfase (sublinhado colorido + peso da fonte).
- fcpxml/forced_align.py, fcpxml/llm_local.py, ai_edit.py: alinhamento
  fonético via whisperx e roteirização local via Ollama/Gemma.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-21 18:26:04 -04:00

280 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# G-ART — Engine Overview
Este documento descreve a arquitetura interna do **G-ART / fcp-mcp-server**
(repositório `G-ART`), uma aplicação **MCP (Model Context Protocol) server em
Python** que lê, analisa e reescreve arquivos **FCPXML** do Final Cut Pro —
a ponte entre o Final Cut Pro e IA.
Diferente do CommandPost (automação de GUI via Lua/Hammerspoon), este projeto
opera **fora** do Final Cut Pro: você exporta o XML, o servidor processa o
documento como dados estruturados e devolve um XML modificado para importação.
Nada é patcheado, nenhuma API privada é usada.
Toda a análise foi feita a partir do código-fonte. Este README é a visão
geral; o detalhe módulo a módulo mora em `docs/02_MODULES.md`, que é o
documento a manter atualizado quando a estrutura mudar.
> **Começando agora?** Leia [01 Arquitetura](docs/01_ARCHITECTURE.md) e depois
> [09 Manutenção](docs/09_MANUTENCAO.md) — o primeiro diz como o sistema é
> dividido, o segundo diz por onde começar a mexer e o que está em aberto.
>
> **Guia completo:** [01 Arquitetura](docs/01_ARCHITECTURE.md) ·
> [02 Módulos](docs/02_MODULES.md) · [03 Server/Tools](docs/03_SERVER_TOOLS.md) ·
> [04 Testes & Workflow](docs/04_TESTS_AND_WORKFLOW.md) ·
> [05 Experiências](docs/05_EXPERIENCIAS.md) · [06 Boas Práticas](docs/06_BOAS_PRATICAS.md) ·
> [07 Projeto Ativo no FCP](docs/07_ESTUDO_PROJETO_ATIVO_FCP.md) ·
> [08 App macOS](docs/08_APP_MACOS.md) · [09 Manutenção](docs/09_MANUTENCAO.md)
---
## 1. O que o programa faz
1. **Um motor de parse/serialização FCPXML** — transforma timelines do Final
Cut Pro (XML v1.8–v1.14, flat `.fcpxml` e bundles `.fcpxmld`) em objetos
Python, e reescreve de volta sem perda de sidecars (object tracking,
Cinematic).
2. **Uma camada MCP de 74 ferramentas** — expõe análise, edição em lote, QC,
geração, exportação cross-NLE, inteligência de mídia (silêncio/beats) e
edição baseada em transcrição, tudo acessível por um cliente MCP (Claude).
3. **Modo Live (macOS)** — faz *push* de um FCPXML direto para o Final Cut Pro
em execução via Apple events oficiais (Open Document), sem re-importação
manual. Leitura de bibliotecas abertas via dicionário AppleScript read-only.
---
## 2. Pilha tecnológica
| Camada | Tecnologia |
|--------|-----------|
| Linguagem | **Python 3.10+** (~13k linhas em `server.py`, `server_tools/` e `fcpxml/`) |
| Protocolo MCP | **mcp** (`mcp` SDK), servidor por stdio |
| Parsing XML | **defusedxml** em todos os 4 entry points + `lxml`/`ElementTree` |
| Tempo racional | frações `numerador/denominador` no formato `"600/2400s"` |
| Análise de mídia | **ffmpeg** `silencedetect` (opcional) e **librosa** (extra `[intelligence]`) |
| Transcrição | **Whisper** local (extra `[transcribe]`) |
| Controle Live | **osascript** / Apple events para o bundle `com.apple.FinalCut` |
| Validação | **xmllint** contra os DTDs oficiais do bundle do Final Cut Pro |
| Licença | MIT |
---
## 3. Estrutura geral do repositório
```
G-ART/
├── CLAUDE.md # Regras do projeto para o agente
├── admin/ # Ponte com o app (fora de code/)
│ ├── models_api.py # Entry point: docstring dos comandos + dispatch
│ └── api/ # Os 37 comandos, um módulo por assunto
└── code/
├── server.py # MCP entry point — só dispatch
├── server_tools/ # Handlers das 74 tools + _shared/
├── fcpxml/ # "Engine" — biblioteca Python de núcleo
│ ├── writer/ # PACOTE: edição/escrita (mixins por assunto)
│ ├── models/ # PACOTE: dados por família (timing, timeline…)
│ ├── parser.py # FCPXML → objetos Python
│ ├── rough_cut.py # Gera timelines novas
│ ├── voice_*.py # Pipeline de voz (features → timeline → actions)
│ ├── phrase_review.py # Revisão de frases da etapa 5
│ ├── text_layout.py # Diagramação das legendas
│ ├── live.py # Modo Live — push_to_fcp
│ ├── safe_xml.py # defusedxml + serialize_xml()
│ └── dtd.py # Validação contra DTDs da Apple
├── MacApp/Sources/ # App SwiftUI (compilado por swiftc)
├── Engine/ # Esta documentação
├── docs/ # WORKFLOWS, CAPABILITY-AUDIT, specs
├── examples/ # Fixture de teste (sample.fcpxml)
└── tests/ # 1.466 testes / 42 suítes
```
---
## 4. O "Engine": a biblioteca `fcpxml/`
É o núcleo desacoplado do MCP. Não conhece o protocolo MCP nem os argumentos
das ferramentas — trabalha apenas com objetos Python e XML. `server.py` atua
como *camada de transporte/adaptação* que chama este núcleo.
### 4.1 Fundamentos de tempo — `models.TimeValue`
Toda hora é uma fração racional, nunca float. Isso elimina erro de arredondamento
em trim/split/speed em qualquer frame rate:
```python
TimeValue(600, 2400) # "600/2400s" == 0.25s
```
- Comparações por **multiplicação cruzada** (`a/b < c/d` → `a*d < c*b`),
permanecendo sempre em inteiros.
- Denominadores normalizados para positivos na construção — o sinal vive no
numerador.
- Soma/subtração compartilham um único caminho `_binop()` (fast-path de mesmo
denominador + alinhamento por LCM).
### 4.2 Modelos principais — `models/`
| Classe | Função |
|--------|--------|
| `TimeValue`, `Timecode` | Tempo racional e formatação/parse de timecode |
| `Clip`, `VideoClip`, `AudioClip` | Clips da timeline (offset, start, duration, markers) |
| `ConnectedClip` | Clips com atributo `lane` (acima/abaixo da espinha) |
| `CompoundClip` | Clips compostos |
| `Timeline`, `Project` | Contêineres de espinha + connected clips |
| `Marker`, `MarkerType`, `MarkerColor` | Marcadores; `INCOMPLETE` é canônico, `TODO` é alias |
| `SilenceCandidate`, `FlashFrame`, `GapInfo`, `DuplicateGroup` | Resultados de QC |
| `ValidationIssue`, `ValidationResult` | Resultados de validação |
| `SegmentSpec`, `PacingConfig`, `MontageConfig` | Parâmetros de geração |
**Single source of truth**: `MarkerType` enum é dono da serialização —
`from_string()` para entrada, `from_xml_element()` para parse, `xml_attrs` para
escrita. `from_xml_element` faz match estrito do atributo `completed`
(`'0'`/`'1'` apenas), rejeitando valores com padding de espaço.
### 4.3 Subsistemas
| Subsistema | Módulo | Função |
|-----------|--------|--------|
| Parser | `parser.py` | FCPXML → objetos Python: espinha, connected clips, secondary storylines, roles |
| Modifier | `writer/` (`FCPXMLModifier`) | Edição index-based (clips/resources/formats dicts) do documento existente |
| Writer | `writer/generator.py` | Gera FCPXML novo a partir de objetos Python |
| Rough cut | `rough_cut.py` | Gera timelines (rough cuts, montages, A/B roll) |
| Diff | `diff.py` | Compara timelines — detecta added/removed/moved/trimmed |
| Export | `export.py` | DaVinci Resolve v1.9 + FCP7 XMEML v5 |
| Media intel | `media_intel.py` | Detecção real de silêncio (ffmpeg) e beats (librosa, lazy) |
| Transcript | `transcribe.py` | Whisper local + edição por transcrição |
| Templates | `templates.py` | Estruturas pré-prontas (intro/outro, lower thirds, music video) |
| Live | `live.py` | push_to_fcp (Apple event) e list_fcp_libraries (AppleScript) |
| Segurança XML | `safe_xml.py` | Wrappers defusedxml centralizados + `serialize_xml()` |
| DTD | `dtd.py` | Valida output contra DTDs oficiais no bundle do FCP |
---
## 5. A camada MCP — `server.py`
### 5.1 Padrão de dispatch
Não há cadeias gigantes de `if/elif`. Um dicionário mapeia nome → handler
assíncrono:
```python
TOOL_HANDLERS = {
"analyze_timeline": handle_analyze_timeline,
"list_clips": handle_list_clips,
# ... 74 tools, todos em server_tools/
}
```
Cada ferramenta tem seu `async def handle_<name>(arguments: dict)`. Todas
retornam via `_text_result(text)`, que envolve strings no `TextContent` MCP.
### 5.2 Helpers centrais
| Helper | Linha | Função |
|--------|-------|--------|
| `_parse_project()` | `server.py:319` | Parseia FCPXML → `(tree, timeline, project)`; a maioria dos handlers começa aqui |
| `_resolve_io_paths()` | `server.py:357` | Consolida validação de caminho de entrada/saída |
| `_setup_modifier()` / `_setup_generator()` | `server.py:390` / `:414` | Preparam modifier/generator com validação |
| `_format_clip_table()` / `_markdown_table()` | `server.py:245` / `:259` | Renderização de tabelas |
| `_parse_timestamp_parts()` | `server.py:433` | Parse de timestamps (min:seg, H:MM:SS, SMPTE) |
| `_detect_flash_frames()` / `_detect_gaps()` / `_detect_duplicate_groups()` | `server.py:1667+` | Detectores de QC |
| `_validate_filepath()` / `_validate_output_path()` | `server.py:103` / `:149` | Sandbox de I/O |
| `_check_json_depth()` | `server.py:83` | Rejeita payloads aninhados além de 50 níveis |
### 5.3 Modo Live — `fcpxml/live.py`
Rode apenas as superfícies sancionadas da Apple — sem patch de binário, sem
APIs privadas, sem acessibilidade:
- **push_to_fcp** — import FCPXML via Apple event *Open Document*. Injeta um
`<import-options>` no documento (local da biblioteca, copy/link assets,
suprimir avisos). Requer um caminho `.fcpbundle` para *zero-click* de verdade;
sem ele, o FCP abre um modal "Open Library" que bloqueia até resposta humana.
- **list_fcp_libraries** — enumera bibliotecas → eventos → projetos via o
dicionário AppleScript read-only (suite `com.apple.FinalCut.library.inspection`).
**A assimetria estrutural**: import é scriptable, mas a Apple não oferece export
programático — para puxar a timeline atual de volta, você ainda roda
`File > Export XML`. O modo Live *empurra*; round-trips voltam pelas ferramentas
XML.
---
## 6. Fluxo de um pedido
```
Cliente MCP (Claude)
│ JSON-RPC (stdio)
▼
server.py ── dispatcher (TOOL_HANDLERS)
│
├── handle_* (valida caminho, _parse_project, opera)
│
├── fcpxml/parser.py (XML → objetos)
├── fcpxml/writer.py (edita / grava)
├── fcpxml/rough_cut.py (gera novas timelines)
├── fcpxml/export.py (cross-NLE)
│
▼
output_<suffix>.fcpxml (original nunca é sobrescrito)
│
▼
Final Cut Pro: File → Import → XML (ou push_to_fcp, sem cliques)
```
---
## 7. Validação padrão pós-correção (é obrigatório)
Regra do padrão do sistema: **sempre após concluir qualquer correção de
código, o sistema é automaticamente executado/validado.**
O gatilho é o script `[Engine/run_after_fix.sh](run_after_fix.sh)`. Toda vez
que você terminar uma correção, acione-o:
```bash
./Engine/run_after_fix.sh
```
Ele roda (a partir de qualquer diretório) e falha (`set -e`) se algo não
passar:
1. **`uv run ruff check . --exclude docs/`** — lint com zero erros.
2. **`uv run pytest tests/ -v`** — toda a suíte de testes passa.
Se falhar, corrija antes de prosseguir. Esta validação é o mesmo critério já
descrito em `CLAUDE.md` (Pre-Commit) — a diferença é que agora há um comando
único padronizado que garante a execução automática do sistema após cada
correção, sem depender de lembrar dos dois comandos no pre-commit.
---
## 8. Como navegar a documentação
> Esta pasta `Engine/` é o **hub da documentação**. Comece por aqui.
### Documentação padrão (leia nesta ordem)
- [docs/01_ARCHITECTURE.md](docs/01_ARCHITECTURE.md) — como o sistema é dividido
(camadas: admin → server → fcpxml/) e como se conectam. **Leia antes de qualquer mudança.**
- [docs/02_MODULES.md](docs/02_MODULES.md) — guia módulo a módulo do `fcpxml/`
(responsabilidade, tamanho, APIs públicas).
- [docs/03_SERVER_TOOLS.md](docs/03_SERVER_TOOLS.md) — a camada MCP `server.py`,
74 ferramentas, helpers e o padrão de handler.
- [docs/04_TESTS_AND_WORKFLOW.md](docs/04_TESTS_AND_WORKFLOW.md) — suíte de testes,
fluxo de trabalho (lint + pytest), execução e estado atual do sistema.
- [docs/05_EXPERIENCIAS.md](docs/05_EXPERIENCIAS.md) — **memória de projeto**:
registro cumulativo de problemas estruturais, erros recorrentes e decisões.
**Atualize sempre que um problema for detectado/corrigido.**
- [docs/06_BOAS_PRATICAS.md](docs/06_BOAS_PRATICAS.md) — **boas práticas de
programação** a aplicar em toda alteração/correção; inclui checklist final.
### Outros documentos
- [../CLAUDE.md](../../CLAUDE.md) — visão geral, key patterns, execução e pre-commit.
- [../docs/CAPABILITY-AUDIT-2026-06.md](../docs/CAPABILITY-AUDIT-2026-06.md) —
auditoria do ecossistema e roadmap dual-mode (XML + Live).
- [../docs/WORKFLOWS.md](../docs/WORKFLOWS.md) — 8 receitas de workflow de produção.
- [../docs/specs/](../docs/specs/) — schemas de tools, estrutura FCPXML, pseudocódigo
do writer, algoritmo de rough cut, implementação do server, roadmap, modelos.
- [../admin/graphify.md](../../admin/graphify.md) — pipeline de graphify do código.