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 @@
+
+
+
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(),