Fix cartazes deletion and media persistence
This commit is contained in:
@@ -82,6 +82,30 @@ const dbPool = new Pool({
|
||||
connectionString: process.env.DATABASE_URL
|
||||
});
|
||||
|
||||
async function backupMediaFile(filePath, buffer) {
|
||||
try {
|
||||
const filename = path.basename(filePath);
|
||||
let contentType = 'application/octet-stream';
|
||||
const ext = path.extname(filename).toLowerCase();
|
||||
if (ext === '.jpg' || ext === '.jpeg') contentType = 'image/jpeg';
|
||||
else if (ext === '.png') contentType = 'image/png';
|
||||
else if (ext === '.gif') contentType = 'image/gif';
|
||||
else if (ext === '.mp3') contentType = 'audio/mpeg';
|
||||
else if (ext === '.mp4') contentType = 'video/mp4';
|
||||
else if (ext === '.wav') contentType = 'audio/wav';
|
||||
|
||||
await dbPool.query(
|
||||
`INSERT INTO escola.arquivos (filename, content_type, dados)
|
||||
VALUES ($1, $2, $3)
|
||||
ON CONFLICT (filename) DO UPDATE SET dados = $3, content_type = $2`,
|
||||
[filename, contentType, buffer]
|
||||
);
|
||||
console.log(`[Media Backup] Backup do arquivo ${filename} salvo no banco escola.arquivos.`);
|
||||
} catch (err) {
|
||||
console.error(`[Media Backup] Erro ao fazer backup do arquivo ${filePath}:`, err);
|
||||
}
|
||||
}
|
||||
|
||||
const CONFIG_FILE_PATH = path.join(__dirname, 'agent_config.json');
|
||||
const KNOWLEDGE_FILE_PATH = path.join(__dirname, 'conhecimento_adquirido.json');
|
||||
const TEMPERATURE_PRESETS = {
|
||||
@@ -302,7 +326,9 @@ Retorne APENAS a letra formatada em versos com quebras de linha. Não adicione n
|
||||
}
|
||||
const fileName = `estudio_musica_${Date.now()}.mp3`;
|
||||
const filePath = path.join(mediaDir, fileName);
|
||||
fs.writeFileSync(filePath, Buffer.from(audioHex, 'hex'));
|
||||
const audioBuffer = Buffer.from(audioHex, 'hex');
|
||||
fs.writeFileSync(filePath, audioBuffer);
|
||||
await backupMediaFile(filePath, audioBuffer);
|
||||
const audioUrl = `/generated-media/${fileName}`;
|
||||
|
||||
// 6. Retornar dados para o frontend
|
||||
@@ -526,6 +552,7 @@ app.post('/api/comics/generate-frame', requireAuth, async (req, res) => {
|
||||
const fileName = `quadrinho_${Date.now()}_${Math.floor(Math.random() * 1000)}.jpg`;
|
||||
const filePath = path.join(mediaDir, fileName);
|
||||
fs.writeFileSync(filePath, imgBuffer);
|
||||
await backupMediaFile(filePath, imgBuffer);
|
||||
|
||||
res.json({ imageUrl: `/generated-media/${fileName}` });
|
||||
} catch (error) {
|
||||
@@ -600,6 +627,16 @@ app.post('/api/comics/generate-video', requireAuth, async (req, res) => {
|
||||
return res.status(500).json({ error: 'Erro ao gerar o vídeo da apresentação. Como o servidor foi atualizado e reiniciado recentemente, por favor crie/gere a história em quadrinhos novamente para recriar as imagens temporárias antes de exportar o vídeo.' });
|
||||
}
|
||||
|
||||
// Backup do vídeo gerado no banco de dados
|
||||
(async () => {
|
||||
try {
|
||||
const videoBuffer = fs.readFileSync(outputFilePath);
|
||||
await backupMediaFile(outputFilePath, videoBuffer);
|
||||
} catch (backupErr) {
|
||||
console.error('[Media Backup] Erro ao fazer backup do vídeo do FFmpeg:', backupErr);
|
||||
}
|
||||
})();
|
||||
|
||||
res.json({ videoUrl: `/generated-media/${outputFileName}` });
|
||||
});
|
||||
|
||||
@@ -909,6 +946,7 @@ app.post('/api/garatujas/analyze', requireAuth, multerUpload.single('image'), as
|
||||
const fileName = `garatuja_${Date.now()}${ext}`;
|
||||
const filePath = path.join(mediaDir, fileName);
|
||||
fs.writeFileSync(filePath, file.buffer);
|
||||
await backupMediaFile(filePath, file.buffer);
|
||||
const imageUrl = `/generated-media/${fileName}`;
|
||||
|
||||
const base64Image = file.buffer.toString('base64');
|
||||
@@ -1448,7 +1486,9 @@ REGRAS:
|
||||
if (imgFetch.ok) {
|
||||
const imgBuffer = Buffer.from(await imgFetch.arrayBuffer());
|
||||
const fileName = `cartaz_${Date.now()}_${Math.floor(Math.random() * 1000)}.jpg`;
|
||||
fs.writeFileSync(path.join(mediaDir, fileName), imgBuffer);
|
||||
const filePath = path.join(mediaDir, fileName);
|
||||
fs.writeFileSync(filePath, imgBuffer);
|
||||
await backupMediaFile(filePath, imgBuffer);
|
||||
generatedImageUrl = `/generated-media/${fileName}`;
|
||||
}
|
||||
}
|
||||
@@ -2063,6 +2103,15 @@ async function initDatabase() {
|
||||
imagem_url TEXT,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Arquivos persistentes (Mídia gerada por IA)
|
||||
CREATE TABLE IF NOT EXISTS escola.arquivos (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
filename VARCHAR(255) UNIQUE NOT NULL,
|
||||
content_type VARCHAR(100) NOT NULL,
|
||||
dados BYTEA NOT NULL,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
`);
|
||||
|
||||
// Garantir que a coluna character_description_english exista na tabela projetos_comics
|
||||
@@ -3229,13 +3278,51 @@ app.use((req, res, next) => {
|
||||
req.path === '/manifest.json' ||
|
||||
req.path === '/sw.js' ||
|
||||
req.path === '/favicon.ico' ||
|
||||
req.path.startsWith('/assets/')
|
||||
req.path.startsWith('/assets/') ||
|
||||
req.path.startsWith('/generated-media/')
|
||||
) {
|
||||
return next();
|
||||
}
|
||||
requireAuth(req, res, next);
|
||||
});
|
||||
|
||||
// Interceptar requisições de mídia gerada para restaurar do banco se sumir do disco
|
||||
app.get('/generated-media/:filename', async (req, res, next) => {
|
||||
const { filename } = req.params;
|
||||
const localPath = path.join(__dirname, 'public', 'generated-media', filename);
|
||||
|
||||
if (fs.existsSync(localPath)) {
|
||||
return next();
|
||||
}
|
||||
|
||||
try {
|
||||
const dbRes = await dbPool.query(
|
||||
`SELECT content_type, dados FROM escola.arquivos WHERE filename = $1`,
|
||||
[filename]
|
||||
);
|
||||
|
||||
if (dbRes.rowCount === 0) {
|
||||
console.log(`[Media Server] Arquivo não encontrado no banco: ${filename}`);
|
||||
return res.status(404).send('Arquivo não encontrado');
|
||||
}
|
||||
|
||||
const { content_type, dados } = dbRes.rows[0];
|
||||
|
||||
const mediaDir = path.join(__dirname, 'public', 'generated-media');
|
||||
if (!fs.existsSync(mediaDir)) {
|
||||
fs.mkdirSync(mediaDir, { recursive: true });
|
||||
}
|
||||
fs.writeFileSync(localPath, dados);
|
||||
console.log(`[Media Server] Arquivo ${filename} carregado do banco de dados e gravado em cache.`);
|
||||
|
||||
res.setHeader('Content-Type', content_type);
|
||||
return res.send(dados);
|
||||
} catch (err) {
|
||||
console.error(`[Media Server] Erro ao carregar ${filename} do banco:`, err);
|
||||
return res.status(500).send('Erro ao carregar arquivo');
|
||||
}
|
||||
});
|
||||
|
||||
// Servir arquivos estáticos do diretório public
|
||||
app.use(express.static(path.join(__dirname, 'public')));
|
||||
|
||||
@@ -3897,8 +3984,9 @@ app.post('/api/chat', requireAuth, async (req, res) => {
|
||||
const fileName = `imagem_${Date.now()}.jpg`;
|
||||
const filePath = path.join(mediaDir, fileName);
|
||||
fs.writeFileSync(filePath, imgBuffer);
|
||||
await backupMediaFile(filePath, imgBuffer);
|
||||
const localUrl = `/generated-media/${fileName}`;
|
||||
console.log(`[Smart Router] Imagem salva em: ${localUrl}`);
|
||||
console.log(`[Smart Router] Imagem salva em e backup no banco: ${localUrl}`);
|
||||
|
||||
res.write(`data: ${JSON.stringify({ type: 'image', url: localUrl, originalPrompt: promptText })}\n\n`);
|
||||
res.write('data: [DONE]\n\n');
|
||||
@@ -3996,9 +4084,11 @@ app.post('/api/chat', requireAuth, async (req, res) => {
|
||||
}
|
||||
const fileName = `musica_${Date.now()}.mp3`;
|
||||
const filePath = path.join(mediaDir, fileName);
|
||||
fs.writeFileSync(filePath, Buffer.from(audioHex, 'hex'));
|
||||
const audioBuffer = Buffer.from(audioHex, 'hex');
|
||||
fs.writeFileSync(filePath, audioBuffer);
|
||||
await backupMediaFile(filePath, audioBuffer);
|
||||
const audioUrl = `/generated-media/${fileName}`;
|
||||
console.log(`[Smart Router] Música salva em: ${audioUrl}`);
|
||||
console.log(`[Smart Router] Música salva em e backup no banco: ${audioUrl}`);
|
||||
|
||||
res.write(`data: ${JSON.stringify({ type: 'audio', url: audioUrl, originalPrompt: promptText })}\n\n`);
|
||||
res.write('data: [DONE]\n\n');
|
||||
@@ -4108,10 +4198,12 @@ app.post('/api/chat', requireAuth, async (req, res) => {
|
||||
|
||||
const fileName = `video_${Date.now()}.mp4`;
|
||||
const filePath = path.join(mediaDir, fileName);
|
||||
fs.writeFileSync(filePath, Buffer.from(videoBuffer));
|
||||
const vBuf = Buffer.from(videoBuffer);
|
||||
fs.writeFileSync(filePath, vBuf);
|
||||
await backupMediaFile(filePath, vBuf);
|
||||
|
||||
const localVideoUrl = `/generated-media/${fileName}`;
|
||||
console.log(`[Smart Router] Vídeo salvo localmente em: ${localVideoUrl}`);
|
||||
console.log(`[Smart Router] Vídeo salvo em e backup no banco: ${localVideoUrl}`);
|
||||
|
||||
res.write(`data: ${JSON.stringify({ type: 'video', url: localVideoUrl, originalPrompt: promptText })}\n\n`);
|
||||
res.write('data: [DONE]\n\n');
|
||||
|
||||
Reference in New Issue
Block a user