Files
gart/code/Engine/docs/02_MODULES.md
T
João HenriqueandClaude Opus 5 dcdd73edb5 docs: varredura geral, documentação por função e regra de atualização
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>
2026-08-19 22:51:33 -04:00

192 lines
8.3 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.
# 02 — Módulos do Engine (`fcpxml/`)
> **Escopo:** Mapa do engine `fcpxml/`: qual módulo faz o quê e onde mexer.
> **Não cobre:** Camadas e regras gerais (→ 01) · handlers MCP (→ 03) · o que está aberto (→ 09)
Mapa módulo a módulo do núcleo Python: onde cada coisa mora e o que ela faz.
A API pública é reexportada em `fcpxml/__init__.py` — essa é a fonte da verdade
do `__all__`.
Versão: `0.6.35` · Última varredura: 2026-08-19
> **Por que existem pacotes aqui.** `writer.py` tinha 4.199 linhas e `models.py`
> 1.091, cada um com muitos assuntos dentro. Viraram pacotes com um módulo por
> assunto. Do lado de fora **nada mudou**: `from .writer import FCPXMLModifier`
> e `from .models import TimeValue` seguem valendo, porque os `__init__.py`
> reexportam tudo — inclusive os nomes com underscore que a suíte usa.
---
## Visão geral
| Módulo / pacote | Linhas | Papel |
|-----------------|-------:|-------|
| `writer/` | 4.687 | **Edição e escrita de FCPXML** — o coração |
| `models/` | 1.195 | Data classes e enums |
| `text_layout.py` | 901 | Diagramação das legendas dinâmicas |
| `rough_cut.py` | 798 | Geração de timelines novas |
| `model_manager.py` | 748 | Modelos Whisper: catálogo, download, config |
| `voice_timeline.py` | 594 | O JSON de voz que a IA lê |
| `phrase_review.py` | 547 | Revisão de frases (etapa 5 do assistente) |
| `collision.py` | 472 | Colisão entre títulos na tela |
| `font_metrics.py` | 445 | Largura real de glifos por fonte |
| `templates.py` | 387 | Templates de timeline |
| `parser.py` | 367 | FCPXML → objetos Python |
| `transcribe.py` | 300 | Transcrição Whisper e corte por texto |
| `live.py` | 273 | Modo Live (push_to_fcp) |
| `diff.py` | 269 | Comparação de timelines |
| `voice_actions.py` | 263 | Decisões de edição (cut/zoom/text/marker) |
| `export.py` | 226 | Export Resolve v1.9 + FCP7 XMEML v5 |
| `voice_features.py` | 220 | Pitch, energia, ritmo, pausas |
| `diarize.py` | 180 | Quem falou (pyannote) |
| `media_intel.py` | 177 | Silêncio (ffmpeg) e beats (librosa) |
| `emphasis.py` | 133 | Índice de ênfase por palavra |
| `safe_xml.py` | 113 | `defusedxml` + `serialize_xml()` |
| `dtd.py` | 112 | Validação contra os DTDs da Apple |
---
## `writer/` — edição e escrita
O `FCPXMLModifier` é montado por **composição de mixins**: um mixin por assunto
editorial, todos operando sobre o mesmo documento e os mesmos índices.
| Módulo | Linhas | Conteúdo |
|--------|-------:|----------|
| `core.py` | 723 | `ModifierCore`: carga, índices, navegação na spine, `save` |
| `titles.py` | 600 | Títulos de texto e legendas dinâmicas |
| `cut.py` | 333 | Dividir, cortar faixas, apagar |
| `speed.py` | 297 | Velocidade e zoom (punch-in) |
| `helpers.py` | 279 | Sanitização, escalas, construtores de elemento |
| `rapid.py` | 240 | Flash frames, rapid trim, preencher buracos |
| `validation.py` | 232 | Verificações estruturais antes de salvar |
| `compound.py` | 196 | Compound clips: criar e achatar |
| `silence.py` | 185 | Detectar e remover silêncio |
| `document.py` | 170 | Assets de vídeo, timebases, `write_fcpxml` |
| `markers.py` | 165 | Marcadores: um, por timecode, em lote |
| `audio.py` | 162 | Clipes de áudio e cama musical |
| `generator.py` | 147 | `FCPXMLWriter` — cria documento do zero |
| `reorder.py` | 126 | Reordenar e recalcular offsets |
| `trim.py` | 125 | Aparar e propagar o ripple |
| `transitions.py` | 94 | Transições entre vizinhos |
| `relink.py` | 94 | Repontar mídia |
| `insert.py` | 78 | Inserir clipes na spine |
| `modifier.py` | 64 | Monta a classe a partir dos mixins |
| `selection.py` | 57 | Selecionar por palavra-chave |
| `api.py` | 55 | Atalhos de uma linha |
| `connected.py` | 49 | Clipes conectados (lanes) |
| `roles.py` | 43 | Atribuir roles |
| `reformat.py` | 43 | Reenquadrar resolução |
**Onde mexer:** ache o assunto na tabela e abra só aquele arquivo. Se a sua
mudança precisa de dois mixins ao mesmo tempo, provavelmente o que você quer
é um método novo no `core.py` que os dois chamem.
**Cuidado:** os mixins compartilham `self`. Um método novo que colida de nome
com outro mixin sobrescreve em silêncio — a ordem em `modifier.py` decide quem
ganha. Hoje nenhum colide; mantenha assim.
---
## `models/` — dados e enums
Fonte única da estrutura de dados. **Nunca mexa aqui sem rodar `test_models.py`.**
| Módulo | Linhas | Conteúdo |
|--------|-------:|----------|
| `timing.py` | 304 | `TimeValue` (fração racional), `Timecode` |
| `timeline.py` | 217 | `Clip`, `ConnectedClip`, `CompoundClip`, `Timeline`, `Project`, `Marker` |
| `enums.py` | 183 | `MarkerType`, `MarkerColor`, `TransitionType`, `PacingStyle`… |
| `subtitles.py` | 157 | `WordLook`, `WordStyle`, `DynamicSubtitleConfig`, paleta |
| `qc.py` | 121 | `FlashFrame`, `GapInfo`, `DuplicateGroup`, `ValidationIssue` |
| `planning.py` | 93 | `SegmentSpec`, `PacingConfig`, `RoughCutResult`, `MontageConfig` |
`MarkerType` é o dono da serialização de marcador (`from_string`,
`from_xml_element`, `xml_attrs`) — não reimplemente isso em outro lugar.
---
## O caminho da voz (do áudio à decisão)
Estes seis módulos formam um pipeline. É o fluxo mais novo e o menos óbvio do
projeto, então vale ler nesta ordem:
```
transcribe.py áudio → palavras com tempo
+
diarize.py quem falou cada trecho
+
voice_features.py pitch, energia, ritmo, pausas
▼
emphasis.py combina tudo num índice 0–1 por palavra
▼
voice_timeline.py monta o _voice_timeline.json ◄── é isto que a IA lê
▼
[decisão: skill "editar-por-voz", ou a mão do usuário]
▼
voice_actions.py valida a lista de cut/zoom/text/marker
▼
phrase_review.py funde tudo em frases revisáveis (etapa 5 do app)
▼
writer/ aplica no FCPXML
```
**Regra de ouro do pipeline:** toda ação carrega tempo da **mídia original**,
nunca pós-corte. Cortes deslocam tudo depois deles; resolver o deslocamento só
na hora de aplicar (`shift_after_cuts`) elimina uma classe inteira de bug.
### `voice_timeline.py` — o contrato com a IA
Saída em camadas, para um modelo raciocinar do topo e descer só onde importa:
```
{version, source, language,
layers: {transcript, acoustics, speakers, emotion} ← o que rodou de verdade
scales: {…} ← como ler cada número
summary: {…}
speakers: [...]
segments: [{start, end, speaker, text, gap_before, take_boundary,
avg_energy, peak_emphasis, emotion, emotion_confidence,
words: [{text, start, end, energy, pitch_delta, rate_delta,
pause_before, emphasis}]}]}
```
`layers` existe para separar *"a fala é monótona"* de *"a análise acústica nunca
carregou"* — os dois deixam os mesmos zeros nos dados.
### `phrase_review.py` — a revisão humana
Junta o timeline de voz com as ações da IA numa lista de frases editáveis, e
converte de volta. Frase inativa vira `cut`; ênfase ≥ 1 vira `zoom` mais um
`emphasis_spans` que a etapa de legendas usa. O trim de cada frase anda em
**fronteira de palavra** — cortar é apontar para uma palavra, nunca caçar frame.
---
## Legendas dinâmicas (três módulos que andam juntos)
| Módulo | Papel |
|--------|-------|
| `text_layout.py` | Quebra a frase em linhas e posiciona cada palavra |
| `font_metrics.py` | Largura real de cada glifo na fonte escolhida |
| `collision.py` | Detecta título saindo do quadro ou colidindo com outro |
Estes três não estão divididos porque **cada um já é um assunto só**. O
`text_layout.py` tem 901 linhas de um problema coeso: diagramação.
---
## Armadilhas do FCPXML (custaram sessões de depuração)
- Tempo é fração: `"3600/2400s"` = 1,5 s.
- `offset` é posição na timeline; `start` é o in-point da mídia.
- `<asset-clip>` (biblioteca) é diferente de `<clip>` (timeline).
- Marcadores são **filhos** do clipe, não irmãos.
- `.fcpxmld` é um **diretório** — sidecars precisam ser copiados no save, ou
dados de object tracking e Cinematic são destruídos.
- Negrito no FCP é `bold="1"` (atributo); itálico é `fontFace` + `italic="1"`.
- `id` de `<text-style-def>` precisa ser XML Name válido — acento, espaço ou
dígito inicial fazem o FCP recusar o arquivo inteiro.
- `code/examples/sample.fcpxml` **não** é DTD-conformante. Não use como fixture
de validade.