Files
jhonny-editor/code/engine/testes/test_arquivos_e_metadados.py
T
João Henrique b9bf3b2863 feat: criado repositório jhonny-editor no Gitea
criado repositório jhonny-editor no Gitea
adicionado script admin/deploy.command com commit automático
atualizado admin/DEV-NOTES.md com template limpo

Resumo:
- 48 arquivos alterados
- 26 novos
- 19 modificados
- 3 removidos

 22 files changed, 658 insertions(+), 568 deletions(-)

Arquivos:
  - AGENTS.md
  - admin/DEV-NOTES.md
  - admin/OpenCut.command
  - admin/commit.command
  - admin/deploy.command
  - admin/update.command
  - code/cep-plugin/index.html
  - code/cep-plugin/main.js
  - code/cep-plugin/styles.css
  - code/engine/ARQUITETURA.md
  - code/engine/arquitetura/README.md
  - code/engine/gerar_relatorio_timeline.py
  - code/engine/integracoes/apple_speech/apple_speech_transcriber.swift
  - code/engine/integracoes/apple_speech/provider_de_transcricao_apple.py
  - code/engine/integracoes/midia/__init__.py
  - code/engine/integracoes/whisper/provider_de_transcricao_local.py
  - code/engine/scanner/__init__.py
  - code/engine/scanner/coordenacao/__init__.py
  - code/engine/scanner/descoberta/__init__.py
  - code/engine/scanner/modelos.py
  - code/engine/scanner/transcricao_da_timeline.py
  - code/src/tools/discovery.ts
  - :memory:.ses
  - admin/Jhonny.command
  - admin/inativos/OpenCut.command
  - admin/inativos/update.command
  - code/docs/glossario-analise-emocional.md
  - code/docs/levantamento-apple-vision-e-apple-intelligence.md
  - code/docs/levantamento-ferramentas-analise-visual-local.md
  - code/engine/arquitetura/biblioteca-inteligente-de-videos.md
  - code/engine/executar_scanner.py
  - code/engine/integracoes/huggingface/
  - code/engine/integracoes/midia/extracao_de_metadados.py
  - code/engine/integracoes/visual/
  - code/engine/requirements-visual.txt
  - code/engine/scanner/configuracao_visual.py
  - code/engine/scanner/descoberta/descoberta_de_arquivos.py
  - code/engine/scanner/metadados.py
  - code/engine/scanner/relatorio_visual.py
  - code/engine/scanner/retakes/
  - code/engine/scanner/visual.py
  - code/engine/testes/test_analise_visual_local.py
  - code/engine/testes/test_arquivos_e_metadados.py
  - code/engine/testes/test_provider_de_transcricao_apple.py
  - code/relatorios/analise-brools/
  - code/relatorios/analise-visual/
  - code/relatorios/audio/arquivos/
  - code/relatorios/transcricao-timeline.md
2026-09-08 16:13:27 -04:00

87 lines
4.0 KiB
Python

import json
import tempfile
import unittest
from pathlib import Path
from unittest.mock import patch
from engine.dominio import Clipe, Faixa, IntervaloDeTempo, Timeline
from engine.scanner.coordenacao import ContextoDeAnalise
from engine.scanner.descoberta import ArquivoDescoberto, DescobertaDeArquivos
from engine.scanner.metadados import ExtracaoDeMetadados
from engine.integracoes.midia.extracao_de_metadados import ExtracaoDeMetadados as Adapter
class TesteArquivosEMetadados(unittest.TestCase):
def _contexto(self, *arquivos: str | None) -> ContextoDeAnalise:
clipes = [Clipe(str(i), f"clipe-{i}", IntervaloDeTempo(0, 1), arquivo=arquivo)
for i, arquivo in enumerate(arquivos)]
timeline = Timeline("timeline", "Principal", faixas=[Faixa("v1", "V1", "video", 0, clipes)])
return ContextoDeAnalise(timeline=timeline)
def test_descoberta_normaliza_caminho_e_marca_offline(self):
with tempfile.TemporaryDirectory() as pasta:
arquivo = Path(pasta) / "camera.mp4"
arquivo.touch()
contexto = self._contexto(str(arquivo), str(Path(pasta) / "ausente.mp4"), None)
DescobertaDeArquivos().executar(contexto)
self.assertEqual(contexto.timeline.faixas[0].clipes[0].arquivo, str(arquivo.resolve()))
self.assertFalse(contexto.timeline.faixas[0].clipes[0].offline)
self.assertTrue(contexto.timeline.faixas[0].clipes[1].offline)
self.assertTrue(contexto.timeline.faixas[0].clipes[2].offline)
self.assertEqual(len(contexto.erros), 2)
def test_descoberta_identifica_arquivo_duplicado(self):
with tempfile.TemporaryDirectory() as pasta:
arquivo = Path(pasta) / "camera.mov"
arquivo.touch()
contexto = self._contexto(str(arquivo), str(arquivo))
DescobertaDeArquivos().executar(contexto)
self.assertEqual(len(contexto.avisos), 1)
def test_extracao_converte_ffprobe_e_usa_cache(self):
dados = {"format": {"format_name": "mov,mp4,m4a", "duration": "12.5", "size": "900"},
"streams": [{"codec_type": "video", "codec_name": "h264", "width": 1920,
"height": 1080, "avg_frame_rate": "30000/1001"},
{"codec_type": "audio", "codec_name": "aac", "channels": 2,
"sample_rate": "48000"}]}
with tempfile.TemporaryDirectory() as pasta:
arquivo = Path(pasta) / "camera.mp4"
arquivo.touch()
adapter = Adapter(ffprobe="ffprobe-simulado")
processo = type("Processo", (), {"stdout": json.dumps(dados)})()
with patch("engine.integracoes.midia.extracao_de_metadados.subprocess.run", return_value=processo) as run:
primeiro = adapter.extrair(arquivo)
segundo = adapter.extrair(arquivo)
self.assertEqual(primeiro.codec_de_video, "h264")
self.assertEqual(primeiro.codec_de_audio, "aac")
self.assertEqual((primeiro.largura, primeiro.altura), (1920, 1080))
self.assertAlmostEqual(primeiro.taxa_de_quadros, 29.97002997, places=5)
self.assertEqual(primeiro.duracao, 12.5)
self.assertIs(primeiro, segundo)
run.assert_called_once()
def test_etapa_de_metadados_ignora_arquivos_offline(self):
class ExtratorSimulado:
def extrair(self, arquivo):
return "metadados"
with tempfile.TemporaryDirectory() as pasta:
arquivo = Path(pasta) / "camera.mp4"
arquivo.touch()
contexto = self._contexto(str(arquivo), str(Path(pasta) / "ausente.mp4"))
DescobertaDeArquivos().executar(contexto)
ExtracaoDeMetadados(ExtratorSimulado()).executar(contexto)
self.assertEqual(contexto.metadados_dos_arquivos["0"], "metadados")
self.assertNotIn("1", contexto.metadados_dos_arquivos)
if __name__ == "__main__":
unittest.main()