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:
@@ -0,0 +1,154 @@
|
||||
"""Modelos de dados do submódulo de detecção de retakes.
|
||||
|
||||
Mantemos os mesmos princípios de ``scanner/modelos.py``: dataclasses
|
||||
imutáveis (``frozen=True``) com validação em ``__post_init__`` e sem
|
||||
dependência de implementações concretas de providers.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import uuid
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from ..modelos import PalavraDeTranscricao
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Fala:
|
||||
"""Uma fala normalizada, já posicionada na timeline do vídeo.
|
||||
|
||||
Corresponde a um ``SegmentoDeTranscricao`` convertido pelo adaptador,
|
||||
mas carrega o contexto necessário para a análise temporal e de
|
||||
agrupamento: vídeo, faixa de áudio, número de sequência e o vínculo
|
||||
com o segmento/clipe de origem.
|
||||
"""
|
||||
|
||||
id: str
|
||||
video_id: str
|
||||
faixa_id: str
|
||||
segmento_id: str
|
||||
ordem: int
|
||||
inicio: float
|
||||
fim: float
|
||||
texto: str
|
||||
texto_normalizado: str = ""
|
||||
palavras: tuple[PalavraDeTranscricao, ...] = ()
|
||||
falante: str | None = None
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
if self.inicio < 0 or self.fim < self.inicio:
|
||||
raise ValueError(f"Intervalo da fala {self.id} inválido.")
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SinalDeReinicio:
|
||||
"""Indício de que uma fala recomeçou uma ideia já iniciada."""
|
||||
|
||||
fala_id: str
|
||||
tipo: str
|
||||
intensidade: float
|
||||
evidencias: list[str] = field(default_factory=list)
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
if not 0.0 <= self.intensidade <= 1.0:
|
||||
raise ValueError(f"Intensidade do reinício {self.fala_id} fora de [0, 1].")
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ResultadoDaComparacao:
|
||||
"""Resultado da comparação de duas falas, textual e por sequência."""
|
||||
|
||||
fala_a_id: str
|
||||
fala_b_id: str
|
||||
similaridade_jaccard: float = 0.0
|
||||
similaridade_de_sequencia: float = 0.0
|
||||
similaridade_do_inicio: float = 0.0
|
||||
similaridade_do_final: float = 0.0
|
||||
similaridade_final: float = 0.0
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ResultadoDoIntervalo:
|
||||
"""Relação temporal entre duas falas."""
|
||||
|
||||
intervalo_em_segundos: float
|
||||
mesmo_segmento: bool = False
|
||||
proximidade_temporal: float = 0.0
|
||||
indicio_de_nova_tentativa: bool = False
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class EvidenciaDeRetake:
|
||||
"""Uma evidência legível que sustenta a classificação de um grupo."""
|
||||
|
||||
tipo: str
|
||||
descricao: str
|
||||
valor: float | None = None
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class TomadaDeRetake:
|
||||
"""Uma tomada (fala) pertencente a um grupo de retakes."""
|
||||
|
||||
ordem: int
|
||||
fala_id: str
|
||||
segmento_id: str
|
||||
inicio: float
|
||||
fim: float
|
||||
texto: str
|
||||
similaridade_com_anterior: float = 0.0
|
||||
confianca: float = 0.0
|
||||
|
||||
|
||||
def gerar_id_do_grupo() -> str:
|
||||
"""Gera um id curto e monótono para um grupo de retakes."""
|
||||
return f"retake_{uuid.uuid4().hex[:6]}"
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ParAgrupado:
|
||||
"""Um par de falas que o agrupador juntou como candidato a retake."""
|
||||
|
||||
fala_a: Fala
|
||||
fala_b: Fala
|
||||
comparacao: ResultadoDaComparacao
|
||||
intervalo: ResultadoDoIntervalo
|
||||
similaridade_semantica: float = 0.0
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class AgrupamentoDeFalas:
|
||||
"""Um grupo candidato formado pelo agrupador, antes da classificação."""
|
||||
|
||||
fala_ids: tuple[str, ...]
|
||||
pares: tuple[ParAgrupado, ...]
|
||||
sinais_de_reinicio: tuple[SinalDeReinicio, ...] = ()
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class GrupoDeRetake:
|
||||
"""Agrupa as tomadas que representam tentativas da mesma fala."""
|
||||
|
||||
id: str
|
||||
video_id: str
|
||||
faixa_id: str
|
||||
tipo: str
|
||||
confianca: float
|
||||
tomadas: list[TomadaDeRetake] = field(default_factory=list)
|
||||
evidencias: list[EvidenciaDeRetake] = field(default_factory=list)
|
||||
criado_em: str = field(default_factory=lambda: datetime.now(timezone.utc).isoformat())
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
if not 0.0 <= self.confianca <= 1.0:
|
||||
raise ValueError(f"Confiança do grupo {self.id} fora de [0, 1].")
|
||||
|
||||
@property
|
||||
def tomada_principal_id(self) -> str | None:
|
||||
if not self.tomadas:
|
||||
return None
|
||||
return max(self.tomadas, key=lambda item: item.confianca).fala_id
|
||||
|
||||
@property
|
||||
def fala_ids(self) -> list[str]:
|
||||
return [tomada.fala_id for tomada in self.tomadas]
|
||||
Reference in New Issue
Block a user