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
This commit is contained in:
João Henrique
2026-09-08 16:13:27 -04:00
parent b541f502ba
commit b9bf3b2863
76 changed files with 16147 additions and 322 deletions
+184
View File
@@ -0,0 +1,184 @@
from collections.abc import Callable
from dataclasses import dataclass, field
from ..dominio import Clipe
from ..integracoes.visual.contratos import (AnalisadorDeFrame, AnalisadorDeSequenciaDeQuadros,
DetectorDeIntervalosDeCena, ExtratorDeQuadros)
from ..integracoes.visual.modelos import QuadroDeVideo
from .modelos import Cena, CenaVisual, EvidenciaVisual
@dataclass(frozen=True)
class ResultadoVisualDoClipe:
identificador_do_clipe: str
evidencias: list[EvidenciaVisual] = field(default_factory=list)
cenas: list[Cena] = field(default_factory=list)
cenas_visuais: list[CenaVisual] = field(default_factory=list)
class DetectorDeCenas:
"""Módulo profundo que orquestra frames, analisadores e cenas de um clipe.
A interface não expõe OpenCV, ONNX, MediaPipe, PySceneDetect ou Vision.
Cada adapter pode ser trocado sem alterar chamadores nem resultados do domínio.
"""
def __init__(self, extrator: ExtratorDeQuadros,
analisadores_de_frame: list[AnalisadorDeFrame] | None = None,
detectores_de_intervalo: list[DetectorDeIntervalosDeCena] | None = None,
analisadores_de_sequencia: list[AnalisadorDeSequenciaDeQuadros] | None = None,
ao_progresso: Callable[[int, int], None] | None = None) -> None:
self.extrator = extrator
self.analisadores_de_frame = analisadores_de_frame or []
self.analisadores_de_sequencia = analisadores_de_sequencia or []
self.detectores_de_intervalo = detectores_de_intervalo or []
self.ao_progresso = ao_progresso
def detectar(self, clipe: Clipe) -> ResultadoVisualDoClipe:
quadros = self.extrator.extrair(clipe)
total = len(quadros) * len(self.analisadores_de_frame) + len(self.analisadores_de_sequencia)
progresso = [0]
evidencias = self._analisar_quadros(quadros, progresso, total) + self._analisar_sequencia(quadros, progresso, total)
cenas = self._detectar_intervalos(clipe, quadros)
cenas_visuais = [CenaVisual(
clipe.identificador, cena.inicio, cena.fim,
tuple(item for item in evidencias if item.inicio <= cena.fim and item.fim >= cena.inicio),
cena.referencias,
) for cena in cenas]
return ResultadoVisualDoClipe(clipe.identificador, evidencias, cenas, cenas_visuais)
def _analisar_quadros(self, quadros: list[QuadroDeVideo], progresso: list[int] | None = None,
total: int = 0) -> list[EvidenciaVisual]:
resultado: list[EvidenciaVisual] = []
for quadro in quadros:
for analisador in self.analisadores_de_frame:
resultado.extend(analisador.analisar(quadro))
self._avancar_progresso(progresso, total)
return resultado
def _analisar_sequencia(self, quadros: list[QuadroDeVideo], progresso: list[int] | None = None,
total: int = 0) -> list[EvidenciaVisual]:
resultado: list[EvidenciaVisual] = []
for analisador in self.analisadores_de_sequencia:
resultado.extend(analisador.analisar(quadros))
self._avancar_progresso(progresso, total)
return resultado
def _avancar_progresso(self, progresso: list[int] | None, total: int) -> None:
if progresso is None or total <= 0:
return
progresso[0] += 1
if self.ao_progresso:
self.ao_progresso(progresso[0], total)
def _detectar_intervalos(self, clipe: Clipe, quadros: list[QuadroDeVideo]) -> list[Cena]:
cenas: list[Cena] = []
for detector in self.detectores_de_intervalo:
cenas.extend(detector.detectar(clipe, quadros))
cenas = self._consolidar_cenas(cenas)
if not cenas and quadros:
cenas = [Cena(quadros[0].timestamp, quadros[-1].timestamp)]
return cenas
@staticmethod
def _consolidar_cenas(cenas: list[Cena]) -> list[Cena]:
resultado: list[Cena] = []
for cena in sorted(cenas, key=lambda item: (item.inicio, item.fim)):
if resultado and cena.inicio <= resultado[-1].fim:
anterior = resultado[-1]
resultado[-1] = Cena(anterior.inicio, max(anterior.fim, cena.fim),
anterior.confianca or cena.confianca,
anterior.referencias + cena.referencias)
else:
resultado.append(cena)
return resultado
class AnaliseVisualDaTimeline:
"""Etapa do Scanner que guarda evidências e cenas por clipe no contexto."""
nome = "analise_visual_local"
def __init__(self, detector: DetectorDeCenas, continuar_em_falha: bool = True) -> None:
self.detector = detector
self.continuar_em_falha = continuar_em_falha
def executar(self, contexto):
if contexto.timeline is None:
return contexto
for faixa in contexto.timeline.faixas:
for clipe in faixa.clipes:
if clipe.offline or not clipe.arquivo:
continue
try:
resultado = self.detector.detectar(clipe)
except Exception as exc:
if not self.continuar_em_falha:
raise
contexto.avisos.append(
f"analise_visual_indisponivel: clipe {clipe.identificador}: {exc}"
)
contexto.caracteristicas_visuais[clipe.identificador] = {
"evidencias": [], "cenas": [], "cenas_visuais": [], "disponivel": False,
}
continue
contexto.caracteristicas_visuais[clipe.identificador] = {
"evidencias": resultado.evidencias,
"cenas": resultado.cenas,
"cenas_visuais": resultado.cenas_visuais,
"disponivel": True,
}
contexto.cenas.extend(resultado.cenas)
contexto.cenas_visuais.extend(resultado.cenas_visuais)
contexto.cenas = DetectorDeCenas._consolidar_cenas(contexto.cenas)
return contexto
def criar_detector_visual_local(
diretorio_de_cache: str | None = ".frames",
intervalo_em_segundos: float = 1.0,
) -> DetectorDeCenas:
"""Monta o detector local padrão com uma interface curta para o Scanner."""
from ..integracoes.visual import (
AnalisadorDeComposicaoOpenCV,
AnalisadorDeContinuidadeOpenCV,
AnalisadorDeQualidadeOpenCV,
ExtratorDeQuadrosOpenCV,
)
return DetectorDeCenas(
ExtratorDeQuadrosOpenCV(
intervalo_em_segundos=intervalo_em_segundos,
diretorio_de_cache=diretorio_de_cache,
),
analisadores_de_frame=[
AnalisadorDeQualidadeOpenCV(),
AnalisadorDeComposicaoOpenCV(),
],
analisadores_de_sequencia=[AnalisadorDeContinuidadeOpenCV()],
)
def criar_detector_apple_vision(
diretorio_de_cache: str | None = ".frames",
intervalo_em_segundos: float = 1.0,
configuracao=None,
) -> DetectorDeCenas:
"""Monta a análise local de cena baseada em Vision e arquivos de origem."""
from .configuracao_visual import ConfiguracaoVisualDoScanner
from ..integracoes.visual import (AnalisadorAppleVisionNativo,
AnalisadorSequenciaAppleVisionNativo,
ExtratorDeQuadrosFFmpeg)
perfil = configuracao or ConfiguracaoVisualDoScanner(intervalo_em_segundos=intervalo_em_segundos)
return DetectorDeCenas(
ExtratorDeQuadrosFFmpeg(intervalo_em_segundos=perfil.intervalo_em_segundos,
diretorio_de_cache=diretorio_de_cache or ".frames"),
analisadores_de_frame=[AnalisadorAppleVisionNativo(
recursos=tuple(sorted(perfil.recursos_vision)),
interpretar_com_apple_intelligence=perfil.interpretar_cena,
)],
analisadores_de_sequencia=[AnalisadorSequenciaAppleVisionNativo()]
if perfil.analisar_continuidade else [],
)