From 4ba98c1545d2c88551460657b5fce566839ebc6c Mon Sep 17 00:00:00 2001 From: admtracksteel Date: Fri, 5 Jun 2026 19:01:18 +0000 Subject: [PATCH] =?UTF-8?q?Feat/Fix:=20Adiciona=20hist=C3=B3rico=20com=20e?= =?UTF-8?q?xclus=C3=A3o=20para=20o=20Est=C3=BAdio=20Musical=20e=20melhora?= =?UTF-8?q?=20prompt=20de=20voz=20(PT-BR=20infantil=20nativo)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/app.js | 83 +++++++++++++++++++++++++++++++++++++++++++++++ public/index.html | 22 ++++++++++++- public/sw.js | 2 +- server.js | 68 +++++++++++++++++++++++++++++++++++--- 4 files changed, 168 insertions(+), 7 deletions(-) diff --git a/public/app.js b/public/app.js index 8029035..f1f4885 100644 --- a/public/app.js +++ b/public/app.js @@ -3698,6 +3698,89 @@ const initApp = () => { }); } + // ===================================== + // HISTÓRICO DO ESTÚDIO MUSICAL + // ===================================== + const btnOpenEstudioHistory = document.getElementById('btnOpenEstudioHistory'); + const estudioHistoryModal = document.getElementById('estudioHistoryModal'); + const btnCloseEstudioHistory = document.getElementById('btnCloseEstudioHistory'); + const estudioHistoryList = document.getElementById('estudioHistoryList'); + + if (btnOpenEstudioHistory) { + btnOpenEstudioHistory.addEventListener('click', async () => { + estudioHistoryModal.style.display = 'flex'; + estudioHistoryList.innerHTML = '
Carregando histórico...
'; + try { + const res = await fetch('/api/music/list'); + const data = await res.json(); + estudioHistoryList.innerHTML = ''; + if (data.length === 0) { + estudioHistoryList.innerHTML = '
Nenhuma canção salva ainda no estúdio.
'; + return; + } + data.forEach(item => { + const div = document.createElement('div'); + div.style.background = 'var(--bg-tertiary)'; + div.style.padding = '12px 15px'; + div.style.borderRadius = '8px'; + div.style.display = 'flex'; + div.style.justifyContent = 'space-between'; + div.style.alignItems = 'center'; + div.innerHTML = ` +
+
🎵 ${item.tema || 'Música'}
+
Voz: ${item.voz} | Ritmo: ${item.ritmo}
+
${new Date(item.created_at).toLocaleString()}
+
+
+ + +
+ `; + estudioHistoryList.appendChild(div); + }); + + document.querySelectorAll('.btn-ver-estudio').forEach(btn => { + btn.addEventListener('click', async (e) => { + const id = e.target.dataset.id; + try { + const res = await fetch(`/api/music/${id}`); + const itemData = await res.json(); + + musicStudioLyricsArea.textContent = itemData.letra; + musicStudioAudioPlayer.src = itemData.audio_url; + musicStudioDownloadBtn.href = itemData.audio_url; + musicResultArea.style.display = 'flex'; + + estudioHistoryModal.style.display = 'none'; + } catch (err) { alert('Erro ao carregar música do estúdio.'); } + }); + }); + + document.querySelectorAll('.btn-del-estudio').forEach(btn => { + btn.addEventListener('click', async (e) => { + if(!confirm('Tem certeza que deseja apagar esta canção?')) return; + const id = e.target.dataset.id; + try { + await fetch(`/api/music/${id}`, { method: 'DELETE' }); + e.target.closest('div[style*="var(--bg-tertiary)"]').remove(); + } catch (err) { alert('Erro ao deletar.'); } + }); + }); + + } catch (err) { + estudioHistoryList.innerHTML = '
Erro ao carregar histórico.
'; + } + }); + } + + if (btnCloseEstudioHistory) { + btnCloseEstudioHistory.addEventListener('click', () => { + estudioHistoryModal.style.display = 'none'; + }); + } + + // ============================================================ // FÁBRICA DE QUADRINHOS (Criação de histórias visuais pedagógicas) // ============================================================ diff --git a/public/index.html b/public/index.html index 5de7a32..1959dc7 100644 --- a/public/index.html +++ b/public/index.html @@ -655,7 +655,12 @@

🎵 Estúdio Musical PedaGog

- +
+ + +
@@ -736,6 +741,21 @@
+ + + diff --git a/public/sw.js b/public/sw.js index deb80e2..a997081 100644 --- a/public/sw.js +++ b/public/sw.js @@ -1,4 +1,4 @@ -const CACHE_NAME = 'camila-ai-v5'; +const CACHE_NAME = 'camila-ai-v6'; const urlsToCache = [ '/', '/index.html', diff --git a/server.js b/server.js index 41749fe..7241a9a 100644 --- a/server.js +++ b/server.js @@ -220,11 +220,11 @@ app.post('/api/music/generate', requireAuth, async (req, res) => { try { // 1. Determinar o prompt de estilo baseado nos inputs - let voiceDesc = 'female vocals, clear and sweet singing voice'; + let voiceDesc = 'young female vocals, singing in clear native brazilian portuguese accent, no european accent, sweet and warm voice'; if (voz === 'homem') { - voiceDesc = 'male vocals, warm and gentle singing voice'; + voiceDesc = 'male vocals, singing in clear native brazilian portuguese accent, warm and gentle singing voice'; } else if (voz === 'crianca') { - voiceDesc = 'children vocals, sweet child singing voice'; + voiceDesc = 'real young kid vocals, little child singing, boy or girl child voice, playful children voice, native brazilian portuguese kid accent, definitely no adult voices'; } let rhythmDesc = 'happy acoustic children song, simple melody, acoustic guitar'; @@ -238,7 +238,7 @@ app.post('/api/music/generate', requireAuth, async (req, res) => { rhythmDesc = 'marching children rhythm, playful snare drums, acoustic guitar, brass accent'; } - const stylePrompt = `${rhythmDesc}, ${voiceDesc}, educational children song style, singing in Brazilian Portuguese, Brazilian Portuguese vocals, clear Brazilian pronunciation, 100 BPM`; + const stylePrompt = `${rhythmDesc}, ${voiceDesc}, educational children song style, singing exclusively in native Brazilian Portuguese (PT-BR) accent, authentic Brazilian vocals, clear pronunciation`; // 2. Determinar o tamanho das letras baseado na duração (duracao) let lengthInstructions = 'curta, de apenas 6 a 8 versos'; @@ -306,9 +306,16 @@ Retorne APENAS a letra formatada em versos com quebras de linha. Não adicione n const audioUrl = `/generated-media/${fileName}`; // 6. Retornar dados para o frontend + const usuarioId = req.user?.id || '00000000-0000-0000-0000-000000000000'; + const dbRes = await dbPool.query( + `INSERT INTO escola.estudio_musicas (usuario_id, voz, ritmo, tema, letra, audio_url) VALUES ($1, $2, $3, $4, $5, $6) RETURNING id`, + [usuarioId, voz, ritmo, tema, lyrics, audioUrl] + ); + res.json({ success: true, - lyrics: lyrics, + id: dbRes.rows[0].id, + lyrics: lyrics, audioUrl: audioUrl }); @@ -318,6 +325,45 @@ Retorne APENAS a letra formatada em versos com quebras de linha. Não adicione n } }); +// ============================================================ +// ROTAS DE HISTÓRICO DO ESTÚDIO MUSICAL +// ============================================================ +app.get('/api/music/list', requireAuth, async (req, res) => { + const usuarioId = req.user?.id || '00000000-0000-0000-0000-000000000000'; + try { + const dbRes = await dbPool.query(`SELECT id, voz, ritmo, tema, created_at FROM escola.estudio_musicas WHERE usuario_id = $1 ORDER BY created_at DESC`, [usuarioId]); + res.json(dbRes.rows); + } catch (err) { res.status(500).json({ error: 'Erro ao listar histórico.' }); } +}); + +app.get('/api/music/:id', requireAuth, async (req, res) => { + const usuarioId = req.user?.id || '00000000-0000-0000-0000-000000000000'; + try { + const dbRes = await dbPool.query(`SELECT * FROM escola.estudio_musicas WHERE id = $1 AND usuario_id = $2`, [req.params.id, usuarioId]); + if (dbRes.rows.length === 0) return res.status(404).json({ error: 'Não encontrada.' }); + res.json(dbRes.rows[0]); + } catch (err) { res.status(500).json({ error: 'Erro ao buscar.' }); } +}); + +app.delete('/api/music/:id', requireAuth, async (req, res) => { + const usuarioId = req.user?.id || '00000000-0000-0000-0000-000000000000'; + try { + // Apagar também o arquivo físico se quiser + const fileRes = await dbPool.query(`SELECT audio_url FROM escola.estudio_musicas WHERE id = $1 AND usuario_id = $2`, [req.params.id, usuarioId]); + if (fileRes.rows.length > 0 && fileRes.rows[0].audio_url) { + const fs = require('fs'); + const path = require('path'); + const filePath = path.join(__dirname, 'public', fileRes.rows[0].audio_url); + if (fs.existsSync(filePath)) { + fs.unlinkSync(filePath); + } + } + + await dbPool.query(`DELETE FROM escola.estudio_musicas WHERE id = $1 AND usuario_id = $2`, [req.params.id, usuarioId]); + res.json({ success: true }); + } catch (err) { res.status(500).json({ error: 'Erro ao deletar.' }); } +}); + // ============================================================ // ROTAS DO ESTÚDIO: FÁBRICA DE QUADRINHOS // ============================================================ @@ -1770,6 +1816,18 @@ async function initDatabase() { created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); + -- Estúdio Musical (Áudio MiniMax) + CREATE TABLE IF NOT EXISTS escola.estudio_musicas ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + usuario_id UUID NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000', + voz VARCHAR(50), + ritmo VARCHAR(100), + tema TEXT, + letra TEXT, + audio_url TEXT NOT NULL, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() + ); + -- Histórias (Inventando Histórias) CREATE TABLE IF NOT EXISTS escola.historias ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(),