A documentação descrevia um sistema que não existe mais: 62/73 ferramentas
(são 74), writer.py e models.py como arquivos (viraram pacotes), 1032 testes
(são 1454), models_api.py descrito como "API FastAPI" (é ponte JSON) e o app
SwiftUI ausente por completo — 5.500 linhas que o usuário opera todo dia sem
uma linha de documentação.
Cada arquivo passa a ter uma função específica, com cabeçalho de escopo
dizendo o que cobre e o que NÃO cobre (com a seta para quem cobre). O objetivo
é ler só o necessário: doc fora do assunto custa tempo e processamento sem
entregar nada.
01 arquitetura camadas, duas portas de entrada, regras transversais
02 módulos mapa do engine, incluindo o pipeline de voz
03 server/tools as 74 tools, helpers e como criar uma nova
08 app macOS NOVO — build por swiftc, telas, ponte, etapa 5
09 manutenção NOVO — por onde começar, o que está aberto, sintoma→arquivo
CLAUDE.md ganha a seção "Documentação (MANDATORY)": tabela de roteamento
(qual arquivo abrir para cada tarefa) e a regra de que toda alteração de
código atualiza a doc no mesmo commit, com o mapa de o-que-mexeu → o-que-
atualizar. Doc velha engana mais que doc ausente.
O índice do 05_EXPERIENCIAS subiu para o topo: consultar "isso já quebrou
antes?" custava carregar 1.281 linhas antes de chegar na tabela.
Dívidas levantadas na varredura e registradas em 09 §2: etapa 6 ainda ignora
o phrase_review.json, offset de ~400ms do Whisper, MacApp sem teste, admin/
fora do lint, confirmações visuais pendentes no FCP, submódulo WHISPERX sujo.
Também corrigidos dois links quebrados no Engine/README que apontavam um
nível acima do certo.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
178 lines
8.7 KiB
Markdown
178 lines
8.7 KiB
Markdown
# 01 — Arquitetura do Sistema (G-ART / fcp-mcp-server)
|
|
|
|
> **Escopo:** Como o sistema é dividido em camadas e onde cada responsabilidade mora.
|
|
> **Não cobre:** Detalhe módulo a módulo (→ 02) · ferramentas MCP (→ 03) · app (→ 08)
|
|
|
|
> Referência canônica de como o sistema está dividido. Leia antes de qualquer
|
|
> mudança de código. Se algo aqui divergir do código, **o código está certo e
|
|
> este documento está velho** — corrija-o no mesmo commit.
|
|
|
|
Última varredura: 2026-08-19 · 74 ferramentas MCP · 1.454 testes · versão `0.6.35`
|
|
|
|
---
|
|
|
|
## 1. Visão de cima (camadas)
|
|
|
|
O sistema lê, analisa e reescreve **FCPXML** do Final Cut Pro. Ele opera *fora*
|
|
do FCP: você exporta o XML, o programa processa como dados estruturados e
|
|
devolve um XML para importar. Nada é patcheado, nenhuma API privada é usada.
|
|
|
|
São **quatro camadas**, e o ponto importante é que existem **duas portas de
|
|
entrada diferentes** para o mesmo motor:
|
|
|
|
```
|
|
┌──────────────────────────┐ ┌──────────────────────────────┐
|
|
│ MacApp/ (SwiftUI) │ │ Cliente MCP (Claude) │
|
|
│ O app que o usuário usa │ │ Conversa, decide a edição │
|
|
└───────────┬──────────────┘ └───────────────┬──────────────┘
|
|
│ subprocesso + JSON-lines │ JSON-RPC (stdio)
|
|
▼ ▼
|
|
┌──────────────────────────┐ ┌──────────────────────────────┐
|
|
│ admin/models_api.py │ │ server.py + server_tools/ │
|
|
│ + admin/api/ │ │ 74 tools, dispatch, schemas │
|
|
│ 37 comandos da ponte │ │ NÃO tem lógica de timeline │
|
|
└───────────┬──────────────┘ └───────────────┬──────────────┘
|
|
└───────────────┬────────────────────┘
|
|
▼
|
|
┌───────────────────────────────────┐
|
|
│ fcpxml/ — O ENGINE │
|
|
│ Núcleo puro Python, desacoplado. │
|
|
│ Não conhece MCP nem o app. │
|
|
│ É onde quase tudo mora. │
|
|
└───────────────────────────────────┘
|
|
```
|
|
|
|
**A regra que sustenta tudo:** nem `server.py` nem `admin/api/` implementam
|
|
lógica de timeline. Os dois validam entrada, chamam o engine e formatam a
|
|
saída. Toda regra de negócio é testável sem MCP e sem app.
|
|
|
|
**Por que duas portas.** O MCP existe para o julgamento editorial — qual tomada
|
|
usar, onde dar zoom — que é conversa com uma IA. A ponte existe para o que o
|
|
usuário faz sozinho no app — transcrever, configurar, processar. As duas caem
|
|
no mesmo engine, então uma correção ali vale para as duas.
|
|
|
|
---
|
|
|
|
## 2. Regras transversais (valem em todo o código)
|
|
|
|
| Conceito | Regra |
|
|
|----------|-------|
|
|
| **Tempo** | `TimeValue`, fração racional `"600/2400s"`. **Nunca float para tempo.** |
|
|
| **Tempo de decisão** | Ações de voz usam sempre segundos da **mídia original**, nunca pós-corte. |
|
|
| **I/O paths** | Sempre via `_validate_filepath` / `_validate_output_path` (sandbox). |
|
|
| **Nome de saída** | Nunca sobrescrever o original: `generate_output_path()` gera `_suffix`. |
|
|
| **Segurança XML** | Sempre `defusedxml` via `safe_xml.py`. Nunca `xml.etree` direto para ler. |
|
|
| **Deps opcionais** | `librosa`/`ffmpeg`/`huggingface_hub` importados **lazy**, degradam com `None`. |
|
|
| **Idioma** | Comunicação com o usuário em português. Código e comentários em inglês. |
|
|
| **Validação** | `./Engine/run_after_fix.sh` **sempre** após cada correção. |
|
|
| **App** | Alterou `MacApp/`? Compile e rode: `./MacApp/build_app.sh --run`. |
|
|
|
|
---
|
|
|
|
## 3. Fluxo de um request
|
|
|
|
### Pela porta MCP (Claude decidindo a edição)
|
|
|
|
```
|
|
Cliente MCP ──JSON-RPC──► server.py
|
|
│ TOOL_HANDLERS[nome]
|
|
▼
|
|
server_tools/<categoria>.py
|
|
│ _shared/: valida path, parseia projeto
|
|
▼
|
|
fcpxml/ (parser → writer → safe_xml)
|
|
▼
|
|
projeto_<suffix>.fcpxml (original intocado)
|
|
```
|
|
|
|
### Pela porta do app (usuário operando)
|
|
|
|
```
|
|
MacApp ──Process + argv JSON──► admin/models_api.py
|
|
│ handlers[comando]
|
|
▼
|
|
admin/api/<assunto>.py
|
|
│ shared.emit() devolve JSON-lines
|
|
▼
|
|
fcpxml/ (ou chama um handler do server)
|
|
▼
|
|
arquivo gerado + caminho de volta ao app
|
|
```
|
|
|
|
A saída da ponte é **JSON-lines**: um documento JSON por linha, para que
|
|
comandos longos transmitam progresso enquanto rodam. Toda escrita passa por
|
|
`admin/api/shared.py::emit`, que serializa o acesso a stdout — dois comandos
|
|
escrevendo ao mesmo tempo entrelaçariam documentos.
|
|
|
|
---
|
|
|
|
## 4. Dual-mode: XML + Live
|
|
|
|
- **Modo XML (principal):** exporta FCPXML, processa como dados, reimporta.
|
|
- **Modo Live (`fcpxml/live.py`):** *push* do FCPXML direto para o FCP em
|
|
execução via Apple events oficiais (`Open Document`). Leitura de bibliotecas
|
|
via AppleScript read-only.
|
|
|
|
**Assimetria estrutural:** import é scriptable, mas a Apple não oferece export
|
|
programático. Round-trips sempre voltam pelas ferramentas XML.
|
|
|
|
---
|
|
|
|
## 5. Onde está cada responsabilidade
|
|
|
|
| Responsabilidade | Fica em |
|
|
|------------------|---------|
|
|
| Modelos de dados (tempo, clips, markers, QC, legendas) | `fcpxml/models/` |
|
|
| Parse FCPXML → objetos | `fcpxml/parser.py` |
|
|
| Edição e escrita de FCPXML | `fcpxml/writer/` |
|
|
| Geração de timeline nova | `fcpxml/rough_cut.py` |
|
|
| Comparação de timelines | `fcpxml/diff.py` |
|
|
| Export cross-NLE (Resolve, FCP7) | `fcpxml/export.py` |
|
|
| Silêncio e beats | `fcpxml/media_intel.py` |
|
|
| Transcrição Whisper | `fcpxml/transcribe.py` |
|
|
| Diarização (quem falou) | `fcpxml/diarize.py` |
|
|
| Ênfase acústica | `fcpxml/emphasis.py`, `fcpxml/voice_features.py` |
|
|
| Timeline de voz (o JSON que a IA lê) | `fcpxml/voice_timeline.py` |
|
|
| Decisões de edição (cut/zoom/text/marker) | `fcpxml/voice_actions.py` |
|
|
| Revisão de frases da etapa 5 | `fcpxml/phrase_review.py` |
|
|
| Layout de legendas e métricas de fonte | `fcpxml/text_layout.py`, `font_metrics.py`, `collision.py` |
|
|
| Gestão de modelos Whisper | `fcpxml/model_manager.py` |
|
|
| Controle Live do FCP | `fcpxml/live.py` |
|
|
| Segurança XML | `fcpxml/safe_xml.py` |
|
|
| Validação contra DTDs da Apple | `fcpxml/dtd.py` |
|
|
| Transporte MCP (74 tools) | `server.py` + `server_tools/` |
|
|
| Ponte com o app (37 comandos) | `admin/models_api.py` + `admin/api/` |
|
|
| Interface do usuário | `MacApp/Sources/` |
|
|
|
|
---
|
|
|
|
## 6. Mapa de dependências
|
|
|
|
```
|
|
MacApp/ ──► admin/models_api.py (subprocesso, por caminho)
|
|
admin/api/ ──► fcpxml/* e, para algumas operações, server.py
|
|
server.py ──► server_tools/*
|
|
server_tools/* ──► server_tools/_shared/ ──► fcpxml/*
|
|
fcpxml/writer/ ──► fcpxml/models/, safe_xml, dtd, text_layout, collision
|
|
fcpxml/models/ ──► fcpxml/text_layout (só o pacote subtitles)
|
|
fcpxml/__init__.py ──► reexporta a API pública
|
|
```
|
|
|
|
**A seta que não existe, e não deve existir:** `fcpxml/` nunca importa de
|
|
`server_tools/`, de `admin/` ou de qualquer coisa que saiba o que é uma tool.
|
|
Se você precisar disso, a lógica está no lugar errado.
|
|
|
|
---
|
|
|
|
## 7. Criando algo novo — por onde começar
|
|
|
|
| Você quer… | Comece por |
|
|
|-----------|-----------|
|
|
| Uma **ferramenta MCP** nova | Função pura em `fcpxml/` + teste. O handler em `server_tools/` fica fino. |
|
|
| Um **comando do app** novo | Mesmo caminho, e exponha em `admin/api/<assunto>.py` + tabela em `models_api.py`. |
|
|
| Uma **tela** nova | `MacApp/Sources/`, consumindo comandos que já existem na ponte. |
|
|
| Uma **regra de edição** nova | `fcpxml/` sempre. Se você está escrevendo `if` sobre timeline fora de `fcpxml/`, pare. |
|
|
|
|
O trabalho principal é **sempre** no engine. As camadas de cima são finas de
|
|
propósito: é o que permite testar 1.454 casos sem abrir o app nem subir o MCP.
|