diff --git a/server.js b/server.js index 1852e80..15f8a6b 100644 --- a/server.js +++ b/server.js @@ -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);