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
87 lines
4.0 KiB
Python
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()
|