🔧 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); console.error('Erro ao buscar metadados do oEmbed:', metaErr);
} }
let transcriptText = '';
let isFallbackMode = false;
let systemNotice = '';
try { try {
// Tenta obter legenda // Tenta obter legenda
const transcriptObj = await YoutubeTranscript.fetchTranscript(videoId); const transcriptObj = await YoutubeTranscript.fetchTranscript(videoId);
transcriptText = transcriptObj.map(t => t.text).join(' '); transcriptText = transcriptObj.map(t => t.text).join(' ');
} catch (transcErr) { } 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) { if (!transcriptText) {
return res.status(400).json({ isFallbackMode = true;
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.' 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 { try {
const prompt = `Analise a transcrição deste vídeo intitulado "${metadata.title}" (Canal: ${metadata.author}): const prompt = `Analise este vídeo do YouTube intitulado "${metadata.title}" (Canal: ${metadata.author}):
Transcrição do vídeo: 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} ${transcriptText}
""" """
Instruções para o conteúdo de saída: 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: 2. Faça uma ANÁLISE PEDAGÓGICA COMPLEMENTAR detalhando:
- Relevância do conteúdo para fins educacionais. - Relevância do conteúdo para fins educacionais.
- Indicação de faixa etária/segmento dos alunos recomendado. - 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 # 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]`; [Insira a análise pedagógica, os pontos da BNCC e as sugestões de atividades práticas aqui]`;
const aiResponse = await callMinimax({ let reportText = '';
system: "Você é um pedagogo experiente e especialista em planejamento de aulas no Brasil, alinhado à Base Nacional Comum Curricular (BNCC).", const systemInstruction = "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, // Se estiver em fallback, damos preferência ao Gemini pela capacidade de grounding e busca
max_tokens: 3000 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({ res.json({
success: true, success: true,
metadata, metadata,
report: aiResponse.text || '' report: systemNotice + reportText
}); });
} catch (err) { } catch (err) {
console.error('Erro ao processar análise do VideoMind:', err); console.error('Erro ao processar análise do VideoMind:', err);