Files
gart/code/Engine/docs/01_ARCHITECTURE.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

8.8 KiB

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 · 77 ferramentas MCP · 1.498 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/            │     │  77 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: admin/run_app.command (padrão de revisão; equivale a ./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 (77 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.498 casos sem abrir o app nem subir o MCP.