"""Entrada de linha de comando para carregar um plano salvo no SQLite.""" from __future__ import annotations import argparse import json from pathlib import Path import sqlite3 import sys 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.persistencia.conexao import abrir_banco from engine.persistencia.repositorio_de_planos import RepositorioDePlanos def resolver_arquivo_de_origem( conexao: sqlite3.Connection, video_id: str | None, origem_informada: str, ) -> str: """Resolve a mídia física de um plano histórico associado a uma sequência. Preserva a origem quando ela já identifica um clipe do vídeo. Quando o plano guardou o nome editorial da sequência, usa automaticamente o arquivo dos clipes somente se todas as ocorrências apontarem para um único nome-base de mídia. Em timelines com fontes distintas, não escolhe uma delas por suposição. """ if not video_id: return origem_informada linhas = conexao.execute( """SELECT nome, arquivo FROM clipes WHERE video_id = ? AND (trim(coalesce(nome, '')) <> '' OR trim(coalesce(arquivo, '')) <> '')""", (video_id,), ).fetchall() alvo = Path(origem_informada.strip()).name.casefold() for linha in linhas: nome = str(linha["nome"] or "").strip().casefold() nome_base_do_arquivo = Path(str(linha["arquivo"] or "").strip()).name.casefold() if alvo and alvo in {nome, nome_base_do_arquivo}: return origem_informada arquivos_por_nome_base: dict[str, str] = {} for linha in linhas: arquivo = str(linha["arquivo"] or "").strip() if arquivo: arquivos_por_nome_base.setdefault(Path(arquivo).name.casefold(), arquivo) if len(arquivos_por_nome_base) == 1: return next(iter(arquivos_por_nome_base.values())) return origem_informada def carregar( caminho_do_banco: Path, plano_id: int | None = None, video_id: str | None = None, ) -> dict[str, object] | None: """Carrega um plano existente, preferindo o mais recente do vídeo informado.""" conexao = abrir_banco(caminho_do_banco) try: if plano_id is None: if not video_id: raise ValueError("Informe plano_id ou video_id para carregar um plano.") linha = conexao.execute( """SELECT id FROM planos_de_edicao WHERE video_id = ? ORDER BY criado_em DESC, id DESC LIMIT 1""", (video_id,), ).fetchone() if linha is None: return None plano_id = int(linha["id"]) plano = RepositorioDePlanos(conexao).carregar_plano(plano_id) if plano is None: return None arquivo_de_origem = resolver_arquivo_de_origem( conexao, plano.video_id, plano.origem, ) return { "plano_id": plano_id, "source": arquivo_de_origem, "actions": [ { "kind": acao.tipo, "start": acao.inicio, "end": acao.fim, "reason": acao.motivo or "Decisão registrada no plano editorial.", **acao.parametros, } for acao in plano.acoes ], "video_id": plano.video_id, "tipo_de_video": plano.tipo_de_video, "modelo_da_ia": plano.modelo_da_ia, "intencao": plano.intencao, } finally: conexao.close() def principal() -> None: """Executa o carregamento pela linha de comando.""" argumentos = argparse.ArgumentParser() argumentos.add_argument("--banco", type=Path, default=Path(".jhonny/analises.db")) argumentos.add_argument("--plano-id", type=int) argumentos.add_argument("--video-id") opcoes = argumentos.parse_args() try: plano = carregar(opcoes.banco, opcoes.plano_id, opcoes.video_id) if plano is None: raise ValueError("Nenhum plano de edição encontrado.") print(json.dumps({"ok": True, "plano": plano}, ensure_ascii=False)) except (OSError, ValueError) as erro: print(json.dumps({"ok": False, "erro": str(erro)}, ensure_ascii=False)) raise SystemExit(1) from erro if __name__ == "__main__": principal()