Files
jhonny-editor/code/engine/scanner/relatorio_visual.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

80 lines
3.5 KiB
Python
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.
"""Exportação estruturada de uma leitura visual de clipe."""
from collections import defaultdict
from dataclasses import asdict
from datetime import datetime, timezone
import json
from pathlib import Path
from ..dominio import Clipe
from .visual import ResultadoVisualDoClipe
VERSAO_DO_RELATORIO_VISUAL = "1.0"
def gerar_relatorio_visual(clipe: Clipe, resultado: ResultadoVisualDoClipe,
destino: str | Path, intervalo_de_amostragem: float) -> Path:
"""Grava um JSON autocontido, com fatos por frame e fatos temporais separados."""
if intervalo_de_amostragem <= 0:
raise ValueError("intervalo_de_amostragem deve ser positivo.")
caminho = Path(destino)
caminho.parent.mkdir(parents=True, exist_ok=True)
caminho.write_text(json.dumps(_dados_do_relatorio(clipe, resultado, intervalo_de_amostragem),
ensure_ascii=False, indent=2), encoding="utf-8")
return caminho
def gerar_resumo_visual_markdown(clipe: Clipe, resultado: ResultadoVisualDoClipe,
destino: str | Path, intervalo_de_amostragem: float) -> Path:
"""Grava uma visão humana do mesmo resultado exportado em JSON."""
caminho = Path(destino)
caminho.parent.mkdir(parents=True, exist_ok=True)
dados = _dados_do_relatorio(clipe, resultado, intervalo_de_amostragem)
linhas = [f"# Relatório visual — {clipe.nome}", "",
f"- Clipe: `{clipe.identificador}`",
f"- Origem: `{clipe.arquivo}`",
f"- Timeline: {clipe.intervalo_na_timeline.inicio:.3f}s–{clipe.intervalo_na_timeline.fim:.3f}s",
f"- Amostragem: a cada {intervalo_de_amostragem:g} segundo(s)", "",
"## Observações por frame", ""]
for frame in dados["observacoes_por_frame"]:
tipos = ", ".join(item["tipo"] for item in frame["evidencias"])
linhas.extend([f"### Origem {frame['timestamp_na_origem']:.3f}s", "", tipos or "Sem evidências.", ""])
linhas.extend(["## Continuidade", ""])
for evidencia in dados["evidencias_temporais"]:
linhas.append(f"- {evidencia['tipo']}: {evidencia['inicio']:.3f}s–{evidencia['fim']:.3f}s — "
f"`{json.dumps(evidencia['valor'], ensure_ascii=False)}`")
caminho.write_text("\n".join(linhas) + "\n", encoding="utf-8")
return caminho
def _dados_do_relatorio(clipe: Clipe, resultado: ResultadoVisualDoClipe,
intervalo_de_amostragem: float) -> dict:
por_frame = defaultdict(list)
temporais = []
for evidencia in resultado.evidencias:
item = asdict(evidencia)
if evidencia.inicio == evidencia.fim:
por_frame[evidencia.inicio].append(item)
else:
temporais.append(item)
origem = clipe.intervalo_na_origem
return {
"versao": VERSAO_DO_RELATORIO_VISUAL,
"gerado_em": datetime.now(timezone.utc).isoformat(),
"clipe": {
"identificador": clipe.identificador,
"nome": clipe.nome,
"arquivo_original": clipe.arquivo,
"intervalo_na_timeline": asdict(clipe.intervalo_na_timeline),
"intervalo_na_origem": asdict(origem) if origem else None,
},
"amostragem": {"intervalo_em_segundos": intervalo_de_amostragem},
"observacoes_por_frame": [
{"timestamp_na_origem": timestamp, "evidencias": evidencias}
for timestamp, evidencias in sorted(por_frame.items())
],
"evidencias_temporais": temporais,
"cenas": [asdict(cena) for cena in resultado.cenas_visuais],
}