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:
@@ -28,6 +28,9 @@
|
||||
<button class="tab-button" id="tabBtnScanner" role="tab" aria-selected="false" aria-controls="tabScanner" onclick="switchTab('scanner')" type="button">
|
||||
<span class="tab-icon" aria-hidden="true">◉</span>Scanner
|
||||
</button>
|
||||
<button class="tab-button" id="tabBtnRetakes" role="tab" aria-selected="false" aria-controls="tabRetakes" onclick="switchTab('retakes')" type="button">
|
||||
<span class="tab-icon" aria-hidden="true">↻</span>Retakes
|
||||
</button>
|
||||
<button class="tab-button" id="tabBtnBridge" role="tab" aria-selected="false" aria-controls="tabBridge" onclick="switchTab('bridge')" type="button">
|
||||
<span class="tab-icon" aria-hidden="true">⇄</span>Conexão
|
||||
</button>
|
||||
@@ -226,9 +229,19 @@
|
||||
<p class="tab-intro">Escolha o que será lido no arquivo original. O Scanner usa os tempos da timeline, mas não exporta frames pelo Premiere.</p>
|
||||
<section class="card">
|
||||
<div class="card-head"><h2>Amostragem e faixas</h2></div>
|
||||
<label class="field-label" for="scannerInterval">Frames de vídeo</label>
|
||||
<select id="scannerInterval" class="select-field"><option value="0.2">Precisa — 5 frames por segundo</option><option value="1" selected>Equilibrada — 1 frame por segundo</option><option value="5">Econômica — 1 frame a cada 5 segundos</option></select>
|
||||
<p class="field-help">Use a amostragem precisa somente no trecho que será reenquadrado.</p>
|
||||
<label class="field-label" for="scannerInterval">Segundos por quadro</label>
|
||||
<div class="range-field-row">
|
||||
<input id="scannerInterval" type="range" min="0.2" max="10" step="0.1" value="1" oninput="scannerUpdateIntervalLabel()">
|
||||
<span class="range-field-value" id="scannerIntervalValue">1 s/quadro</span>
|
||||
</div>
|
||||
<p class="field-help">Quanto menor, mais frames e mais detalhado (e mais lento). Valores maiores aceleram a análise, principalmente em clipes curtos.</p>
|
||||
<label class="field-label" for="scannerResolution">Resolução da análise</label>
|
||||
<select id="scannerResolution" class="select-field">
|
||||
<option value="0">Original — sem redimensionar</option>
|
||||
<option value="1080" selected>1080p — recomendado</option>
|
||||
<option value="720">720p — mais rápido</option>
|
||||
</select>
|
||||
<p class="field-help">Reduzir a resolução acelera a extração e a leitura Vision sem perder precisão no rosto, pose ou objetos.</p>
|
||||
<button class="button button-ghost" id="btnScannerDetectTracks" onclick="scannerDetectTracks()" type="button">Detectar faixas da sequência</button>
|
||||
<p class="field-help" id="scannerTracksHelp">Abra a sequência desejada e detecte as faixas para selecioná-las.</p>
|
||||
<div class="scanner-track-grid"><div><span class="field-label">Faixas de vídeo</span><div class="scanner-track-list" id="scannerVideoTracks"></div></div><div><span class="field-label">Faixas de áudio</span><div class="scanner-track-list" id="scannerAudioTracks"></div></div></div>
|
||||
@@ -258,6 +271,68 @@
|
||||
<div class="callout callout-info" id="scannerReportCard" hidden><strong>Relatório pronto</strong><p id="scannerReportPath"></p><div class="button-row"><button class="button button-ghost" onclick="scannerOpenReport()" type="button">Abrir JSON</button><button class="button button-ghost" onclick="scannerCopyReport()" type="button">Copiar conteúdo</button></div></div>
|
||||
</section></div>
|
||||
</div>
|
||||
<!-- ============================== RETAKES ============================== -->
|
||||
<div class="tab-panel" id="tabRetakes" role="tabpanel" aria-labelledby="tabBtnRetakes" hidden>
|
||||
<p class="tab-intro">Identifica repetições e tentativas alternativas usando a transcrição do Scanner. O painel nunca corta o vídeo: você revisa e decide.</p>
|
||||
|
||||
<!-- 1. Arquivo ou conjunto analisado -->
|
||||
<section class="card">
|
||||
<div class="card-head"><h2>Arquivo analisado</h2><span class="card-kicker" id="retakesStatusGeral">—</span></div>
|
||||
<div class="field-grid">
|
||||
<div><label class="field-label" for="retakesCaso">Caso do Scanner</label>
|
||||
<select id="retakesCaso" class="select-field" onchange="retakesCarregarCaso()"></select></div>
|
||||
<div><button class="button button-ghost" onclick="retakesListarCasos()" type="button">Atualizar lista</button></div>
|
||||
</div>
|
||||
<p class="field-help" id="retakesInfoCaso">Selecione um caso já processado pelo Scanner.</p>
|
||||
</section>
|
||||
|
||||
<!-- 2/3. Configurações e critérios -->
|
||||
<div class="scanner-options-grid">
|
||||
<section class="card"><div class="card-head"><h2>Critérios</h2></div>
|
||||
<label class="field-label" for="retakesCriterio">Critério de confirmação</label>
|
||||
<select id="retakesCriterio" class="select-field">
|
||||
<option value="conservador">Conservador — só evidências fortes</option>
|
||||
<option value="equilibrado" selected>Equilibrado — prováveis e fortes</option>
|
||||
<option value="sensivel">Sensível — também possíveis</option>
|
||||
</select>
|
||||
<label class="checkbox-field"><input id="retakesReaproveitar" type="checkbox" checked><span>Reaproveitar análise existente</span></label>
|
||||
<p class="field-help">A comparação textual, os reinícios de fala e as pausas já fazem parte da análise da engine.</p>
|
||||
</section>
|
||||
<section class="card"><div class="card-head"><h2>Análise visual auxiliar</h2></div>
|
||||
<label class="checkbox-field"><input id="retakesUsarVisual" type="checkbox"><span>Usar cortes visuais como evidência</span></label>
|
||||
<p class="field-help">Reforça a confiança, mas nunca decide sozinha. Reutiliza as cenas já analisadas.</p>
|
||||
<div class="button-row">
|
||||
<button class="button button-primary" id="btnRetakesAnalisar" onclick="retakesAnalisar()" type="button">Analisar retakes</button>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<!-- 4. Progresso -->
|
||||
<div class="callout callout-info" id="retakesProgressoCard" hidden>
|
||||
<strong id="retakesProgressoEtapa">Analisando retakes…</strong>
|
||||
<p id="retakesProgressoDetalhe">—</p>
|
||||
</div>
|
||||
|
||||
<!-- 5/6. Resultados e revisão -->
|
||||
<section class="card">
|
||||
<div class="card-head"><h2>Resultados</h2><span class="card-kicker" id="retakesContagem">—</span></div>
|
||||
<label class="field-label" for="retakesFiltro">Filtrar</label>
|
||||
<select id="retakesFiltro" class="select-field" onchange="retakesRenderResultados()">
|
||||
<option value="todos" selected>Todos</option>
|
||||
<option value="fortes">Fortes</option>
|
||||
<option value="possiveis">Possíveis e prováveis</option>
|
||||
<option value="pendente_de_revisao">Pendentes de revisão</option>
|
||||
<option value="confirmado">Confirmados</option>
|
||||
<option value="nao_e_retake">Rejeitados</option>
|
||||
<option value="ignorado">Ignorados</option>
|
||||
</select>
|
||||
<div id="retakesResultados" role="list" aria-label="Grupos de retakes"></div>
|
||||
<p class="field-help" id="retakesSemResultados" hidden>Nenhum retake identificado com os critérios atuais.</p>
|
||||
</section>
|
||||
<div id="retakesLog" role="log" aria-live="polite" aria-relevant="additions" tabindex="0" aria-label="Log de retakes"></div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<!-- ============================== CONEXÃO ============================== -->
|
||||
<div class="tab-panel" id="tabBridge" role="tabpanel" aria-labelledby="tabBtnBridge" hidden>
|
||||
|
||||
+197
-3
@@ -149,6 +149,7 @@ function switchTab(name) {
|
||||
var tabs = [
|
||||
{ key: "silence", panel: "tabSilence", btn: "tabBtnSilence" },
|
||||
{ key: "scanner", panel: "tabScanner", btn: "tabBtnScanner" },
|
||||
{ key: "retakes", panel: "tabRetakes", btn: "tabBtnRetakes" },
|
||||
{ key: "bridge", panel: "tabBridge", btn: "tabBtnBridge" },
|
||||
{ key: "models", panel: "tabModels", btn: "tabBtnModels" },
|
||||
{ key: "settings", panel: "tabSettings", btn: "tabBtnSettings" },
|
||||
@@ -166,6 +167,190 @@ function switchTab(name) {
|
||||
if (name === "silence") { syncTranscribeModelSelect(); syncLanguageInfo(); }
|
||||
if (name === "settings") renderEditorPersonalities();
|
||||
if (name === "scanner") { renderScannerTranscriptionOptions(); loadScannerProfile(); renderScannerHuggingFaceStatus(); }
|
||||
if (name === "retakes") retakesAoAbrir();
|
||||
}
|
||||
|
||||
// ---- Retakes ---------------------------------------------------------------
|
||||
// A aba apenas controla o processo e mostra resultados; a análise inteira
|
||||
// acontece na engine (scanner/retakes), chamada via executar_retakes.py.
|
||||
var RETAKES_ENGINE_SCRIPT = "/Volumes/Merongo/SISTEMAS/GENIAL SISTEMAS/Jhonny/code/engine/executar_retakes.py";
|
||||
var retakesCasoAtual = null;
|
||||
var retakesGrupos = [];
|
||||
var retakesProcesso = null;
|
||||
|
||||
function retakesRaiz() { return path.join(os.homedir(), ".premiere-mcp", "scanner"); }
|
||||
|
||||
function retakesAoAbrir() { retakesListarCasos(); }
|
||||
|
||||
function retakesListarCasos() {
|
||||
var seletor = document.getElementById("retakesCaso");
|
||||
var anterior = seletor.value;
|
||||
seletor.innerHTML = "";
|
||||
var raiz = retakesRaiz();
|
||||
var casos = [];
|
||||
try { casos = fs.readdirSync(raiz).filter(function (item) { return fs.statSync(path.join(raiz, item)).isDirectory(); }).sort(); } catch (_) {}
|
||||
if (!casos.length) { seletor.appendChild(new Option("Nenhum caso encontrado", "")); retakesCasoAtual = null; retakesInfo("Execute o Scanner primeiro para gerar transcrições."); return; }
|
||||
casos.forEach(function (caso) { seletor.appendChild(new Option(caso, caso)); });
|
||||
seletor.value = anterior && casos.indexOf(anterior) >= 0 ? anterior : casos[0];
|
||||
retakesCarregarCaso();
|
||||
}
|
||||
|
||||
function retakesInfo(mensagem) { document.getElementById("retakesInfoCaso").textContent = mensagem; }
|
||||
|
||||
function retakesArtefatosDeAudio(caso) {
|
||||
var pasta = path.join(retakesRaiz(), caso);
|
||||
try { return fs.readdirSync(pasta).filter(function (item) { return /-audio-faixa-\d+\.json$/.test(item); }).map(function (item) { return path.join(pasta, item); }).sort(); } catch (_) { return []; }
|
||||
}
|
||||
|
||||
function retakesCarregarCaso() {
|
||||
var caso = document.getElementById("retakesCaso").value;
|
||||
retakesCasoAtual = caso || null;
|
||||
retakesGrupos = [];
|
||||
retakesRenderResultados();
|
||||
if (!caso) { retakesInfo("Nenhum caso disponível."); return; }
|
||||
var artefatos = retakesArtefatosDeAudio(caso);
|
||||
var jaTem = fs.existsSync(path.join(retakesRaiz(), caso, "retakes"));
|
||||
retakesInfo("Caso: " + caso + " · " + artefatos.length + " faixa(s) de áudio com transcrição" + (jaTem ? " · já analisado" : " · não analisado"));
|
||||
document.getElementById("retakesStatusGeral").textContent = jaTem ? "Analisado" : "Não analisado";
|
||||
if (jaTem) retakesCarregarResultadosSalvos();
|
||||
}
|
||||
|
||||
function retakesCarregarResultadosSalvos() {
|
||||
var arquivo = path.join(retakesRaiz(), retakesCasoAtual, "retakes", "retakes-" + retakesCasoAtual + ".json");
|
||||
try {
|
||||
var dados = JSON.parse(fs.readFileSync(arquivo, "utf-8"));
|
||||
retakesGrupos = dados.grupos_de_retakes || [];
|
||||
retakesRenderResultados();
|
||||
} catch (erro) { showToast("err", "Não foi possível ler os resultados: " + erro.message); }
|
||||
}
|
||||
|
||||
function retakesFormatarTempo(segundos) {
|
||||
if (typeof segundos !== "number" || isNaN(segundos)) return "—";
|
||||
var total = Math.max(0, Math.round(segundos));
|
||||
var m = Math.floor(total / 60), s = total % 60;
|
||||
return String(m).padStart(2, "0") + ":" + String(s).padStart(2, "0");
|
||||
}
|
||||
|
||||
function retakesRotuloDoGrupo(grupo) {
|
||||
var tipo = { retake_confirmado: "Retake forte", possivel_retake: "Possível retake", repeticao_parcial: "Repetição parcial", repeticao_de_frase: "Repetição de frase", repeticao_de_inicio: "Repetição de início", continuidade_de_fala: "Continuidade de fala", sem_indicio_de_retake: "Sem indício" }[grupo.tipo] || grupo.tipo;
|
||||
var status = grupo.status_de_revisao === "confirmado" ? " · confirmado" : grupo.status_de_revisao === "nao_e_retake" ? " · rejeitado" : grupo.status_de_revisao === "ignorado" ? " · ignorado" : "";
|
||||
return tipo + " · " + Math.round(grupo.confianca * 100) + "% de confiança" + status;
|
||||
}
|
||||
|
||||
function retakesPassaNoFiltro(grupo) {
|
||||
var filtro = document.getElementById("retakesFiltro").value;
|
||||
if (filtro === "todos") return true;
|
||||
if (filtro === "fortes") return grupo.tipo === "retake_confirmado";
|
||||
if (filtro === "possiveis") return grupo.tipo !== "retake_confirmado" && grupo.tipo !== "sem_indicio_de_retake";
|
||||
return grupo.status_de_revisao === filtro;
|
||||
}
|
||||
|
||||
function retakesRenderResultados() {
|
||||
var alvo = document.getElementById("retakesResultados");
|
||||
alvo.innerHTML = "";
|
||||
var visiveis = retakesGrupos.filter(retakesPassaNoFiltro);
|
||||
document.getElementById("retakesContagem").textContent = retakesGrupos.length ? retakesGrupos.length + " grupo(s)" : "—";
|
||||
document.getElementById("retakesSemResultados").hidden = visiveis.length > 0;
|
||||
visiveis.forEach(function (grupo) { alvo.appendChild(retakesCartaoDoGrupo(grupo)); });
|
||||
}
|
||||
|
||||
function retakesCartaoDoGrupo(grupo) {
|
||||
var cartao = document.createElement("div");
|
||||
cartao.className = "card"; cartao.setAttribute("role", "listitem");
|
||||
var titulo = document.createElement("strong"); titulo.textContent = retakesRotuloDoGrupo(grupo);
|
||||
cartao.appendChild(titulo);
|
||||
grupo.tomadas.forEach(function (tomada, indice) {
|
||||
var linha = document.createElement("p");
|
||||
linha.textContent = "Tentativa " + (indice + 1) + ": " + retakesFormatarTempo(tomada.inicio) + " – " + retakesFormatarTempo(tomada.fim) + " · " + (tomada.texto || "");
|
||||
linha.style.margin = "2px 0"; cartao.appendChild(linha);
|
||||
});
|
||||
if (grupo.evidencias && grupo.evidencias.length) {
|
||||
var motivos = document.createElement("p");
|
||||
motivos.textContent = "Evidências: " + grupo.evidencias.map(function (e) { return e.descricao || e.tipo; }).join("; ");
|
||||
motivos.className = "field-help"; cartao.appendChild(motivos);
|
||||
}
|
||||
var acoes = document.createElement("div"); acoes.className = "button-row";
|
||||
[["confirmar", "Confirmar"], ["rejeitar", "Não é retake"], ["manter", "Manter p/ revisão"], ["ignorar", "Ignorar"]].forEach(function (par) {
|
||||
var botao = document.createElement("button");
|
||||
botao.className = "button button-ghost"; botao.type = "button"; botao.textContent = par[1];
|
||||
botao.onclick = function () { retakesRevisar(grupo.id, par[0]); };
|
||||
acoes.appendChild(botao);
|
||||
});
|
||||
cartao.appendChild(acoes);
|
||||
return cartao;
|
||||
}
|
||||
|
||||
function retakesAnalisar() {
|
||||
if (!retakesCasoAtual) { showToast("err", "Selecione um caso do Scanner."); return; }
|
||||
var artefatos = retakesArtefatosDeAudio(retakesCasoAtual);
|
||||
if (!artefatos.length) { showToast("err", "O caso não possui transcrição. Execute o Scanner com transcrição primeiro."); return; }
|
||||
if (retakesProcesso) { showToast("err", "Já existe uma análise em andamento."); return; }
|
||||
if (!document.getElementById("retakesReaproveitar").checked) {
|
||||
var pastaRetakes = path.join(retakesRaiz(), retakesCasoAtual, "retakes");
|
||||
try { if (fs.existsSync(pastaRetakes)) fs.rmSync(pastaRetakes, { recursive: true }); } catch (_) {}
|
||||
}
|
||||
var pedido = { acao: "analisar", video_id: retakesCasoAtual, artefatos_de_audio: artefatos,
|
||||
configuracao: { criterio: document.getElementById("retakesCriterio").value,
|
||||
usar_analise_visual: document.getElementById("retakesUsarVisual").checked } };
|
||||
var entrada = path.join(os.tmpdir(), "pedido-retakes-" + Date.now() + ".json");
|
||||
fs.writeFileSync(entrada, JSON.stringify(pedido, null, 2), "utf-8");
|
||||
setBusy("btnRetakesAnalisar", true);
|
||||
taskStart("Retakes", "Preparando análise", null);
|
||||
document.getElementById("retakesProgressoCard").hidden = false;
|
||||
document.getElementById("retakesProgressoEtapa").textContent = "Analisando retakes…";
|
||||
var child = childProcess.spawn("python3", [RETAKES_ENGINE_SCRIPT, entrada, path.join(retakesRaiz(), retakesCasoAtual)],
|
||||
{ cwd: "/Volumes/Merongo/SISTEMAS/GENIAL SISTEMAS/Jhonny/code/engine", stdio: ["ignore", "pipe", "pipe"] });
|
||||
retakesProcesso = child;
|
||||
var resto = "";
|
||||
child.stdout.on("data", function (pedaco) {
|
||||
resto += pedaco.toString(); var linhas = resto.split("\n"); resto = linhas.pop();
|
||||
linhas.forEach(function (linha) { var evento; try { evento = JSON.parse(linha); } catch (_) { return; } retakesProcessarEvento(evento); });
|
||||
});
|
||||
child.stderr.on("data", function (pedaco) { if (pedaco.toString().trim()) retakesLog(pedaco.toString().trim()); });
|
||||
child.on("error", function (erro) { retakesProcesso = null; setBusy("btnRetakesAnalisar", false); taskEnd(false, "Engine indisponível"); showToast("err", erro.message); });
|
||||
child.on("close", function (codigo) {
|
||||
retakesProcesso = null; setBusy("btnRetakesAnalisar", false);
|
||||
document.getElementById("retakesProgressoCard").hidden = true;
|
||||
document.getElementById("retakesStatusGeral").textContent = "Analisado";
|
||||
if (codigo !== 0) taskEnd(false, "Análise falhou");
|
||||
});
|
||||
}
|
||||
|
||||
function retakesProcessarEvento(evento) {
|
||||
if (evento.evento === "progresso") {
|
||||
taskUpdate({ stage: evento.etapa, pct: evento.percentual });
|
||||
document.getElementById("retakesProgressoEtapa").textContent = evento.etapa;
|
||||
document.getElementById("retakesProgressoDetalhe").textContent = evento.percentual + "% concluído";
|
||||
} else if (evento.evento === "erro") {
|
||||
showToast("err", evento.mensagem); retakesLog("Erro: " + evento.mensagem);
|
||||
} else if (evento.evento === "concluido") {
|
||||
retakesGrupos = (evento.resultado && evento.resultado.grupos) || [];
|
||||
retakesRenderResultados();
|
||||
taskEnd(true, retakesGrupos.length + " grupo(s) de retakes");
|
||||
showToast("ok", "Análise concluída: " + retakesGrupos.length + " grupo(s) encontrado(s).");
|
||||
}
|
||||
}
|
||||
|
||||
function retakesRevisar(grupoId, acao) {
|
||||
if (!retakesCasoAtual) return;
|
||||
var pedido = { acao: "revisar", video_id: retakesCasoAtual, revisao: { grupo_id: grupoId, acao: acao } };
|
||||
var entrada = path.join(os.tmpdir(), "revisao-retakes-" + Date.now() + ".json");
|
||||
fs.writeFileSync(entrada, JSON.stringify(pedido), "utf-8");
|
||||
var child = childProcess.spawn("python3", [RETAKES_ENGINE_SCRIPT, entrada, path.join(retakesRaiz(), retakesCasoAtual)],
|
||||
{ cwd: "/Volumes/Merongo/SISTEMAS/GENIAL SISTEMAS/Jhonny/code/engine", stdio: ["ignore", "pipe", "pipe"] });
|
||||
child.on("close", function (codigo) {
|
||||
if (codigo !== 0) { showToast("err", "Não foi possível registrar a revisão."); return; }
|
||||
var novos = { confirmar: "confirmado", rejeitar: "nao_e_retake", manter: "pendente_de_revisao", ignorar: "ignorado" };
|
||||
retakesGrupos = retakesGrupos.map(function (g) { return g.id === grupoId ? Object.assign({}, g, { status_de_revisao: novos[acao] }) : g; });
|
||||
retakesRenderResultados();
|
||||
showToast("ok", "Revisão registrada.");
|
||||
});
|
||||
}
|
||||
|
||||
function retakesLog(mensagem) {
|
||||
var alvo = document.getElementById("retakesLog");
|
||||
var linha = document.createElement("p"); linha.textContent = mensagem;
|
||||
alvo.appendChild(linha);
|
||||
}
|
||||
|
||||
// ---- Perfil do Scanner -----------------------------------------------------
|
||||
@@ -191,23 +376,31 @@ function scannerRenderTracks(kind, tracks, selected) {
|
||||
tracks.forEach(function (track) { var label = document.createElement("label"); label.className = "checkbox-field"; var input = document.createElement("input"); input.type = "checkbox"; input.setAttribute("data-scanner-track", kind); input.value = track.index; input.checked = selected.indexOf(track.index) >= 0; label.appendChild(input); label.appendChild(document.createTextNode(" " + track.name + " · " + track.numClips + " clipe(s)")); target.appendChild(label); });
|
||||
}
|
||||
|
||||
function scannerUpdateIntervalLabel() {
|
||||
var valor = Number(document.getElementById("scannerInterval").value);
|
||||
var texto = valor === 1 ? "1 s/quadro" : valor.toFixed(1).replace(/\.0$/, "") + " s/quadro";
|
||||
document.getElementById("scannerIntervalValue").textContent = texto;
|
||||
}
|
||||
|
||||
function scannerProfileFromScreen() {
|
||||
var enabled = Array.prototype.slice.call(document.querySelectorAll("[data-scanner-resource]:checked")).map(function (item) { return item.getAttribute("data-scanner-resource"); });
|
||||
function include(source, extras) { if (enabled.indexOf(source) >= 0) extras.forEach(function (item) { if (enabled.indexOf(item) < 0) enabled.push(item); }); }
|
||||
include("faces", ["qualidade_facial"]); include("pose", ["maos"]); include("ocr", ["codigos"]); include("estetica", ["horizonte", "retangulos", "contornos"]);
|
||||
var modelo = document.getElementById("scannerTranscribeModel").value;
|
||||
var opcao = document.getElementById("scannerTranscribeModel").selectedOptions[0];
|
||||
return { versao: 1, intervalo_em_segundos: Number(document.getElementById("scannerInterval").value), faixas_de_video: scannerSelectedTracks("video"), faixas_de_audio: scannerSelectedTracks("audio"), recursos_vision: SCANNER_ALL_RESOURCES.filter(function (item) { return enabled.indexOf(item) >= 0; }), detectar_cenas: document.getElementById("scannerScenes").checked, analisar_continuidade: document.getElementById("scannerContinuity").checked, preparar_reenquadramento: document.getElementById("scannerReframe").checked, interpretar_cena: document.getElementById("scannerInterpret").checked, transcricao: { modelo: modelo, caminho_modelo: opcao && opcao.dataset.path || "", idioma: document.getElementById("scannerTranscribeLanguage").value, diarizacao: document.getElementById("scannerDiarization").checked, usar_hugging_face: !!loadHfToken() } };
|
||||
return { versao: 1, intervalo_em_segundos: Number(document.getElementById("scannerInterval").value), resolucao_maxima: Number(document.getElementById("scannerResolution").value), faixas_de_video: scannerSelectedTracks("video"), faixas_de_audio: scannerSelectedTracks("audio"), recursos_vision: SCANNER_ALL_RESOURCES.filter(function (item) { return enabled.indexOf(item) >= 0; }), detectar_cenas: document.getElementById("scannerScenes").checked, analisar_continuidade: document.getElementById("scannerContinuity").checked, preparar_reenquadramento: document.getElementById("scannerReframe").checked, interpretar_cena: document.getElementById("scannerInterpret").checked, transcricao: { modelo: modelo, caminho_modelo: opcao && opcao.dataset.path || "", idioma: document.getElementById("scannerTranscribeLanguage").value, diarizacao: document.getElementById("scannerDiarization").checked, usar_hugging_face: !!loadHfToken() } };
|
||||
}
|
||||
|
||||
function saveScannerProfile() {
|
||||
try { var profile = scannerProfileFromScreen(); localStorage.setItem(SCANNER_PROFILE_KEY, JSON.stringify(profile)); document.getElementById("scannerProfileStatus").textContent = "Perfil salvo: " + profile.intervalo_em_segundos + " s por frame; " + profile.faixas_de_video.length + " faixa(s) de vídeo."; showToast("ok", "Perfil do Scanner salvo."); } catch (error) { showToast("err", error.message || String(error)); }
|
||||
try { var profile = scannerProfileFromScreen(); localStorage.setItem(SCANNER_PROFILE_KEY, JSON.stringify(profile)); document.getElementById("scannerProfileStatus").textContent = "Perfil salvo: " + profile.intervalo_em_segundos + " s por quadro; " + profile.faixas_de_video.length + " faixa(s) de vídeo."; showToast("ok", "Perfil do Scanner salvo."); } catch (error) { showToast("err", error.message || String(error)); }
|
||||
}
|
||||
|
||||
function loadScannerProfile() {
|
||||
var saved; try { saved = JSON.parse(localStorage.getItem(SCANNER_PROFILE_KEY) || "null"); } catch (_) { saved = null; }
|
||||
if (!saved) return;
|
||||
if (!saved) { scannerUpdateIntervalLabel(); return; }
|
||||
document.getElementById("scannerInterval").value = String(saved.intervalo_em_segundos || 1);
|
||||
scannerUpdateIntervalLabel();
|
||||
document.getElementById("scannerResolution").value = String(saved.resolucao_maxima != null ? saved.resolucao_maxima : 1080);
|
||||
if (scannerTimeline) { scannerRenderTracks("video", scannerTimeline.videoTracks, saved.faixas_de_video || []); scannerRenderTracks("audio", scannerTimeline.audioTracks, saved.faixas_de_audio || []); }
|
||||
var resources = saved.recursos_vision || SCANNER_ALL_RESOURCES;
|
||||
Array.prototype.slice.call(document.querySelectorAll("[data-scanner-resource]")).forEach(function (item) { item.checked = resources.indexOf(item.getAttribute("data-scanner-resource")) >= 0; });
|
||||
@@ -368,6 +561,7 @@ var fs = nodeRequire("fs");
|
||||
var path = nodeRequire("path");
|
||||
var os = nodeRequire("os");
|
||||
var https = nodeRequire("https");
|
||||
var childProcess = nodeRequire("child_process");
|
||||
function defaultBridgeDirectory() {
|
||||
try {
|
||||
var nodeProcess = nodeRequire("process");
|
||||
|
||||
@@ -339,6 +339,9 @@ h1, h2, h3 { margin: 0; }
|
||||
transition: border-color .15s, box-shadow .15s;
|
||||
}
|
||||
.select-field { height: 32px; padding: 0 8px; font-size: 10.5px; }
|
||||
.range-field-row { display: flex; align-items: center; gap: 10px; }
|
||||
.range-field-row input[type="range"] { flex: 1 1 auto; accent-color: var(--violet); height: 20px; }
|
||||
.range-field-value { flex: 0 0 auto; min-width: 84px; text-align: right; font: 10.5px var(--font-mono); color: var(--text-secondary); }
|
||||
.path-field input, .file-field input { height: 34px; padding: 0 10px; font: 10px var(--font-mono); color: var(--text-secondary); user-select: text; }
|
||||
.path-field > svg + input { padding-left: 32px; }
|
||||
textarea { padding: 8px 10px; font: 10px var(--font-mono); color: var(--text-secondary); resize: vertical; user-select: text; }
|
||||
|
||||
Reference in New Issue
Block a user