Files
jhonny-editor/code/engine/analisar_trilhas.py
T
João Henrique 2c9bdf2ff0 feat: criado monitoramento contínuo de trilhas com varredura recur
- criado monitoramento contínuo de trilhas com varredura recursiva e baixa prioridade
- gerado e validado plano editorial revisável do depoimento da Erika sem aplicação na timeline
- adicionada leitura e comparação estrutural da timeline após aplicar planos de edição
- limitadas as classificações de trilhas às duas maiores probabilidades de cada categoria
- limpo o catálogo musical para reiniciar as análises sem remover dados de vídeo

Resumo:
- 34 arquivos alterados
- 6 novos
- 28 modificados
- 0 removidos

 28 files changed, 833 insertions(+), 75 deletions(-)

Arquivos:
  - .jhonny/analises.db
  - code/cep-plugin/index.html
  - code/cep-plugin/main.js
  - code/engine/analisar_trilhas.py
  - code/engine/aplicar_plano_de_edicao.py
  - code/engine/consultar_detalhes_de_trilha.py
  - code/engine/editor/__init__.py
  - code/engine/editor/aplicador_de_plano_de_edicao.py
  - code/engine/editor/backup_de_sequencia.py
  - code/engine/editor/escrita/escrita_no_editor.py
  - code/engine/editor/modelos.py
  - code/engine/editor/validacao_semantica.py
  - code/engine/integracoes/audio/classificador_de_musica_essentia.py
  - code/engine/integracoes/audio/modelos_de_analise_musical.py
  - code/engine/integracoes/audio/provider_de_analise_musical_essentia.py
  - code/engine/persistencia/esquema.py
  - code/engine/persistencia/repositorio_de_trilhas.py
  - code/engine/testes/duplos_de_premiere.py
  - code/engine/testes/test_aplicador_de_plano_de_edicao.py
  - code/engine/testes/test_aplicar_plano_de_edicao.py
  - code/engine/testes/test_escrita_no_editor.py
  - code/engine/testes/test_leitor_de_plano_de_edicao.py
  - code/engine/testes/test_validador_semantico_de_plano.py
  - code/plugins/premiere-pro/skills/editar-por-voz/SKILL.md
  - code/plugins/premiere-pro/skills/editar-por-voz/criterios/08-formato-de-saida.md
  - code/plugins/premiere-pro/skills/editar-por-voz/criterios/10-revisao-humana.md
  - code/src/tools/timeline.ts
  - code/tests/tools/tool-modules.test.ts
  - .jhonny/planos/
  - code/.jhonny/analises.db-shm
  - code/.jhonny/analises.db-wal
  - code/engine/editor/verificador_da_aplicacao.py
  - code/engine/testes/test_verificador_da_aplicacao.py
  - code/plugins/premiere-pro/skills/editar-por-voz/criterios/11-gancho.md
2026-09-10 15:09:27 -04:00

107 lines
4.9 KiB
Python

"""Executa a análise em lote de uma pasta de trilhas e emite progresso JSON."""
from __future__ import annotations
import argparse
import json
from pathlib import Path
import sys
import time
CAMINHO_DO_CODIGO = Path(__file__).resolve().parent.parent
if str(CAMINHO_DO_CODIGO) not in sys.path:
sys.path.insert(0, str(CAMINHO_DO_CODIGO))
from engine.integracoes.audio import AnalisadorDeMusicaInstrumentalEssentia, ClassificadorDeMusicaEssentia
from engine.persistencia.conexao import abrir_banco
from engine.persistencia.repositorio_de_trilhas import RepositorioDeTrilhasSQLite
EXTENSOES_DE_AUDIO = {".wav", ".mp3", ".flac", ".m4a", ".aiff", ".ogg"}
def emitir(evento: str, **dados: object) -> None:
"""Emite uma linha de progresso consumível pelo painel CEP."""
print("@@PROGRESS " + json.dumps({"evento": evento, **dados}, ensure_ascii=False), flush=True)
def executar(pasta: Path, banco: Path, diretorio_dos_modelos: Path | None, apenas_listar: bool = False) -> int:
"""Atualiza o catálogo e, opcionalmente, analisa as trilhas pendentes."""
arquivos = tuple(sorted(
arquivo for arquivo in pasta.rglob("*")
if arquivo.is_file() and arquivo.suffix.lower() in EXTENSOES_DE_AUDIO
))
conexao = abrir_banco(banco)
try:
repositorio = RepositorioDeTrilhasSQLite(conexao)
trilhas = repositorio.registrar_pasta_e_arquivos(pasta, arquivos)
repositorio.reabrir_analises_sem_etiquetas()
trilhas = repositorio.registrar_pasta_e_arquivos(pasta, arquivos)
if apenas_listar:
emitir("catalogo_atualizado", trilhas=[
{"nome": trilha.nome_do_arquivo, "caminho": str(trilha.caminho), "status": trilha.status}
for trilha in trilhas
])
return 0
pendentes = tuple(trilha for trilha in trilhas if trilha.status != "concluida")
emitir(
"lote_iniciado",
total=len(pendentes),
pasta=str(pasta),
nomes=[trilha.nome_do_arquivo for trilha in pendentes],
trilhas=[{"nome": trilha.nome_do_arquivo, "caminho": str(trilha.caminho), "status": trilha.status} for trilha in trilhas],
)
if not pendentes:
emitir("lote_concluido", total=0, percentual=100)
return 0
classificador = (
ClassificadorDeMusicaEssentia(diretorio_dos_modelos)
if diretorio_dos_modelos and diretorio_dos_modelos.is_dir()
else None
)
analisador = AnalisadorDeMusicaInstrumentalEssentia(classificador=classificador)
for ordem, trilha in enumerate(pendentes, start=1):
repositorio.iniciar_analise(trilha.identificador)
emitir("trilha_iniciada", nome=trilha.nome_do_arquivo, caminho=str(trilha.caminho), indice=ordem, total=len(trilhas), percentual=(ordem - 1) * 100 / max(1, len(trilhas)))
try:
resultado = analisador.analisar(trilha.caminho)
repositorio.registrar_analise(trilha, resultado)
emitir("trilha_concluida", nome=trilha.nome_do_arquivo, caminho=str(trilha.caminho), indice=ordem, total=len(trilhas), percentual=ordem * 100 / max(1, len(trilhas)))
except (OSError, RuntimeError, ValueError) as erro:
repositorio.registrar_erro(trilha.identificador, str(erro))
emitir("trilha_com_erro", nome=trilha.nome_do_arquivo, caminho=str(trilha.caminho), indice=ordem, total=len(trilhas), erro=str(erro), percentual=ordem * 100 / max(1, len(trilhas)))
emitir("lote_concluido", total=len(trilhas), percentual=100)
return 0
finally:
conexao.close()
def executar_continuamente(pasta: Path, banco: Path, diretorio_dos_modelos: Path | None, intervalo: float) -> int:
"""Mantém a pasta monitorada e processa novas trilhas em baixa frequência."""
while True:
executar(pasta, banco, diretorio_dos_modelos)
emitir("worker_aguardando", intervalo=intervalo)
time.sleep(intervalo)
def principal() -> None:
"""Lê argumentos e executa o lote de análise."""
argumentos = argparse.ArgumentParser()
argumentos.add_argument("pasta", type=Path)
argumentos.add_argument("--banco", type=Path, default=Path(".jhonny/analises.db"))
argumentos.add_argument("--modelos", type=Path, default=None)
argumentos.add_argument("--listar", action="store_true")
argumentos.add_argument("--continuo", action="store_true")
argumentos.add_argument("--intervalo", type=float, default=20.0)
opcoes = argumentos.parse_args()
try:
if opcoes.continuo:
raise SystemExit(executar_continuamente(opcoes.pasta, opcoes.banco, opcoes.modelos, opcoes.intervalo))
raise SystemExit(executar(opcoes.pasta, opcoes.banco, opcoes.modelos, opcoes.listar))
except (OSError, RuntimeError, ValueError) as erro:
emitir("lote_com_erro", erro=str(erro))
raise SystemExit(1) from erro
if __name__ == "__main__":
principal()