Adiciona ferramenta VideoMind para transcrição e análise de vídeos com resumo customizado
This commit is contained in:
+227
@@ -4489,6 +4489,233 @@ const initApp = () => {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// VIDEOMIND (TRANSCRIÇÃO E ANÁLISE DE VÍDEO PEDAGÓGICO)
|
||||
// ============================================================
|
||||
const videoMindModal = document.getElementById('videoMindModal');
|
||||
const btnCloseVideoMindModal = document.getElementById('btnCloseVideoMindModal');
|
||||
const barBtnVideoMind = document.getElementById('barBtnVideoMind');
|
||||
|
||||
const btnGenerateVideoMind = document.getElementById('btnGenerateVideoMind');
|
||||
const videoMindUrlInput = document.getElementById('videoMindUrlInput');
|
||||
const videoMindLoader = document.getElementById('videoMindLoader');
|
||||
const videoMindLoaderText = document.getElementById('videoMindLoaderText');
|
||||
const videoMindResultArea = document.getElementById('videoMindResultArea');
|
||||
|
||||
const videoMindThumbnail = document.getElementById('videoMindThumbnail');
|
||||
const videoMindTitle = document.getElementById('videoMindTitle');
|
||||
const videoMindAuthor = document.getElementById('videoMindAuthor');
|
||||
const videoMindReportArea = document.getElementById('videoMindReportArea');
|
||||
|
||||
const btnCopyVideoMindReport = document.getElementById('btnCopyVideoMindReport');
|
||||
const btnDownloadVideoMindReport = document.getElementById('btnDownloadVideoMindReport');
|
||||
const btnPdfVideoMindReport = document.getElementById('btnPdfVideoMindReport');
|
||||
|
||||
let selectedVideoMindLines = 20;
|
||||
let activeVideoMindReport = null;
|
||||
|
||||
// Listener para selecionar o tamanho do resumo
|
||||
document.querySelectorAll('#videoMindModal .music-option-grid .btn-videomind-lines').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
document.querySelectorAll('#videoMindModal .music-option-grid .btn-videomind-lines').forEach(b => b.classList.remove('active'));
|
||||
btn.classList.add('active');
|
||||
selectedVideoMindLines = parseInt(btn.dataset.lines) || 20;
|
||||
});
|
||||
});
|
||||
|
||||
// Abrir modal do VideoMind
|
||||
if (barBtnVideoMind) {
|
||||
barBtnVideoMind.addEventListener('click', () => {
|
||||
videoMindModal.style.display = 'flex';
|
||||
videoMindUrlInput.value = '';
|
||||
videoMindResultArea.style.display = 'none';
|
||||
videoMindReportArea.innerHTML = '';
|
||||
if (videoMindLoader) videoMindLoader.style.display = 'none';
|
||||
btnGenerateVideoMind.disabled = false;
|
||||
activeVideoMindReport = null;
|
||||
});
|
||||
}
|
||||
|
||||
// Fechar modal
|
||||
if (btnCloseVideoMindModal) {
|
||||
btnCloseVideoMindModal.addEventListener('click', () => {
|
||||
videoMindModal.style.display = 'none';
|
||||
});
|
||||
}
|
||||
|
||||
window.addEventListener('click', (e) => {
|
||||
if (e.target === videoMindModal) {
|
||||
videoMindModal.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
// Ação de analisar vídeo
|
||||
if (btnGenerateVideoMind) {
|
||||
btnGenerateVideoMind.addEventListener('click', async () => {
|
||||
const url = videoMindUrlInput.value.trim();
|
||||
if (!url) {
|
||||
alert('Por favor, insira o link de um vídeo do YouTube.');
|
||||
return;
|
||||
}
|
||||
|
||||
btnGenerateVideoMind.disabled = true;
|
||||
videoMindLoader.style.display = 'flex';
|
||||
videoMindResultArea.style.display = 'none';
|
||||
videoMindLoaderText.textContent = 'Buscando metadados do vídeo...';
|
||||
|
||||
let statusMsgIdx = 0;
|
||||
const statusMessages = [
|
||||
'Buscando legenda automática do vídeo...',
|
||||
'Carregando a transcrição completa...',
|
||||
'Enviando transcrição para a IA...',
|
||||
'Pedagoga IA analisando o conteúdo...',
|
||||
'Alinhando temas do vídeo com a BNCC...',
|
||||
'Gerando resumo de ' + selectedVideoMindLines + ' linhas...',
|
||||
'Criando propostas de atividades pedagógicas...',
|
||||
'Finalizando o parecer pedagógico...'
|
||||
];
|
||||
|
||||
const statusInterval = setInterval(() => {
|
||||
if (statusMsgIdx < statusMessages.length) {
|
||||
videoMindLoaderText.textContent = statusMessages[statusMsgIdx];
|
||||
statusMsgIdx++;
|
||||
} else {
|
||||
videoMindLoaderText.textContent = 'Ajustando formatação da análise...';
|
||||
}
|
||||
}, 4000);
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/videomind/analyze', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
url,
|
||||
lines: selectedVideoMindLines
|
||||
})
|
||||
});
|
||||
|
||||
clearInterval(statusInterval);
|
||||
|
||||
if (!response.ok) {
|
||||
const errData = await response.json();
|
||||
throw new Error(errData.error || 'Erro desconhecido');
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
activeVideoMindReport = data;
|
||||
|
||||
// Preencher informações do vídeo
|
||||
videoMindTitle.textContent = data.metadata.title;
|
||||
videoMindAuthor.textContent = 'Canal: ' + data.metadata.author;
|
||||
videoMindThumbnail.src = data.metadata.thumbnail;
|
||||
|
||||
// Renderizar Markdown
|
||||
if (window.marked) {
|
||||
videoMindReportArea.innerHTML = marked.parse(data.report);
|
||||
} else {
|
||||
videoMindReportArea.textContent = data.report;
|
||||
}
|
||||
|
||||
videoMindLoader.style.display = 'none';
|
||||
videoMindResultArea.style.display = 'flex';
|
||||
|
||||
} catch (err) {
|
||||
clearInterval(statusInterval);
|
||||
console.error('Erro no VideoMind:', err);
|
||||
alert('Erro ao analisar vídeo: ' + err.message);
|
||||
videoMindLoader.style.display = 'none';
|
||||
btnGenerateVideoMind.disabled = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Copiar Parecer
|
||||
if (btnCopyVideoMindReport) {
|
||||
btnCopyVideoMindReport.addEventListener('click', () => {
|
||||
if (activeVideoMindReport && activeVideoMindReport.report) {
|
||||
navigator.clipboard.writeText(activeVideoMindReport.report)
|
||||
.then(() => {
|
||||
btnCopyVideoMindReport.textContent = '✅ Copiado!';
|
||||
setTimeout(() => {
|
||||
btnCopyVideoMindReport.textContent = '📋 Copiar Parecer';
|
||||
}, 2000);
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('Falha ao copiar:', err);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Baixar relatório TXT
|
||||
if (btnDownloadVideoMindReport) {
|
||||
btnDownloadVideoMindReport.addEventListener('click', () => {
|
||||
if (activeVideoMindReport && activeVideoMindReport.report) {
|
||||
const title = activeVideoMindReport.metadata.title || 'analise_video';
|
||||
const blob = new Blob([activeVideoMindReport.report], { type: 'text/plain;charset=utf-8' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = `VideoMind_${title.replace(/\s+/g, '_').toLowerCase()}.txt`;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Baixar relatório PDF
|
||||
if (btnPdfVideoMindReport) {
|
||||
btnPdfVideoMindReport.addEventListener('click', () => {
|
||||
if (!activeVideoMindReport) return;
|
||||
|
||||
const printWindow = window.open('', '_blank');
|
||||
printWindow.document.write(`
|
||||
<html>
|
||||
<head>
|
||||
<title>VideoMind - Parecer Pedagógico</title>
|
||||
<style>
|
||||
body { font-family: 'Outfit', 'Inter', -apple-system, sans-serif; padding: 40px; color: #333; line-height: 1.6; }
|
||||
h1, h2, h3 { color: #0891b2; margin-top: 24px; margin-bottom: 12px; }
|
||||
h1 { border-bottom: 2px solid #e2e8f0; padding-bottom: 10px; font-size: 22px; }
|
||||
h2 { font-size: 18px; border-bottom: 1px solid #f1f5f9; padding-bottom: 6px; }
|
||||
.meta-box { background: #f8fafc; padding: 16px; border-radius: 8px; margin-bottom: 24px; font-size: 14px; border: 1px solid #e2e8f0; display: flex; gap: 16px; align-items: center; }
|
||||
.meta-info { display: flex; flex-direction: column; gap: 4px; }
|
||||
.meta-label { font-weight: bold; color: #475569; }
|
||||
p { margin-bottom: 14px; text-align: justify; }
|
||||
ul, ol { margin-bottom: 14px; padding-left: 20px; }
|
||||
li { margin-bottom: 6px; }
|
||||
@media print {
|
||||
body { padding: 0; }
|
||||
button { display: none; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>🎬 VideoMind - Análise & Parecer Pedagógico</h1>
|
||||
<div class="meta-box">
|
||||
<img src="${activeVideoMindReport.metadata.thumbnail}" style="width: 100px; aspect-ratio: 16/9; object-fit: cover; border-radius: 6px;">
|
||||
<div class="meta-info">
|
||||
<div><span class="meta-label">Vídeo:</span> <span>${activeVideoMindReport.metadata.title}</span></div>
|
||||
<div><span class="meta-label">Canal:</span> <span>${activeVideoMindReport.metadata.author}</span></div>
|
||||
<div><span class="meta-label">Resumo Escolhido:</span> <span>${selectedVideoMindLines} Linhas</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div>${window.marked ? marked.parse(activeVideoMindReport.report) : activeVideoMindReport.report}</div>
|
||||
<script>
|
||||
window.onload = function() {
|
||||
window.print();
|
||||
setTimeout(() => { window.close(); }, 500);
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
`);
|
||||
printWindow.document.close();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Player de áudio personalizado global para as mídias geradas
|
||||
|
||||
@@ -237,6 +237,9 @@
|
||||
<button type="button" class="btn-prompt-mode mode-comics" id="barBtnComics" style="border-color: rgba(236, 72, 153, 0.4); color: #f472b6; background: rgba(236, 72, 153, 0.05);" title="Abrir Fábrica de Quadrinhos Pedagógicos">
|
||||
<span class="mode-icon">🎨</span> Fábrica de Quadrinhos
|
||||
</button>
|
||||
<button type="button" class="btn-prompt-mode mode-video" id="barBtnVideoMind" style="border-color: rgba(6, 182, 212, 0.4); color: #22d3ee; background: rgba(6, 182, 212, 0.05);" title="Análise pedagógica e resumo de vídeos do YouTube">
|
||||
<span class="mode-icon">🎬</span> VideoMind
|
||||
</button>
|
||||
<button type="button" class="btn-prompt-mode mode-music" data-mode="AUDIO" title="Compor e gerar uma música pedagógica">
|
||||
<span class="mode-icon">🎵</span> Criar Letra de Música
|
||||
</button>
|
||||
@@ -938,6 +941,72 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal: VideoMind (Transcrição e Resumo Pedagógico de Vídeos) -->
|
||||
<div id="videoMindModal" class="settings-modal" style="display: none;">
|
||||
<div class="settings-modal-content" style="max-width: 700px; width: 95%; max-height: 90vh; display: flex; flex-direction: column;">
|
||||
<div class="settings-modal-header" style="background: var(--bg-secondary); border-bottom: 1px solid var(--border-light);">
|
||||
<h3 style="display: flex; align-items: center; gap: 8px; font-family: 'Outfit', sans-serif;">🎬 VideoMind - Transcrição & Análise de Vídeo</h3>
|
||||
<button id="btnCloseVideoMindModal" class="btn-close-modal" style="background: none; border: none; color: var(--text-secondary); cursor: pointer; font-size: 1.5rem;">×</button>
|
||||
</div>
|
||||
<div class="settings-modal-body" style="padding: 20px; overflow-y: auto; display: flex; flex-direction: column; gap: 18px; flex: 1;">
|
||||
|
||||
<!-- INPUT URL YOUTUBE -->
|
||||
<div class="settings-group">
|
||||
<label style="color: var(--text-secondary); font-size: 0.82rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px;">1. Link do Vídeo no YouTube</label>
|
||||
<input type="text" id="videoMindUrlInput" placeholder="Ex: https://www.youtube.com/watch?v=..." class="obs-input" style="width: 100%; margin-top: 4px; padding: 10px;">
|
||||
</div>
|
||||
|
||||
<!-- TAMANHO DO RESUMO -->
|
||||
<div class="settings-group">
|
||||
<label style="color: var(--text-secondary); font-size: 0.82rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px;">2. Tamanho do Resumo Desejado</label>
|
||||
<div class="music-option-grid" style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; margin-top: 4px;">
|
||||
<button type="button" class="btn-music-option btn-videomind-lines active" data-lines="20">📝 Curto (~20 linhas)</button>
|
||||
<button type="button" class="btn-music-option btn-videomind-lines" data-lines="50">📄 Médio (~50 linhas)</button>
|
||||
<button type="button" class="btn-music-option btn-videomind-lines" data-lines="100">📚 Longo (~100 linhas)</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- BOTÃO ANALISAR E STATUS -->
|
||||
<div style="display: flex; flex-direction: column; gap: 10px; margin-top: 8px;">
|
||||
<button id="btnGenerateVideoMind" style="background: linear-gradient(135deg, #06b6d4 0%, #0891b2 100%); color: white; border: none; padding: 12px; border-radius: 8px; font-weight: 600; cursor: pointer; display: flex; align-items: center; justify-content: center; gap: 8px; transition: opacity 0.2s; box-shadow: 0 4px 12px rgba(6, 182, 212, 0.25);">
|
||||
<span>🎬 Analisar e Transcrever Vídeo</span>
|
||||
</button>
|
||||
<div id="videoMindLoader" style="display: none; align-items: center; justify-content: center; gap: 10px; color: var(--text-secondary); font-size: 0.9rem; padding: 10px; background: rgba(255, 255, 255, 0.02); border-radius: 8px;">
|
||||
<div class="record-spinner" style="width: 20px; height: 20px; border-width: 2px;"></div>
|
||||
<span id="videoMindLoaderText">Buscando informações do vídeo...</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ÁREA DE RESULTADO (INICIALMENTE OCULTA) -->
|
||||
<div id="videoMindResultArea" style="display: none; border-top: 1px solid var(--border-light); padding-top: 16px; flex-direction: column; gap: 18px;">
|
||||
|
||||
<!-- Metadados do Vídeo -->
|
||||
<div style="background: var(--bg-secondary); padding: 14px; border-radius: 12px; display: flex; gap: 14px; border: 1px solid var(--border-light); align-items: center;">
|
||||
<img id="videoMindThumbnail" src="" alt="Thumbnail" style="width: 120px; aspect-ratio: 16/9; object-fit: cover; border-radius: 8px; border: 1px solid var(--border-light);">
|
||||
<div style="display: flex; flex-direction: column; gap: 4px;">
|
||||
<h4 id="videoMindTitle" style="margin: 0; color: var(--text-primary); font-size: 1rem; font-weight: 600; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;">Título do Vídeo</h4>
|
||||
<span id="videoMindAuthor" style="font-size: 0.85rem; color: var(--text-secondary);">Canal</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Ações da Análise -->
|
||||
<div style="display: flex; justify-content: space-between; align-items: center;">
|
||||
<label style="color: var(--text-secondary); font-size: 0.78rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px;">Resumo & Parecer Pedagógico</label>
|
||||
<div style="display: flex; gap: 8px;">
|
||||
<button id="btnCopyVideoMindReport" style="background: none; border: 1px solid var(--border-light); color: var(--text-secondary); padding: 5px 10px; border-radius: 6px; font-size: 0.75rem; cursor: pointer; transition: all 0.2s;">📋 Copiar Parecer</button>
|
||||
<button id="btnDownloadVideoMindReport" style="background: none; border: 1px solid var(--border-light); color: var(--text-secondary); padding: 5px 10px; border-radius: 6px; font-size: 0.75rem; cursor: pointer; transition: all 0.2s;">💾 Baixar TXT</button>
|
||||
<button id="btnPdfVideoMindReport" style="background: none; border: 1px solid var(--border-light); color: var(--text-secondary); padding: 5px 10px; border-radius: 6px; font-size: 0.75rem; cursor: pointer; transition: all 0.2s;">📄 Baixar PDF</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Corpo da Análise em Markdown -->
|
||||
<div id="videoMindReportArea" class="report-content-body" style="background: var(--bg-secondary); padding: 16px; border-radius: 10px; max-height: 350px; overflow-y: auto; font-family: inherit; font-size: 0.95rem; line-height: 1.6; border: 1px solid var(--border-light); color: var(--text-primary); margin: 0; box-shadow: inset 0 2px 4px rgba(0,0,0,0.1);"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bibliotecas externas para renderizar markdown e blocos de código -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/marked/12.0.1/marked.min.js"></script>
|
||||
|
||||
Reference in New Issue
Block a user