refactor: writer.py vira pacote, um módulo por assunto

O writer tinha 4.199 linhas, das quais 3.300 numa única classe com dezoito
assuntos dentro. Achar o trecho de zoom exigia rolar por marcadores,
velocidade e legendas.

Agora é o pacote fcpxml/writer/, com um arquivo por assunto e o
FCPXMLModifier montado por composição de mixins. Mixins, e não objetos
separados, porque todas essas operações mexem no mesmo documento e nos
mesmos índices — separá-las em objetos independentes transformaria toda
chamada interna em travessia de fronteira sem nada em troca. A divisão que
importa aqui é de leitura, não de estado.

Nenhuma mudança de comportamento e nenhuma alteração nos ~50 pontos que
importam do writer: o __init__ re-exporta tudo, inclusive os nomes com
underscore que a suíte já usava.

    core      723   carga, índices, navegação na spine, save
    titles    600   títulos e legendas dinâmicas
    cut       333   dividir, cortar faixas, apagar
    speed     297   velocidade e zoom
    (+ 20 módulos menores)

Único ajuste de chamada: quatro testes faziam patch em
fcpxml.writer.subprocess, que agora mora em writer.document (ver
Engine/docs/05_EXPERIENCIAS.md #23).

Lint zerado, 1441 testes passando.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
João Henrique
2026-08-19 21:38:49 -04:00
co-authored by Claude Opus 5
parent 1bebee4359
commit 4f5cf94443
28 changed files with 4719 additions and 4203 deletions
+4 -4
View File
@@ -419,7 +419,7 @@ class TestStillImageConversion:
result = _ensure_video_asset('/path/to/clip.mxf')
assert result == '/path/to/clip.mxf'
@patch('fcpxml.writer.subprocess.run')
@patch('fcpxml.writer.document.subprocess.run')
def test_png_triggers_conversion(self, mock_run):
mock_run.return_value = MagicMock(returncode=0)
with tempfile.NamedTemporaryFile(suffix='.png', delete=False) as f:
@@ -436,7 +436,7 @@ class TestStillImageConversion:
if os.path.exists(mov_path):
os.unlink(mov_path)
@patch('fcpxml.writer.subprocess.run', side_effect=FileNotFoundError)
@patch('fcpxml.writer.document.subprocess.run', side_effect=FileNotFoundError)
def test_missing_ffmpeg_raises(self, mock_run):
with tempfile.NamedTemporaryFile(suffix='.jpg', delete=False) as f:
jpg_path = f.name
@@ -446,7 +446,7 @@ class TestStillImageConversion:
finally:
os.unlink(jpg_path)
@patch('fcpxml.writer.subprocess.run',
@patch('fcpxml.writer.document.subprocess.run',
side_effect=subprocess.TimeoutExpired(cmd='ffmpeg', timeout=120))
def test_ffmpeg_timeout_raises_runtime_error(self, mock_run):
"""Timed-out ffmpeg must raise RuntimeError, not propagate raw TimeoutExpired."""
@@ -458,7 +458,7 @@ class TestStillImageConversion:
finally:
os.unlink(png_path)
@patch('fcpxml.writer.subprocess.run',
@patch('fcpxml.writer.document.subprocess.run',
side_effect=subprocess.CalledProcessError(
1, 'ffmpeg', stderr=b'Invalid codec'))
def test_ffmpeg_failure_raises_runtime_error(self, mock_run):