🔧 Auto-deploy: Modo de inferência inteligente e fallback para o VideoMind quando legendas falharem

This commit is contained in:
2026-06-05 00:11:38 +00:00
parent e389c0144b
commit f32565d720
+55 -14
View File
@@ -735,29 +735,37 @@ app.post('/api/videomind/analyze', requireAuth, async (req, res) => {
console.error('Erro ao buscar metadados do oEmbed:', metaErr);
}
let transcriptText = '';
let isFallbackMode = false;
let systemNotice = '';
try {
// Tenta obter legenda
const transcriptObj = await YoutubeTranscript.fetchTranscript(videoId);
transcriptText = transcriptObj.map(t => t.text).join(' ');
} catch (transcErr) {
console.error('Erro ao obter transcrição:', transcErr);
console.warn(`[VideoMind] Não foi possível obter legenda direta para o vídeo ${videoId}. Ativando modo de inferência inteligente pelo título/autor. Detalhe do erro:`, transcErr.message);
isFallbackMode = true;
}
if (!transcriptText) {
return res.status(400).json({
error: 'Não foi possível extrair as legendas/transcrição automática deste vídeo. Certifique-se de que o vídeo possui legendas ou transcrição automática disponíveis no YouTube e tente novamente.'
});
isFallbackMode = true;
transcriptText = `[Aviso: Devido a restrições e bloqueio de IP do YouTube, a legenda crua de áudio não pôde ser lida diretamente pelo servidor. Por favor, analise e descreva os objetivos pedagógicos com base estritamente no título do vídeo, canal do autor e no seu vasto conhecimento pedagógico sobre o tema indicado no título]`;
systemNotice = `> ⚠️ **Nota do Sistema**: Devido a restrições de conexão do YouTube (bloqueio de IP por excesso de requisições na VPS), a transcrição de áudio automática foi bloqueada temporariamente. A análise e o resumo abaixo foram elaborados a partir do título do vídeo, canal do autor e fundamentação psicopedagógica correspondente.\n\n`;
}
try {
const prompt = `Analise a transcrição deste vídeo intitulado "${metadata.title}" (Canal: ${metadata.author}):
Transcrição do vídeo:
const prompt = `Analise este vídeo do YouTube intitulado "${metadata.title}" (Canal: ${metadata.author}):
ID do Vídeo no YouTube: ${videoId}
Link do vídeo: https://www.youtube.com/watch?v=${videoId}
Conteúdo ou descrição da transcrição:
"""
${transcriptText}
"""
Instruções para o conteúdo de saída:
1. Escreva um RESUMO detalhado do vídeo. Esse resumo precisa ter EXATAMENTE ou APROXIMADAMENTE ${numLines} linhas de texto. Seja extremamente fiel aos fatos relatados.
1. Escreva um RESUMO detalhado do vídeo. Esse resumo precisa ter EXATAMENTE ou APROXIMADAMENTE ${numLines} linhas de texto. Se a transcrição direta não estiver disponível, faça um resumo conceitual com base no título e no tema geral do vídeo com a mesma quantidade de linhas.
2. Faça uma ANÁLISE PEDAGÓGICA COMPLEMENTAR detalhando:
- Relevância do conteúdo para fins educacionais.
- Indicação de faixa etária/segmento dos alunos recomendado.
@@ -771,17 +779,50 @@ Retorne a resposta estruturada estritamente em Markdown. Separe as seções usan
# Análise Pedagógica e Sugestões Práticas
[Insira a análise pedagógica, os pontos da BNCC e as sugestões de atividades práticas aqui]`;
const aiResponse = await callMinimax({
system: "Você é um pedagogo experiente e especialista em planejamento de aulas no Brasil, alinhado à Base Nacional Comum Curricular (BNCC).",
messages: [{ role: 'user', content: prompt }],
temperature: 0.5,
max_tokens: 3000
});
let reportText = '';
const systemInstruction = "Você é um pedagogo experiente e especialista em planejamento de aulas no Brasil, alinhado à Base Nacional Comum Curricular (BNCC).";
// Se estiver em fallback, damos preferência ao Gemini pela capacidade de grounding e busca
if (isFallbackMode && process.env.GOOGLE_AI_API_KEY) {
try {
console.log('[VideoMind] Usando fallback com Google Gemini...');
const geminiUrl = `https://generativelanguage.googleapis.com/v1beta/models/${process.env.GOOGLE_AI_MODEL || 'gemini-2.5-flash'}:generateContent?key=${process.env.GOOGLE_AI_API_KEY}`;
const response = await fetch(geminiUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
contents: [{ parts: [{ text: `${systemInstruction}\n\nTarefa:\n${prompt}` }] }],
generationConfig: { temperature: 0.5, maxOutputTokens: 4000 }
})
});
if (response.ok) {
const data = await response.json();
reportText = data.candidates?.[0]?.content?.parts?.[0]?.text || '';
} else {
const errTxt = await response.text();
console.warn('[VideoMind] Fallback do Gemini respondeu com erro:', errTxt);
}
} catch (geminiErr) {
console.error('[VideoMind] Erro no fallback do Gemini:', geminiErr.message);
}
}
// Se o Gemini não foi usado ou falhou, usamos o callMinimax padrão
if (!reportText) {
const aiResponse = await callMinimax({
system: systemInstruction,
messages: [{ role: 'user', content: prompt }],
temperature: 0.5,
max_tokens: 3000
});
reportText = aiResponse.text || '';
}
res.json({
success: true,
metadata,
report: aiResponse.text || ''
report: systemNotice + reportText
});
} catch (err) {
console.error('Erro ao processar análise do VideoMind:', err);