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: - 32 arquivos alterados - 16 novos - 15 modificados - 0 removidos 16 files changed, 638 insertions(+), 61 deletions(-) Arquivos: - code/engine/skills/boas-praticas-oo.md -> .agents/skills/boas-praticas-oo/SKILL.md - AGENTS.md - code/cep-plugin/index.html - code/cep-plugin/main.js - code/cep-plugin/styles.css - code/engine/integracoes/visual/apple_vision.py - code/engine/integracoes/visual/apple_vision_runner.swift - code/engine/integracoes/visual/extrator_ffmpeg.py - code/engine/scanner/__init__.py - code/engine/scanner/configuracao_visual.py - code/engine/scanner/coordenacao/__init__.py - code/engine/scanner/retakes/adaptador_de_falas.py - code/engine/scanner/retakes/agrupador_de_takes.py - code/engine/scanner/retakes/detector_de_reinicios.py - code/engine/scanner/retakes/modelos_de_retakes.py - code/engine/scanner/visual.py - .jhonny/ - .retakes/ - CODING_STANDARDS.md - code/engine/executar_retakes.py - code/engine/persistencia/ - code/engine/scanner/retakes/__init__.py - code/engine/scanner/retakes/carregador_de_artefatos.py - code/engine/scanner/retakes/classificador_de_retakes.py - code/engine/scanner/retakes/coordenador_de_retakes.py - code/engine/scanner/retakes/deteccao_de_retakes.py - code/engine/scanner/retakes/gerador_de_evidencias.py - code/engine/scanner/retakes/repositorio_de_retakes.py - code/engine/testes/test_consultas_de_persistencia.py - code/engine/testes/test_deteccao_de_retakes.py - code/engine/testes/test_persistencia_sqlite.py - code/relatorios/audio/arquivos/92f97877259a86a8095b1eecafdefe357212a12247d04b78df60e0c0ae38b2ea/
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
"""Persistência SQLite da estrutura da timeline (faixas e clipes).
|
||||
|
||||
É a "coluna vertebral" que liga os `clipe_id`/`video_id` usados em
|
||||
transcrição, evidências visuais e retakes de volta à timeline real —
|
||||
para que um agente de edição possa navegar tudo por consultas SQL, sem
|
||||
precisar reabrir os arquivos de origem ou o projeto de edição.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import sqlite3
|
||||
from pathlib import Path
|
||||
|
||||
from ..dominio.entidades.modelos import Clipe, Faixa, IntervaloDeTempo, Timeline
|
||||
from .conexao import abrir_banco
|
||||
|
||||
|
||||
class RepositorioDeTimelineSQLite:
|
||||
"""Grava e recarrega uma `Timeline` (faixas e clipes) por vídeo."""
|
||||
|
||||
def __init__(self, banco: str | Path | sqlite3.Connection = ".jhonny/analises.db") -> None:
|
||||
self.conexao = banco if isinstance(banco, sqlite3.Connection) else abrir_banco(banco)
|
||||
|
||||
def registrar_timeline(self, timeline: Timeline) -> None:
|
||||
"""Grava a timeline de forma idempotente (substitui faixas/clipes anteriores)."""
|
||||
video_id = timeline.identificador
|
||||
with self.conexao:
|
||||
self.conexao.execute(
|
||||
"""INSERT INTO videos (id, nome, duracao, taxa_de_quadros, largura, altura)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
ON CONFLICT (id) DO UPDATE SET
|
||||
nome = excluded.nome, duracao = excluded.duracao,
|
||||
taxa_de_quadros = excluded.taxa_de_quadros,
|
||||
largura = excluded.largura, altura = excluded.altura""",
|
||||
(video_id, timeline.nome, timeline.duracao, timeline.taxa_de_quadros,
|
||||
timeline.largura, timeline.altura),
|
||||
)
|
||||
# CASCADE em clipes/faixas cuida da limpeza ao apagar as faixas do vídeo.
|
||||
self.conexao.execute("DELETE FROM faixas WHERE video_id = ?", (video_id,))
|
||||
for faixa in timeline.faixas:
|
||||
self.conexao.execute(
|
||||
"INSERT INTO faixas (id, video_id, nome, tipo, indice) VALUES (?, ?, ?, ?, ?)",
|
||||
(faixa.identificador, video_id, faixa.nome, faixa.tipo, faixa.indice),
|
||||
)
|
||||
for clipe in faixa.clipes:
|
||||
origem = clipe.intervalo_na_origem
|
||||
self.conexao.execute(
|
||||
"""INSERT INTO clipes
|
||||
(id, video_id, faixa_id, nome, inicio_na_timeline, fim_na_timeline,
|
||||
inicio_na_origem, fim_na_origem, arquivo, offline, metadados)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
|
||||
(clipe.identificador, video_id, faixa.identificador, clipe.nome,
|
||||
clipe.intervalo_na_timeline.inicio, clipe.intervalo_na_timeline.fim,
|
||||
origem.inicio if origem else None, origem.fim if origem else None,
|
||||
clipe.arquivo, int(clipe.offline),
|
||||
json.dumps(clipe.metadados, ensure_ascii=False)),
|
||||
)
|
||||
|
||||
def carregar_timeline(self, video_id: str) -> Timeline | None:
|
||||
"""Reconstrói a `Timeline` (faixas e clipes) a partir do banco."""
|
||||
video = self.conexao.execute(
|
||||
"SELECT * FROM videos WHERE id = ?", (video_id,),
|
||||
).fetchone()
|
||||
if video is None:
|
||||
return None
|
||||
|
||||
faixas: list[Faixa] = []
|
||||
for linha_faixa in self.conexao.execute(
|
||||
"SELECT * FROM faixas WHERE video_id = ? ORDER BY indice", (video_id,),
|
||||
).fetchall():
|
||||
clipes = [
|
||||
Clipe(
|
||||
identificador=item["id"],
|
||||
nome=item["nome"],
|
||||
intervalo_na_timeline=IntervaloDeTempo(
|
||||
item["inicio_na_timeline"], item["fim_na_timeline"]),
|
||||
intervalo_na_origem=(
|
||||
IntervaloDeTempo(item["inicio_na_origem"], item["fim_na_origem"])
|
||||
if item["inicio_na_origem"] is not None else None
|
||||
),
|
||||
arquivo=item["arquivo"],
|
||||
identificador_da_faixa=item["faixa_id"],
|
||||
offline=bool(item["offline"]),
|
||||
metadados=json.loads(item["metadados"] or "{}"),
|
||||
)
|
||||
for item in self.conexao.execute(
|
||||
"SELECT * FROM clipes WHERE video_id = ? AND faixa_id = ? "
|
||||
"ORDER BY inicio_na_timeline",
|
||||
(video_id, linha_faixa["id"]),
|
||||
).fetchall()
|
||||
]
|
||||
faixas.append(Faixa(
|
||||
identificador=linha_faixa["id"], nome=linha_faixa["nome"],
|
||||
tipo=linha_faixa["tipo"], indice=linha_faixa["indice"], clipes=clipes,
|
||||
))
|
||||
|
||||
return Timeline(
|
||||
identificador=video["id"], nome=video["nome"], duracao=video["duracao"],
|
||||
taxa_de_quadros=video["taxa_de_quadros"], largura=video["largura"],
|
||||
altura=video["altura"], faixas=faixas,
|
||||
)
|
||||
Reference in New Issue
Block a user