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>
280 lines
13 KiB
Markdown
280 lines
13 KiB
Markdown
# 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.
|