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>
175 lines
9.7 KiB
Markdown
Executable File
175 lines
9.7 KiB
Markdown
Executable File
# CLAUDE.md — fcp-mcp-server
|
||
|
||
## Idioma (MANDATORY)
|
||
|
||
Responder **sempre em português** ao usuário, sem exceção. Nunca responder em
|
||
inglês nas mensagens de chat/prompt — inclusive resumos, atualizações de
|
||
progresso e confirmações. Comentários e nomes de código continuam em inglês
|
||
normalmente; a regra é sobre a comunicação com o usuário.
|
||
|
||
## What This Is
|
||
|
||
MCP server that reads/writes Final Cut Pro XML (FCPXML) files. 77 tools for timeline analysis, batch editing, QC, generation, multi-track support, media relink, NLE export, transcript-based editing (local Whisper), LIVE FCP control (push_to_fcp / list_fcp_libraries via Apple events), and local-LLM voice scripting (editar-por-voz against Ollama/Gemma 3). Reads FCPXML 1.8–1.14 (incl. `.fcpxmld` bundles with sidecar preservation), writes 1.13 by default. Dual-mode (XML + Live) direction: `code/docs/CAPABILITY-AUDIT-2026-06.md`.
|
||
|
||
## Architecture
|
||
|
||
Toda a estrutura do projeto fica em `code/`. A pasta `admin/` fica na raiz (fora de `code/`).
|
||
|
||
Há **duas portas de entrada** para o mesmo engine: o MCP (Claude decide a
|
||
edição) e a ponte JSON (o app macOS opera). Nenhuma das duas tem lógica de
|
||
timeline — as duas delegam a `fcpxml/`.
|
||
|
||
```
|
||
code/server.py — MCP entry point (592 linhas). Só dispatch: TOOL_HANDLERS.
|
||
code/server_tools/ — Os handlers das 77 tools, um módulo por categoria.
|
||
code/server_tools/_shared/ — Helpers compartilhados (paths, project, formatting,
|
||
captions, detection, media).
|
||
|
||
code/fcpxml/parser.py — Reads FCPXML → Python objects (Timeline, Clip, Marker…)
|
||
code/fcpxml/writer/ — PACOTE. Edição/escrita de FCPXML. FCPXMLModifier é
|
||
montado por mixins, um por assunto (markers, trim,
|
||
speed, titles, cut, silence…). Ver writer/modifier.py.
|
||
code/fcpxml/models/ — PACOTE. Data classes por família: timing, timeline,
|
||
enums, subtitles, qc, planning.
|
||
code/fcpxml/rough_cut.py — Generates new timelines (rough cuts, montages, A/B rolls).
|
||
code/fcpxml/diff.py — Timeline comparison engine.
|
||
code/fcpxml/export.py — DaVinci Resolve v1.9 + FCP7 XMEML v5 export.
|
||
code/fcpxml/media_intel.py — Silence detection + beat detection.
|
||
code/fcpxml/dtd.py — Validates output against Apple's official DTDs.
|
||
code/fcpxml/voice_*.py — Pipeline de voz: features → emphasis → voice_timeline
|
||
→ voice_actions → phrase_review. Ver Engine/docs/02.
|
||
|
||
admin/models_api.py — Ponte JSON com o app: docstring de comandos + dispatch.
|
||
admin/api/ — Os 37 comandos, um módulo por assunto.
|
||
code/MacApp/Sources/ — App SwiftUI. Compilado por swiftc (sem Xcode/SPM).
|
||
```
|
||
|
||
Os dois `__init__.py` de pacote (`writer/`, `models/`) reexportam tudo, então
|
||
`from .writer import FCPXMLModifier` e `from .models import TimeValue` seguem
|
||
valendo em todo o projeto.
|
||
|
||
## Documentação (MANDATORY)
|
||
|
||
A documentação viva fica em `code/Engine/docs/`. Cada arquivo tem **uma função
|
||
específica** — leia só o que a tarefa exige, não o conjunto. Carregar
|
||
documentação que não é do assunto custa tempo e processamento sem entregar nada.
|
||
|
||
### Qual arquivo abrir
|
||
|
||
| Sua tarefa | Abra | Não precisa de |
|
||
|-----------|------|----------------|
|
||
| Entender como o sistema é dividido | `01_ARCHITECTURE.md` | o resto |
|
||
| Achar onde mora uma função do engine | `02_MODULES.md` | 01, 03 |
|
||
| Criar/alterar uma ferramenta MCP | `03_SERVER_TOOLS.md` | 08 |
|
||
| Entender ou rodar os testes | `04_TESTS_AND_WORKFLOW.md` | — |
|
||
| "Isso já quebrou antes?" | `05_EXPERIENCIAS.md` — **só o índice no topo** | as entradas que não são a sua |
|
||
| Checklist antes de fechar | `06_BOAS_PRATICAS.md` | — |
|
||
| Mexer no app / no Assistente | `08_APP_MACOS.md` | 02, 03 |
|
||
| Escolher o que fazer, ver o que está aberto | `09_MANUTENCAO.md` | — |
|
||
|
||
Quando não souber por onde começar: `09_MANUTENCAO.md`. Ele roteia para o resto.
|
||
|
||
### Regra de atualização (obrigatória)
|
||
|
||
**Toda alteração de código atualiza a documentação no mesmo commit.** Doc velha
|
||
engana mais do que doc ausente — quem lê confia nela e erra com confiança.
|
||
|
||
| Você alterou | Atualize |
|
||
|--------------|----------|
|
||
| Estrutura de pastas, camadas ou dependências | `01_ARCHITECTURE.md` |
|
||
| Criou/moveu/dividiu módulo em `fcpxml/` | `02_MODULES.md` (tabela + linhas) |
|
||
| Criou/removeu ferramenta MCP | `03_SERVER_TOOLS.md` + contagem no `CLAUDE.md` |
|
||
| Comando da ponte | docstring de `admin/models_api.py` + `08_APP_MACOS.md` |
|
||
| Tela ou fluxo do app | `08_APP_MACOS.md` |
|
||
| Resolveu ou abriu uma dívida | `09_MANUTENCAO.md` §2 |
|
||
| Bateu num problema estrutural ou erro recorrente | `05_EXPERIENCIAS.md` + **índice no topo** |
|
||
|
||
Se um número (tools, testes, linhas) mudou, corrija onde ele aparece. Se um
|
||
documento divergir do código, **o código está certo** — conserte o documento.
|
||
|
||
### Ao escrever documentação
|
||
|
||
- **Um assunto por arquivo.** Se um doc começar a cobrir dois, divida.
|
||
- **Diga o que não está ali** e para onde ir — economiza a leitura seguinte.
|
||
- **Fatos verificados**, não suposições: rode o comando e use o número real.
|
||
- **Registre o porquê**, não só o quê. O "o quê" está no código; o "por quê"
|
||
se perde, e é o que evita alguém desfazer uma decisão por engano.
|
||
|
||
## Key Patterns
|
||
|
||
- **TimeValue**: All times are rational fractions (numerator/denominator) matching FCPXML's `"600/2400s"` format. Never use floats for time math.
|
||
- **_parse_project()**: Helper that parses FCPXML and returns `(tree, timeline, project)` tuple. Most handlers start with this.
|
||
- **generate_output_path()**: Creates `_modified`, `_chapters`, etc. suffixed output paths so originals aren't overwritten.
|
||
- **Tool handlers**: Each tool has its own `async def handle_<name>(arguments: dict)` function. All return via `_text_result(text)` which wraps strings in the MCP `TextContent` list.
|
||
- **Connected clips**: Clips with `lane` attribute hang off spine clips. Positive lane = above (video), negative = below (audio). Secondary `<storyline>` elements also contain connected clips.
|
||
- **XMEML export**: Converts spine-based model to track-based model. Primary storyline → Track 0, connected clip lanes → higher tracks.
|
||
|
||
## Running
|
||
|
||
```bash
|
||
cd code && uv run server.py # Start MCP server
|
||
cd code && uv run --extra dev pytest tests/ -v # Run tests
|
||
```
|
||
|
||
## Registro de Experiências e Boas Práticas (MANDATORY)
|
||
|
||
Sempre que um problema de estrutura ou um erro recorrente for detectado e
|
||
corrigido, **registre-o** em `code/Engine/docs/05_EXPERIENCIAS.md` (template já
|
||
presente no arquivo). Antes de concluir qualquer alteração/correção, aplique e
|
||
verifique as boas práticas e o checklist final em
|
||
`code/Engine/docs/06_BOAS_PRATICAS.md`.
|
||
|
||
## Pre-Commit (MANDATORY)
|
||
|
||
Padrão do sistema: **sempre após concluir uma correção, o sistema é
|
||
automaticamente executado/validado.** Acione o script único a cada correção:
|
||
|
||
```bash
|
||
cd code && ./Engine/run_after_fix.sh
|
||
```
|
||
|
||
Ele roda o lint (zero erros) e toda a suíte de testes, e falha se qualquer um
|
||
não passar. Equivalente a rodar manualmente os dois comandos abaixo.
|
||
|
||
## Executar o App Localmente (MANDATORY)
|
||
|
||
Sempre que uma alteração for feita no app (MacApp/) durante o período de
|
||
implementação, **compile e rode o programa localmente no computador** para
|
||
validar visualmente a alteração, além de rodar os testes. O comando padrão
|
||
para isso — que fecha a instância anterior, recompila e abre o app para
|
||
conferência — é:
|
||
|
||
```bash
|
||
admin/run_app.command # compila e abre o app localmente (padrão de revisão)
|
||
```
|
||
|
||
Equivalente a `cd code && ./MacApp/build_app.sh --run`, mas desacoplado do
|
||
Terminal. **Toda vez que uma alteração for concluída, rode este arquivo
|
||
automaticamente** para já conseguirmos revisar o que foi feito antes de
|
||
fechar a tarefa.
|
||
|
||
Regra geral: após qualquer alteração, o app deve ser executado localmente
|
||
antes de concluir a tarefa. Se houver erro de compilação, corrija antes de
|
||
seguir.
|
||
|
||
Before committing ANY changes, run both:
|
||
```bash
|
||
cd code && ruff check . --exclude docs/ # Lint — must pass with zero errors
|
||
cd code && pytest tests/ -v # Tests — all must pass
|
||
```
|
||
CI runs both on every push to main. If either fails, the commit gets an X on GitHub. Fix lint errors before committing, not after.
|
||
|
||
## Testing
|
||
|
||
1498 tests across 43 files, all under `code/tests/`. Um teste fora dessa pasta não roda (`testpaths = ["tests"]`) — se você criar um em outro lugar, confirme que a contagem total subiu. Cobertura por área: `test_models.py` (TimeValue/Timecode/Clip/Timeline), `test_writer.py` (insert/marker/trim/delete/split/speed), `test_server.py` (handlers e dispatch), `test_rough_cut.py`, `test_features_v05.py` (connected clips, roles, diff, reformat, silêncio, export), `test_marker_pipeline.py`, `test_refactored_helpers.py`, `test_transcribe.py`, `test_media_intel.py` (pula sem ffmpeg; o CI instala), `test_phrase_review.py` (revisão de frases da etapa 5) e `test_models_api.py` (comandos da ponte). Fixtures: `examples/sample.fcpxml` e XML inline. Os testes criam temporários e limpam depois.
|
||
|
||
## FCPXML Gotchas
|
||
|
||
- FCPXML uses rational time everywhere: `"3600/2400s"` = 1.5 seconds
|
||
- `offset` in clips is the timeline position, `start` is the source media in-point
|
||
- Library clips (`<asset-clip>`) are different from timeline clips (`<clip>`)
|
||
- Markers are children of clips, not siblings
|
||
- The `<spine>` element is the primary storyline — clips go here
|
||
- `.fcpxmld` bundles are DIRECTORIES wrapping `Info.fcpxml` + sidecar data files — sidecars must be copied on save or object-tracking/Cinematic data is destroyed
|
||
- `code/examples/sample.fcpxml` is NOT DTD-conformant (pre-`media-rep` assets, sequence-level chapter markers) — don't use it as a DTD-validity fixture
|