Corrige prompt cinematografico de quadros, remove rate limit na geracao de 10 quadros e blinda modais contra clique fora
This commit is contained in:
+10
-105
@@ -2843,13 +2843,6 @@ const initApp = () => {
|
|||||||
btnExitRecord.addEventListener('click', closeRecordModal);
|
btnExitRecord.addEventListener('click', closeRecordModal);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fechar clicando fora
|
|
||||||
if (recordModal) {
|
|
||||||
recordModal.addEventListener('click', (e) => {
|
|
||||||
if (e.target === recordModal) closeRecordModal();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// MODAL: MINHAS OBSERVAÇÕES
|
// MODAL: MINHAS OBSERVAÇÕES
|
||||||
// ============================================================
|
// ============================================================
|
||||||
@@ -2914,11 +2907,6 @@ const initApp = () => {
|
|||||||
obsModal.style.display = 'none';
|
obsModal.style.display = 'none';
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (obsModal) {
|
|
||||||
obsModal.addEventListener('click', (e) => {
|
|
||||||
if (e.target === obsModal) obsModal.style.display = 'none';
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filtros
|
// Filtros
|
||||||
if (obsFilterMes) obsFilterMes.addEventListener('change', renderObsList);
|
if (obsFilterMes) obsFilterMes.addEventListener('change', renderObsList);
|
||||||
@@ -3317,11 +3305,6 @@ const initApp = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (btnCloseMinhaTurmaModal) btnCloseMinhaTurmaModal.addEventListener('click', () => minhaTurmaModal.style.display = 'none');
|
if (btnCloseMinhaTurmaModal) btnCloseMinhaTurmaModal.addEventListener('click', () => minhaTurmaModal.style.display = 'none');
|
||||||
if (minhaTurmaModal) {
|
|
||||||
minhaTurmaModal.addEventListener('click', (e) => {
|
|
||||||
if (e.target === minhaTurmaModal) minhaTurmaModal.style.display = 'none';
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (btnCreateChild) btnCreateChild.addEventListener('click', clearChildForm);
|
if (btnCreateChild) btnCreateChild.addEventListener('click', clearChildForm);
|
||||||
if (btnCancelChild) btnCancelChild.addEventListener('click', clearChildForm);
|
if (btnCancelChild) btnCancelChild.addEventListener('click', clearChildForm);
|
||||||
@@ -3507,11 +3490,6 @@ Se algum dado não for mencionado, deixe a string vazia ou false para boolean.`
|
|||||||
modelosRelatorioModal.style.display = 'none';
|
modelosRelatorioModal.style.display = 'none';
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (modelosRelatorioModal) {
|
|
||||||
modelosRelatorioModal.addEventListener('click', (e) => {
|
|
||||||
if (e.target === modelosRelatorioModal) modelosRelatorioModal.style.display = 'none';
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Buscar modelos do backend
|
// Buscar modelos do backend
|
||||||
async function fetchTemplates() {
|
async function fetchTemplates() {
|
||||||
@@ -3737,11 +3715,6 @@ Se algum dado não for mencionado, deixe a string vazia ou false para boolean.`
|
|||||||
emitirRelatorioModal.style.display = 'none';
|
emitirRelatorioModal.style.display = 'none';
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (emitirRelatorioModal) {
|
|
||||||
emitirRelatorioModal.addEventListener('click', (e) => {
|
|
||||||
if (e.target === emitirRelatorioModal) emitirRelatorioModal.style.display = 'none';
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Buscar crianças com base nos filtros de Ano e Turma selecionados
|
// Buscar crianças com base nos filtros de Ano e Turma selecionados
|
||||||
async function loadChildrenList() {
|
async function loadChildrenList() {
|
||||||
@@ -4152,14 +4125,6 @@ Se algum dado não for mencionado, deixe a string vazia ou false para boolean.`
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fechar modal clicando fora
|
|
||||||
window.addEventListener('click', (e) => {
|
|
||||||
if (e.target === estudioMusicalModal) {
|
|
||||||
estudioMusicalModal.style.display = 'none';
|
|
||||||
musicStudioAudioPlayer.pause();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Ação de gerar música no estúdio
|
// Ação de gerar música no estúdio
|
||||||
if (btnGenerateStudioMusic) {
|
if (btnGenerateStudioMusic) {
|
||||||
btnGenerateStudioMusic.addEventListener('click', async () => {
|
btnGenerateStudioMusic.addEventListener('click', async () => {
|
||||||
@@ -5069,11 +5034,7 @@ Se algum dado não for mencionado, deixe a string vazia ou false para boolean.`
|
|||||||
let completedCount = 0;
|
let completedCount = 0;
|
||||||
|
|
||||||
const generateSinglePanel = async (panel) => {
|
const generateSinglePanel = async (panel) => {
|
||||||
let anchorPart = '';
|
const fullPrompt = `${panel.image_prompt}, 3D Pixar Disney style, digital children book illustration, vibrant colors, masterpiece, cinematic lighting, ${selectedComicsRatio === '16:9' ? '16:9 aspect ratio' : '4:3 aspect ratio'}`;
|
||||||
if (currentComicsCharDescEnglish) {
|
|
||||||
anchorPart = `CHARACTER ANCHOR: [${currentComicsCharDescEnglish}]. `;
|
|
||||||
}
|
|
||||||
const fullPrompt = `${anchorPart}${panel.image_prompt}, high resolution children book illustration, cute Pixar style, ${selectedComicsRatio === '16:9' ? '16:9 aspect ratio' : '4:3 aspect ratio'}`;
|
|
||||||
|
|
||||||
let attempts = 0;
|
let attempts = 0;
|
||||||
let frameData = null;
|
let frameData = null;
|
||||||
@@ -5091,11 +5052,11 @@ Se algum dado não for mencionado, deixe a string vazia ou false para boolean.`
|
|||||||
frameData = await frameResp.json();
|
frameData = await frameResp.json();
|
||||||
} else {
|
} else {
|
||||||
console.warn(`[Retry Comics] Tentativa ${attempts} para quadro ${panel.panel_number} respondeu ${frameResp.status}`);
|
console.warn(`[Retry Comics] Tentativa ${attempts} para quadro ${panel.panel_number} respondeu ${frameResp.status}`);
|
||||||
if (attempts < 3) await new Promise(r => setTimeout(r, 1200 * attempts));
|
if (attempts < 3) await new Promise(r => setTimeout(r, 1500 * attempts));
|
||||||
}
|
}
|
||||||
} catch (netErr) {
|
} catch (netErr) {
|
||||||
console.warn(`[Retry Comics] Tentativa ${attempts} erro de rede no quadro ${panel.panel_number}:`, netErr.message);
|
console.warn(`[Retry Comics] Tentativa ${attempts} erro de rede no quadro ${panel.panel_number}:`, netErr.message);
|
||||||
if (attempts < 3) await new Promise(r => setTimeout(r, 1200 * attempts));
|
if (attempts < 3) await new Promise(r => setTimeout(r, 1500 * attempts));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5104,6 +5065,9 @@ Se algum dado não for mencionado, deixe a string vazia ou false para boolean.`
|
|||||||
if (comicsLoaderProgress) comicsLoaderProgress.style.width = `${progressPercent}%`;
|
if (comicsLoaderProgress) comicsLoaderProgress.style.width = `${progressPercent}%`;
|
||||||
if (comicsLoaderText) comicsLoaderText.textContent = `🎨 Ilustrando quadrinhos: ${completedCount}/${totalPanels} quadros concluídos...`;
|
if (comicsLoaderText) comicsLoaderText.textContent = `🎨 Ilustrando quadrinhos: ${completedCount}/${totalPanels} quadros concluídos...`;
|
||||||
|
|
||||||
|
// Pequeno respiro entre gerações para respeitar a taxa da API
|
||||||
|
await new Promise(r => setTimeout(r, 600));
|
||||||
|
|
||||||
if (frameData && frameData.imageUrl) {
|
if (frameData && frameData.imageUrl) {
|
||||||
return {
|
return {
|
||||||
panel_number: panel.panel_number,
|
panel_number: panel.panel_number,
|
||||||
@@ -5113,7 +5077,7 @@ Se algum dado não for mencionado, deixe a string vazia ou false para boolean.`
|
|||||||
needsRegeneration: !!frameData.needsRegeneration
|
needsRegeneration: !!frameData.needsRegeneration
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
console.warn(`[Comics Resiliencia] Quadro ${panel.panel_number} não pôde ser gerado após 3 tentativas. Preservando a história e liberando botão de regenerar.`);
|
console.warn(`[Comics Resiliencia] Quadro ${panel.panel_number} em espera. Preservando a história.`);
|
||||||
return {
|
return {
|
||||||
panel_number: panel.panel_number,
|
panel_number: panel.panel_number,
|
||||||
imageUrl: '/assets/img/placeholder_comic.png',
|
imageUrl: '/assets/img/placeholder_comic.png',
|
||||||
@@ -5124,8 +5088,8 @@ Se algum dado não for mencionado, deixe a string vazia ou false para boolean.`
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Executa com no máximo 2 quadros em paralelo para estabilidade máxima e resposta fluida
|
// Executa com 1 quadro por vez sequencial para estabilidade absoluta e sem estourar rate limit
|
||||||
const newPanels = await mapConcurrent(scriptData.panels, 2, generateSinglePanel);
|
const newPanels = await mapConcurrent(scriptData.panels, 1, generateSinglePanel);
|
||||||
|
|
||||||
// Ordena por panel_number para garantir a sequência correta
|
// Ordena por panel_number para garantir a sequência correta
|
||||||
newPanels.sort((a, b) => a.panel_number - b.panel_number);
|
newPanels.sort((a, b) => a.panel_number - b.panel_number);
|
||||||
@@ -5343,12 +5307,7 @@ Se algum dado não for mencionado, deixe a string vazia ou false para boolean.`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Monta o prompt reforçado com a Âncora Visual Física de Personagens e Estilo 3D Pixar
|
const fullPrompt = `${promptToUse}, 3D Pixar Disney style, digital children book illustration, vibrant colors, masterpiece, cinematic lighting, ${selectedComicsRatio === '16:9' ? '16:9 aspect ratio' : '4:3 aspect ratio'}`;
|
||||||
let anchorPart = '';
|
|
||||||
if (currentComicsCharDescEnglish) {
|
|
||||||
anchorPart = `CHARACTER ANCHOR: [${currentComicsCharDescEnglish}]. `;
|
|
||||||
}
|
|
||||||
const fullPrompt = `${anchorPart}${promptToUse}, high resolution children book illustration, cute Pixar style, ${selectedComicsRatio === '16:9' ? '16:9 aspect ratio' : '4:3 aspect ratio'}`;
|
|
||||||
|
|
||||||
const resp = await fetch('/api/comics/generate-frame', {
|
const resp = await fetch('/api/comics/generate-frame', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@@ -5844,12 +5803,6 @@ Se algum dado não for mencionado, deixe a string vazia ou false para boolean.`
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener('click', (e) => {
|
|
||||||
if (e.target === videoMindModal) {
|
|
||||||
videoMindModal.style.display = 'none';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Ação de analisar vídeo
|
// Ação de analisar vídeo
|
||||||
if (btnGenerateVideoMind) {
|
if (btnGenerateVideoMind) {
|
||||||
btnGenerateVideoMind.addEventListener('click', async () => {
|
btnGenerateVideoMind.addEventListener('click', async () => {
|
||||||
@@ -6096,12 +6049,6 @@ Se algum dado não for mencionado, deixe a string vazia ou false para boolean.`
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener('click', (e) => {
|
|
||||||
if (e.target === garatujasModal) {
|
|
||||||
garatujasModal.style.display = 'none';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Upload trigger
|
// Upload trigger
|
||||||
if (btnUploadGaratuja) {
|
if (btnUploadGaratuja) {
|
||||||
btnUploadGaratuja.addEventListener('click', () => {
|
btnUploadGaratuja.addEventListener('click', () => {
|
||||||
@@ -6534,13 +6481,6 @@ Se algum dado não for mencionado, deixe a string vazia ou false para boolean.`
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
poesiaModal.addEventListener('click', (e) => {
|
|
||||||
if (e.target === poesiaModal) {
|
|
||||||
poesiaModal.style.display = 'none';
|
|
||||||
if (poesiaAudioPlayer) poesiaAudioPlayer.pause();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Aplicar Template
|
// Aplicar Template
|
||||||
const applyPoesiaTemplate = (key) => {
|
const applyPoesiaTemplate = (key) => {
|
||||||
const tmpl = POESIA_TEMPLATES[key];
|
const tmpl = POESIA_TEMPLATES[key];
|
||||||
@@ -6867,12 +6807,6 @@ Se algum dado não for mencionado, deixe a string vazia ou false para boolean.`
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
poesiaHistoryModal.addEventListener('click', (e) => {
|
|
||||||
if (e.target === poesiaHistoryModal) {
|
|
||||||
poesiaHistoryModal.style.display = 'none';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
async function loadPoesiaHistory() {
|
async function loadPoesiaHistory() {
|
||||||
const listContainer = document.getElementById('poesiaHistoryList');
|
const listContainer = document.getElementById('poesiaHistoryList');
|
||||||
if (!listContainer) return;
|
if (!listContainer) return;
|
||||||
@@ -7038,12 +6972,6 @@ Se algum dado não for mencionado, deixe a string vazia ou false para boolean.`
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener('click', (e) => {
|
|
||||||
if (e.target === brincarAtivoModal) {
|
|
||||||
brincarAtivoModal.style.display = 'none';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Gerar Atividade
|
// Gerar Atividade
|
||||||
if (btnGenerateBrincarAtivo) {
|
if (btnGenerateBrincarAtivo) {
|
||||||
btnGenerateBrincarAtivo.addEventListener('click', async () => {
|
btnGenerateBrincarAtivo.addEventListener('click', async () => {
|
||||||
@@ -7473,13 +7401,6 @@ window.toggleCustomAudio = (btn) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
musicaModal.addEventListener('click', (e) => {
|
|
||||||
if (e.target === musicaModal) {
|
|
||||||
musicaModal.style.display = 'none';
|
|
||||||
if (musicaAudioPlayer) musicaAudioPlayer.pause();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Aplicar Template
|
// Aplicar Template
|
||||||
const applyMusicaTemplate = (key) => {
|
const applyMusicaTemplate = (key) => {
|
||||||
const tmpl = MUSICA_TEMPLATES[key];
|
const tmpl = MUSICA_TEMPLATES[key];
|
||||||
@@ -7774,12 +7695,6 @@ window.toggleCustomAudio = (btn) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
musicaHistoryModal.addEventListener('click', (e) => {
|
|
||||||
if (e.target === musicaHistoryModal) {
|
|
||||||
musicaHistoryModal.style.display = 'none';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
async function loadMusicaHistory() {
|
async function loadMusicaHistory() {
|
||||||
const listContainer = document.getElementById('musicaHistoryList');
|
const listContainer = document.getElementById('musicaHistoryList');
|
||||||
if (!listContainer) return;
|
if (!listContainer) return;
|
||||||
@@ -8053,7 +7968,6 @@ window.toggleCustomAudio = (btn) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (btnCloseHistoriaModal) {
|
if (btnCloseHistoriaModal) {
|
||||||
historiaModal.addEventListener('click', (e) => { if (e.target === historiaModal) historiaModal.style.display = 'none'; });
|
|
||||||
btnCloseHistoriaModal.addEventListener('click', () => historiaModal.style.display = 'none');
|
btnCloseHistoriaModal.addEventListener('click', () => historiaModal.style.display = 'none');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -8065,7 +7979,6 @@ window.toggleCustomAudio = (btn) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (btnCloseHistoriaHistory) btnCloseHistoriaHistory.addEventListener('click', () => historiaHistoryModal.style.display = 'none');
|
if (btnCloseHistoriaHistory) btnCloseHistoriaHistory.addEventListener('click', () => historiaHistoryModal.style.display = 'none');
|
||||||
historiaHistoryModal.addEventListener('click', (e) => { if (e.target === historiaHistoryModal) historiaHistoryModal.style.display = 'none'; });
|
|
||||||
|
|
||||||
// Alternância de Abas
|
// Alternância de Abas
|
||||||
function switchHistoriaTab(tab) {
|
function switchHistoriaTab(tab) {
|
||||||
@@ -8612,9 +8525,6 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
btnCloseMindLabModal.addEventListener('click', () => {
|
btnCloseMindLabModal.addEventListener('click', () => {
|
||||||
mindLabModal.style.display = 'none';
|
mindLabModal.style.display = 'none';
|
||||||
});
|
});
|
||||||
mindLabModal.addEventListener('click', (e) => {
|
|
||||||
if (e.target === mindLabModal) mindLabModal.style.display = 'none';
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (btnGenerateMindLab) {
|
if (btnGenerateMindLab) {
|
||||||
@@ -11024,7 +10934,6 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
let currentTimelineAlunoId = null;
|
let currentTimelineAlunoId = null;
|
||||||
|
|
||||||
if (btnCloseAlunoTimeline) btnCloseAlunoTimeline.addEventListener('click', () => alunoTimelineModal.style.display = 'none');
|
if (btnCloseAlunoTimeline) btnCloseAlunoTimeline.addEventListener('click', () => alunoTimelineModal.style.display = 'none');
|
||||||
if (alunoTimelineModal) alunoTimelineModal.addEventListener('click', (e) => { if (e.target === alunoTimelineModal) alunoTimelineModal.style.display = 'none'; });
|
|
||||||
|
|
||||||
window.viewChildTimeline = async (alunoId) => {
|
window.viewChildTimeline = async (alunoId) => {
|
||||||
currentTimelineAlunoId = alunoId;
|
currentTimelineAlunoId = alunoId;
|
||||||
@@ -11196,7 +11105,6 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
const btnProcessCsvImport = document.getElementById('btnProcessCsvImport');
|
const btnProcessCsvImport = document.getElementById('btnProcessCsvImport');
|
||||||
|
|
||||||
if (btnCloseCsvImport) btnCloseCsvImport.addEventListener('click', () => csvImportModal.style.display = 'none');
|
if (btnCloseCsvImport) btnCloseCsvImport.addEventListener('click', () => csvImportModal.style.display = 'none');
|
||||||
if (csvImportModal) csvImportModal.addEventListener('click', (e) => { if (e.target === csvImportModal) csvImportModal.style.display = 'none'; });
|
|
||||||
|
|
||||||
// Botão abrir CSV import (se presente na tela de turmas ou sidebar)
|
// Botão abrir CSV import (se presente na tela de turmas ou sidebar)
|
||||||
window.openCsvImportModal = () => {
|
window.openCsvImportModal = () => {
|
||||||
@@ -11299,7 +11207,6 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (btnCloseCriseModal) btnCloseCriseModal.addEventListener('click', () => criseModal.style.display = 'none');
|
if (btnCloseCriseModal) btnCloseCriseModal.addEventListener('click', () => criseModal.style.display = 'none');
|
||||||
if (criseModal) criseModal.addEventListener('click', (e) => { if (e.target === criseModal) criseModal.style.display = 'none'; });
|
|
||||||
|
|
||||||
if (btnOpenCrisesHistory) {
|
if (btnOpenCrisesHistory) {
|
||||||
btnOpenCrisesHistory.addEventListener('click', async () => {
|
btnOpenCrisesHistory.addEventListener('click', async () => {
|
||||||
@@ -11437,7 +11344,6 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (btnCloseAtaModal) btnCloseAtaModal.addEventListener('click', () => ataModal.style.display = 'none');
|
if (btnCloseAtaModal) btnCloseAtaModal.addEventListener('click', () => ataModal.style.display = 'none');
|
||||||
if (ataModal) ataModal.addEventListener('click', (e) => { if (e.target === ataModal) ataModal.style.display = 'none'; });
|
|
||||||
|
|
||||||
if (btnGerarAta) {
|
if (btnGerarAta) {
|
||||||
btnGerarAta.addEventListener('click', async () => {
|
btnGerarAta.addEventListener('click', async () => {
|
||||||
@@ -11617,7 +11523,6 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (btnCloseStickerModal) btnCloseStickerModal.addEventListener('click', () => stickerModal.style.display = 'none');
|
if (btnCloseStickerModal) btnCloseStickerModal.addEventListener('click', () => stickerModal.style.display = 'none');
|
||||||
if (stickerModal) stickerModal.addEventListener('click', (e) => { if (e.target === stickerModal) stickerModal.style.display = 'none'; });
|
|
||||||
|
|
||||||
window.deleteStickerRecord = async (id) => {
|
window.deleteStickerRecord = async (id) => {
|
||||||
if (confirm('Deseja excluir este sticker?')) {
|
if (confirm('Deseja excluir este sticker?')) {
|
||||||
|
|||||||
@@ -513,57 +513,48 @@ app.post('/api/comics/generate-script', requireAuth, async (req, res) => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
let prompt = '';
|
let prompt = '';
|
||||||
if (existingPanels && Array.isArray(existingPanels) && existingPanels.length > 0) {
|
const isContinuation = existingPanels && Array.isArray(existingPanels) && existingPanels.length > 0 && continuationType && continuationType !== 'new';
|
||||||
const startPanelNum = continuationType === 'extend' ? existingPanels.length + 1 : 1;
|
const startPanelNum = (existingPanels && Array.isArray(existingPanels)) ? existingPanels.length + 1 : 1;
|
||||||
|
|
||||||
prompt = `Você está continuando ou se baseando em um projeto de história em quadrinhos existente.
|
if (isContinuation) {
|
||||||
DADOS DO PROJETO EXISTENTE:
|
prompt = `Continue a história em quadrinhos pedagógica infantil com o tema: "${tema}".
|
||||||
- Descrição visual física e de estilo constante dos personagens (em inglês): "${character_description || ''}"
|
A história deve conter mais ${quantidade} novos quadrinhos dando continuidade cronológica aos anteriores.
|
||||||
- Painéis já existentes (roteiro anterior):
|
Baloes de falas: ${baloes ? 'Sim, cada quadrinho deve conter um diálogo/fala conciso e expressivo, contendo no máximo 12 palavras no total.' : 'Não, a história é silenciosa, sem falas, apenas a ação dos personagens'}.
|
||||||
${existingPanels.map(p => ` * Quadrinho ${p.panel_number || p.panelNumber}: [Visual] ${p.image_prompt || p.imagePrompt || 'N/A'} | [Fala/Legenda] ${p.dialogue}`).join('\n')}
|
|
||||||
|
|
||||||
INSTRUÇÕES DE GERAÇÃO:
|
|
||||||
Tipo de continuidade: ${continuationType === 'extend' ? 'Estender a mesma história (continuação cronológica direta, criando novos painéis sequenciais)' : 'Criar uma nova história separada (spin-off com os mesmos personagens, recomeçando do painel 1)'}.
|
|
||||||
Tema/Objetivo da nova fase: "${tema}".
|
|
||||||
Gere exatamente ${quantidade} novos quadrinhos.
|
|
||||||
Baloes de falas: ${baloes ? 'Sim, cada quadrinho deve conter um diálogo/fala extremamente conciso e curto, contendo no máximo 10 palavras no total.' : 'Não, a história é silenciosa, sem falas, apenas a descrição da cena'}.
|
|
||||||
Proporção visual: ${proporcao}.
|
Proporção visual: ${proporcao}.
|
||||||
Cenário/Local principal da nova fase: ${cenario}.
|
Cenário/Local principal da nova fase: ${cenario}.
|
||||||
|
|
||||||
Gere um JSON perfeitamente válido contendo:
|
Gere um JSON perfeitamente válido contendo:
|
||||||
{
|
{
|
||||||
"title": "${continuationType === 'extend' ? 'Manter o título original ou propor um subtítulo condizente' : 'Novo Título da História'}",
|
"title": "${continuationType === 'extend' ? 'Manter o título original ou propor um subtítulo condizente' : 'Novo Título da História'}",
|
||||||
"character_description": "${character_description || 'Descrição física rigorosamente constante e fixa dos personagens em inglês (roupas fixas, cabelos, cores)'}",
|
"character_description": "${character_description || 'Descrição física fixa dos personagens em inglês (roupas fixas, cabelos, cores)'}",
|
||||||
"panels": [
|
"panels": [
|
||||||
{
|
{
|
||||||
"panel_number": ${startPanelNum},
|
"panel_number": ${startPanelNum},
|
||||||
"image_prompt": "Prompt visual minucioso em inglês para o gerador de imagens. REGRA RÍGIDA DE CONSISTÊNCIA: Substitua nomes de personagens por sua descrição física EXATA e FIXA em inglês (do character_description) em TODOS os quadrinhos onde aparecerem (mesmas roupas, mesma cor de cabelo, mesmas características). Descreva a ação específica do quadrinho mantendo o mesmo estilo visual 3D Pixar.",
|
"image_prompt": "Cena cinematográfica completa em inglês em estilo 3D Pixar/Disney: descreva a ação dos personagens com suas roupas e traços físicos exatos inseridos organicamente no cenário (${cenario}), com iluminação mágica, cores vibrantes e atmosfera infantil encantadora.",
|
||||||
"dialogue": "Falas do quadrinho em português (ex: 'Lucas: Veja aquilo!') com NO MÁXIMO 10 palavras, ou narração curta caso não tenha balões."
|
"dialogue": "Falas do quadrinho em português (ex: 'Lucas: Veja aquilo!') com NO MÁXIMO 12 palavras, ou narração curta caso não tenha balões."
|
||||||
},
|
}
|
||||||
...
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
Responda APENAS o JSON válido, sem qualquer texto explicativo antes ou depois.`;
|
Responda APENAS o JSON válido, sem qualquer texto explicativo antes ou depois.`;
|
||||||
} else {
|
} else {
|
||||||
prompt = `Crie uma história em quadrinhos pedagógica infantil com o tema: "${tema}".
|
prompt = `Crie uma história em quadrinhos pedagógica infantil rica e envolvente com o tema: "${tema}".
|
||||||
A história deve conter exatamente ${quantidade} quadrinhos.
|
A história deve conter exatamente ${quantidade} quadrinhos.
|
||||||
Baloes de falas: ${baloes ? 'Sim, cada quadrinho deve conter um diálogo/fala extremamente conciso e curto, contendo no máximo 10 palavras no total.' : 'Não, a história é silenciosa, sem falas, apenas a descrição da cena'}.
|
Baloes de falas: ${baloes ? 'Sim, cada quadrinho deve conter um diálogo/fala conciso e expressivo, contendo no máximo 12 palavras no total.' : 'Não, a história é silenciosa, sem falas, apenas a descrição da cena'}.
|
||||||
Proporção visual: ${proporcao}.
|
Proporção visual: ${proporcao}.
|
||||||
Personagens participantes: ${JSON.stringify(personagens)}.
|
Personagens participantes: ${JSON.stringify(personagens)}.
|
||||||
Cenário/Local principal: ${cenario}.
|
Cenário/Local principal: ${cenario}.
|
||||||
|
|
||||||
Gere um JSON perfeitamente válido contendo:
|
Gere um JSON perfeitamente válido contendo:
|
||||||
{
|
{
|
||||||
"title": "Título da História",
|
"title": "Título Criativo da História",
|
||||||
"character_description": "Descrição física rigorosamente detalhada e fixa dos personagens em inglês (especifique roupas fixas, cores de camiseta/vestido, tipo/cor de cabelo, tom de pele e características físicas fixas para NUNCA mudarem entre os quadrinhos).",
|
"character_description": "Descrição física detalhada e fixa dos personagens em inglês (ex: Lucas: a cheerful 6-year-old boy with short brown hair wearing a bright red t-shirt and blue shorts. Mariana: a 5-year-old girl with blonde pigtails in a yellow dress).",
|
||||||
"panels": [
|
"panels": [
|
||||||
{
|
{
|
||||||
"panel_number": 1,
|
"panel_number": 1,
|
||||||
"image_prompt": "Prompt visual minucioso em inglês para o gerador de imagens da MiniMax. REGRA RÍGIDA DE CONSISTÊNCIA: Substitua TODOS os nomes de personagens por sua descrição física EXATA e FIXA em inglês (do character_description) em TODOS os quadrinhos em que aparecerem (mesmas roupas fixas, cabelos e características de forma idêntica em 100% das imagens). Descreva a cena específica mantendo a arte 3D Pixar infantil de traço limpo.",
|
"image_prompt": "Cena cinematográfica completa em inglês em estilo 3D Pixar/Disney: descreva a ação dos personagens com suas roupas e traços físicos exatos inseridos vividamente DENTRO do cenário principal (${cenario}), com cores vibrantes, iluminação mágica e atmosfera infantil encantadora.",
|
||||||
"dialogue": "Falas do quadrinho em português (ex: 'Lucas: Veja aquilo!') com NO MÁXIMO 10 palavras, ou narração curta caso não tenha balões."
|
"dialogue": "Falas do quadrinho em português (ex: 'Lucas: Vamos explorar a floresta!') com NO MÁXIMO 12 palavras, ou narração curta caso não tenha balões."
|
||||||
},
|
}
|
||||||
...
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -571,10 +562,10 @@ Responda APENAS o JSON válido, sem qualquer texto explicativo antes ou depois.`
|
|||||||
}
|
}
|
||||||
|
|
||||||
const r = await callMinimax({
|
const r = await callMinimax({
|
||||||
system: "Você é um roteirista pedagógico especialista em histórias em quadrinhos infantis para educação básica. Você responde apenas com estruturas JSON válidas.",
|
system: "Você é um renomado diretor de arte e roteirista de histórias em quadrinhos infantis 3D estilo Pixar Disney. Você sempre descreve cenas ricas com personagens e cenários integrados naturalmente em inglês.",
|
||||||
messages: [{ role: 'user', content: prompt }],
|
messages: [{ role: 'user', content: prompt }],
|
||||||
temperature: 0.3,
|
temperature: 0.4,
|
||||||
max_tokens: 3000
|
max_tokens: 3500
|
||||||
});
|
});
|
||||||
|
|
||||||
let jsonText = r.text;
|
let jsonText = r.text;
|
||||||
@@ -652,9 +643,6 @@ app.post('/api/comics/generate-frame', requireAuth, async (req, res) => {
|
|||||||
fs.mkdirSync(mediaDir, { recursive: true });
|
fs.mkdirSync(mediaDir, { recursive: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
const fileName = `quadrinho_${Date.now()}_${Math.floor(Math.random() * 1000)}.jpg`;
|
|
||||||
const filePath = path.join(mediaDir, fileName);
|
|
||||||
|
|
||||||
let imageBuffer = null;
|
let imageBuffer = null;
|
||||||
let providerUsed = 'minimax';
|
let providerUsed = 'minimax';
|
||||||
|
|
||||||
@@ -758,6 +746,10 @@ app.post('/api/comics/generate-frame', requireAuth, async (req, res) => {
|
|||||||
providerUsed = 'fallback-placeholder';
|
providerUsed = 'fallback-placeholder';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fileExt = (providerUsed === 'fallback-placeholder') ? 'svg' : 'jpg';
|
||||||
|
const fileName = `quadrinho_${Date.now()}_${Math.floor(Math.random() * 1000)}.${fileExt}`;
|
||||||
|
const filePath = path.join(mediaDir, fileName);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
fs.writeFileSync(filePath, imageBuffer);
|
fs.writeFileSync(filePath, imageBuffer);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user