Blindagem total da geracao de quadrinhos com retries em segundo plano (Alt+Tab), imagem de fallback SVG e preservacao dos quadros gerados
This commit is contained in:
+45
-22
@@ -5069,36 +5069,59 @@ 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) => {
|
||||||
const fullPrompt = `${panel.image_prompt}, high resolution children book illustration, cute Pixar style, ${selectedComicsRatio === '16:9' ? '16:9 aspect ratio' : '4:3 aspect ratio'}`;
|
let anchorPart = '';
|
||||||
|
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 frameResp;
|
let attempts = 0;
|
||||||
try {
|
let frameData = null;
|
||||||
frameResp = await fetch('/api/comics/generate-frame', {
|
|
||||||
method: 'POST',
|
while (attempts < 3 && !frameData) {
|
||||||
headers: { 'Content-Type': 'application/json' },
|
attempts++;
|
||||||
body: JSON.stringify({ prompt: fullPrompt, proporcao: selectedComicsRatio })
|
try {
|
||||||
});
|
const frameResp = await fetch('/api/comics/generate-frame', {
|
||||||
} catch (netErr) {
|
method: 'POST',
|
||||||
throw new Error(`Falha de conexão no quadro ${panel.panel_number}: ${netErr.message}`);
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ prompt: fullPrompt, proporcao: selectedComicsRatio })
|
||||||
|
});
|
||||||
|
|
||||||
|
if (frameResp.ok) {
|
||||||
|
frameData = await frameResp.json();
|
||||||
|
} else {
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
} catch (netErr) {
|
||||||
|
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 (!frameResp.ok) {
|
|
||||||
const errMsg = await safeExtractError(frameResp, 'Erro ao ilustrar quadro');
|
|
||||||
throw new Error(`Erro no quadrinho ${panel.panel_number}: ${errMsg}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const frameData = await frameResp.json();
|
|
||||||
completedCount++;
|
completedCount++;
|
||||||
const progressPercent = 20 + Math.floor((completedCount / totalPanels) * 75);
|
const progressPercent = 20 + Math.floor((completedCount / totalPanels) * 75);
|
||||||
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...`;
|
||||||
|
|
||||||
return {
|
if (frameData && frameData.imageUrl) {
|
||||||
panel_number: panel.panel_number,
|
return {
|
||||||
imageUrl: frameData.imageUrl,
|
panel_number: panel.panel_number,
|
||||||
dialogue: panel.dialogue || '',
|
imageUrl: frameData.imageUrl,
|
||||||
image_prompt: panel.image_prompt || ''
|
dialogue: panel.dialogue || '',
|
||||||
};
|
image_prompt: panel.image_prompt || '',
|
||||||
|
needsRegeneration: !!frameData.needsRegeneration
|
||||||
|
};
|
||||||
|
} 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.`);
|
||||||
|
return {
|
||||||
|
panel_number: panel.panel_number,
|
||||||
|
imageUrl: '/assets/img/placeholder_comic.png',
|
||||||
|
dialogue: panel.dialogue || '',
|
||||||
|
image_prompt: panel.image_prompt || '',
|
||||||
|
needsRegeneration: true
|
||||||
|
};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Executa com no máximo 2 quadros em paralelo para estabilidade máxima e resposta fluida
|
// Executa com no máximo 2 quadros em paralelo para estabilidade máxima e resposta fluida
|
||||||
|
|||||||
@@ -743,7 +743,19 @@ app.post('/api/comics/generate-frame', requireAuth, async (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!imageBuffer) {
|
if (!imageBuffer) {
|
||||||
return res.status(500).json({ error: 'Não foi possível gerar a ilustração do quadro no momento. Por favor, tente novamente.' });
|
console.warn('[Comics Frame] Todas as IAs de imagem falharam temporariamente para o prompt. Gerando placeholder de fallback resiliente.');
|
||||||
|
const is169 = proporcao === '16:9' || (prompt && prompt.includes('16:9'));
|
||||||
|
const w = is169 ? 1280 : 1024;
|
||||||
|
const h = is169 ? 720 : 768;
|
||||||
|
|
||||||
|
const svgFallback = `<svg xmlns="http://www.w3.org/2000/svg" width="${w}" height="${h}" viewBox="0 0 ${w} ${h}">
|
||||||
|
<rect width="100%" height="100%" fill="#1e293b"/>
|
||||||
|
<rect x="20" y="20" width="${w-40}" height="${h-40}" rx="16" fill="none" stroke="#db2777" stroke-width="4" stroke-dasharray="8 8"/>
|
||||||
|
<text x="50%" y="45%" dominant-baseline="middle" text-anchor="middle" fill="#f8fafc" font-family="sans-serif" font-size="28" font-weight="bold">🎨 Quadro Pronto para Ilustrar</text>
|
||||||
|
<text x="50%" y="55%" dominant-baseline="middle" text-anchor="middle" fill="#94a3b8" font-family="sans-serif" font-size="20">Clique no botão 🔄 para gerar a imagem deste quadrinho</text>
|
||||||
|
</svg>`;
|
||||||
|
imageBuffer = Buffer.from(svgFallback, 'utf-8');
|
||||||
|
providerUsed = 'fallback-placeholder';
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -756,7 +768,8 @@ app.post('/api/comics/generate-frame', requireAuth, async (req, res) => {
|
|||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
imageUrl: `/generated-media/${fileName}`,
|
imageUrl: `/generated-media/${fileName}`,
|
||||||
provider: providerUsed
|
provider: providerUsed,
|
||||||
|
needsRegeneration: providerUsed === 'fallback-placeholder'
|
||||||
});
|
});
|
||||||
} catch (saveErr) {
|
} catch (saveErr) {
|
||||||
console.error('Erro ao salvar imagem localmente:', saveErr);
|
console.error('Erro ao salvar imagem localmente:', saveErr);
|
||||||
|
|||||||
Reference in New Issue
Block a user