Update templates/index.html
Browse files- templates/index.html +76 -12
templates/index.html
CHANGED
@@ -5,6 +5,7 @@
|
|
5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6 |
<title>Générateur de Manga BD</title>
|
7 |
<style>
|
|
|
8 |
* {
|
9 |
margin: 0;
|
10 |
padding: 0;
|
@@ -217,6 +218,31 @@
|
|
217 |
border: 2px solid #68d391;
|
218 |
}
|
219 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
220 |
@media (max-width: 768px) {
|
221 |
.main-content {
|
222 |
grid-template-columns: 1fr;
|
@@ -241,6 +267,7 @@
|
|
241 |
</div>
|
242 |
|
243 |
<div class="main-content">
|
|
|
244 |
<div class="input-section">
|
245 |
<h2>📝 Configuration du Manga</h2>
|
246 |
<textarea
|
@@ -257,7 +284,6 @@
|
|
257 |
🚀 Générer le Manga
|
258 |
</button>
|
259 |
</div>
|
260 |
-
|
261 |
<div class="status-section">
|
262 |
<h2>📊 Statut de Génération</h2>
|
263 |
<div id="statusContainer">
|
@@ -268,7 +294,16 @@
|
|
268 |
</div>
|
269 |
</div>
|
270 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
271 |
|
|
|
272 |
<div class="example-section">
|
273 |
<h2>📋 Format JSON Attendu</h2>
|
274 |
<p style="margin-bottom: 20px;">
|
@@ -289,21 +324,29 @@
|
|
289 |
</div>
|
290 |
|
291 |
<script>
|
|
|
292 |
let currentTaskId = null;
|
293 |
let statusInterval = null;
|
294 |
|
295 |
const generateBtn = document.getElementById('generateBtn');
|
296 |
const jsonInput = document.getElementById('jsonInput');
|
297 |
const statusContainer = document.getElementById('statusContainer');
|
|
|
|
|
298 |
|
299 |
generateBtn.addEventListener('click', async () => {
|
300 |
-
|
301 |
|
|
|
|
|
|
|
|
|
|
|
302 |
if (!jsonText) {
|
303 |
showAlert('Veuillez saisir une configuration JSON', 'error');
|
304 |
return;
|
305 |
}
|
306 |
-
|
307 |
let jsonData;
|
308 |
try {
|
309 |
jsonData = JSON.parse(jsonText);
|
@@ -348,6 +391,7 @@
|
|
348 |
}
|
349 |
});
|
350 |
|
|
|
351 |
function startStatusPolling() {
|
352 |
if (statusInterval) {
|
353 |
clearInterval(statusInterval);
|
@@ -378,6 +422,7 @@
|
|
378 |
}, 2000); // Vérifier toutes les 2 secondes
|
379 |
}
|
380 |
|
|
|
381 |
function updateStatusDisplay(status) {
|
382 |
const container = statusContainer;
|
383 |
|
@@ -396,10 +441,10 @@
|
|
396 |
statusIcon = '🎨';
|
397 |
statusText = 'Génération en cours';
|
398 |
break;
|
399 |
-
case '
|
400 |
statusClass = 'generating';
|
401 |
-
statusIcon = '
|
402 |
-
statusText = 'Création
|
403 |
break;
|
404 |
case 'completed':
|
405 |
statusClass = 'completed';
|
@@ -413,6 +458,7 @@
|
|
413 |
break;
|
414 |
}
|
415 |
|
|
|
416 |
let progressHtml = '';
|
417 |
if (status.total_pages && status.current_page) {
|
418 |
const progress = (status.current_page / status.total_pages) * 100;
|
@@ -431,16 +477,18 @@
|
|
431 |
if (status.status === 'completed') {
|
432 |
downloadHtml = `
|
433 |
<a href="/download/${currentTaskId}" class="download-btn" style="width: 100%; text-align: center; margin-top: 15px;">
|
434 |
-
📥 Télécharger
|
435 |
</a>
|
436 |
`;
|
437 |
}
|
438 |
|
|
|
439 |
let errorHtml = '';
|
440 |
if (status.error) {
|
441 |
errorHtml = `<div class="alert alert-error">${status.error}</div>`;
|
442 |
}
|
443 |
-
|
|
|
444 |
container.innerHTML = `
|
445 |
<div class="status-card ${statusClass}">
|
446 |
<h3>${statusIcon} ${statusText}</h3>
|
@@ -452,15 +500,33 @@
|
|
452 |
${downloadHtml}
|
453 |
</div>
|
454 |
`;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
455 |
}
|
456 |
|
|
|
457 |
function showAlert(message, type) {
|
458 |
const alertClass = type === 'error' ? 'alert-error' : 'alert-success';
|
459 |
const alertHtml = `<div class="alert ${alertClass}">${message}</div>`;
|
460 |
|
461 |
statusContainer.innerHTML = alertHtml + statusContainer.innerHTML;
|
462 |
|
463 |
-
// Retirer l'alerte après 5 secondes
|
464 |
setTimeout(() => {
|
465 |
const alert = statusContainer.querySelector('.alert');
|
466 |
if (alert) {
|
@@ -469,20 +535,18 @@
|
|
469 |
}, 5000);
|
470 |
}
|
471 |
|
472 |
-
// Nettoyer l'intervalle quand la page est fermée
|
473 |
window.addEventListener('beforeunload', () => {
|
474 |
if (statusInterval) {
|
475 |
clearInterval(statusInterval);
|
476 |
}
|
477 |
});
|
478 |
|
479 |
-
// Auto-formatter le JSON
|
480 |
jsonInput.addEventListener('blur', () => {
|
481 |
try {
|
482 |
const parsed = JSON.parse(jsonInput.value);
|
483 |
jsonInput.value = JSON.stringify(parsed, null, 2);
|
484 |
} catch (error) {
|
485 |
-
// Ignorer les erreurs
|
486 |
}
|
487 |
});
|
488 |
</script>
|
|
|
5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6 |
<title>Générateur de Manga BD</title>
|
7 |
<style>
|
8 |
+
/* ... (TOUT VOTRE CSS RESTE IDENTIQUE) ... */
|
9 |
* {
|
10 |
margin: 0;
|
11 |
padding: 0;
|
|
|
218 |
border: 2px solid #68d391;
|
219 |
}
|
220 |
|
221 |
+
/* NOUVEAUX STYLES POUR LA GRILLE D'IMAGES */
|
222 |
+
.preview-section {
|
223 |
+
background: rgba(255, 255, 255, 0.95);
|
224 |
+
padding: 30px;
|
225 |
+
border-radius: 15px;
|
226 |
+
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
|
227 |
+
margin-top: 30px;
|
228 |
+
display: none; /* Caché par défaut */
|
229 |
+
}
|
230 |
+
|
231 |
+
.image-grid {
|
232 |
+
display: grid;
|
233 |
+
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
234 |
+
gap: 20px;
|
235 |
+
}
|
236 |
+
|
237 |
+
.image-grid img {
|
238 |
+
width: 100%;
|
239 |
+
height: auto;
|
240 |
+
border-radius: 10px;
|
241 |
+
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
|
242 |
+
object-fit: cover;
|
243 |
+
aspect-ratio: 2 / 3; /* Ratio commun pour les pages de manga */
|
244 |
+
}
|
245 |
+
|
246 |
@media (max-width: 768px) {
|
247 |
.main-content {
|
248 |
grid-template-columns: 1fr;
|
|
|
267 |
</div>
|
268 |
|
269 |
<div class="main-content">
|
270 |
+
<!-- ... (La section input reste la même) ... -->
|
271 |
<div class="input-section">
|
272 |
<h2>📝 Configuration du Manga</h2>
|
273 |
<textarea
|
|
|
284 |
🚀 Générer le Manga
|
285 |
</button>
|
286 |
</div>
|
|
|
287 |
<div class="status-section">
|
288 |
<h2>📊 Statut de Génération</h2>
|
289 |
<div id="statusContainer">
|
|
|
294 |
</div>
|
295 |
</div>
|
296 |
</div>
|
297 |
+
|
298 |
+
<!-- NOUVELLE SECTION POUR L'AFFICHAGE DES IMAGES -->
|
299 |
+
<div id="previewSection" class="preview-section">
|
300 |
+
<h2>🖼️ Prévisualisation des Pages</h2>
|
301 |
+
<div id="imageGrid" class="image-grid">
|
302 |
+
<!-- Les images générées apparaîtront ici -->
|
303 |
+
</div>
|
304 |
+
</div>
|
305 |
|
306 |
+
<!-- ... (La section exemple reste la même) ... -->
|
307 |
<div class="example-section">
|
308 |
<h2>📋 Format JSON Attendu</h2>
|
309 |
<p style="margin-bottom: 20px;">
|
|
|
324 |
</div>
|
325 |
|
326 |
<script>
|
327 |
+
// ... (Les variables globales restent les mêmes) ...
|
328 |
let currentTaskId = null;
|
329 |
let statusInterval = null;
|
330 |
|
331 |
const generateBtn = document.getElementById('generateBtn');
|
332 |
const jsonInput = document.getElementById('jsonInput');
|
333 |
const statusContainer = document.getElementById('statusContainer');
|
334 |
+
const previewSection = document.getElementById('previewSection');
|
335 |
+
const imageGrid = document.getElementById('imageGrid');
|
336 |
|
337 |
generateBtn.addEventListener('click', async () => {
|
338 |
+
// ... (Cette fonction reste quasi identique, on ajoute juste un reset de l'affichage)
|
339 |
|
340 |
+
// Reset de l'interface
|
341 |
+
imageGrid.innerHTML = '';
|
342 |
+
previewSection.style.display = 'none';
|
343 |
+
|
344 |
+
const jsonText = jsonInput.value.trim();
|
345 |
if (!jsonText) {
|
346 |
showAlert('Veuillez saisir une configuration JSON', 'error');
|
347 |
return;
|
348 |
}
|
349 |
+
// ... (Le reste du code de la fonction est inchangé)
|
350 |
let jsonData;
|
351 |
try {
|
352 |
jsonData = JSON.parse(jsonText);
|
|
|
391 |
}
|
392 |
});
|
393 |
|
394 |
+
// La fonction startStatusPolling reste la même
|
395 |
function startStatusPolling() {
|
396 |
if (statusInterval) {
|
397 |
clearInterval(statusInterval);
|
|
|
422 |
}, 2000); // Vérifier toutes les 2 secondes
|
423 |
}
|
424 |
|
425 |
+
// MISE À JOUR MAJEURE DE CETTE FONCTION
|
426 |
function updateStatusDisplay(status) {
|
427 |
const container = statusContainer;
|
428 |
|
|
|
441 |
statusIcon = '🎨';
|
442 |
statusText = 'Génération en cours';
|
443 |
break;
|
444 |
+
case 'creating_zip': // STATUT MIS À JOUR
|
445 |
statusClass = 'generating';
|
446 |
+
statusIcon = '🗜️'; // Icône de compression
|
447 |
+
statusText = 'Création de l\'archive ZIP';
|
448 |
break;
|
449 |
case 'completed':
|
450 |
statusClass = 'completed';
|
|
|
458 |
break;
|
459 |
}
|
460 |
|
461 |
+
// ... (La logique de la barre de progression reste la même)
|
462 |
let progressHtml = '';
|
463 |
if (status.total_pages && status.current_page) {
|
464 |
const progress = (status.current_page / status.total_pages) * 100;
|
|
|
477 |
if (status.status === 'completed') {
|
478 |
downloadHtml = `
|
479 |
<a href="/download/${currentTaskId}" class="download-btn" style="width: 100%; text-align: center; margin-top: 15px;">
|
480 |
+
📥 Télécharger l'archive ZIP
|
481 |
</a>
|
482 |
`;
|
483 |
}
|
484 |
|
485 |
+
// ... (Le HTML pour l'erreur reste le même)
|
486 |
let errorHtml = '';
|
487 |
if (status.error) {
|
488 |
errorHtml = `<div class="alert alert-error">${status.error}</div>`;
|
489 |
}
|
490 |
+
|
491 |
+
// Affichage du statut
|
492 |
container.innerHTML = `
|
493 |
<div class="status-card ${statusClass}">
|
494 |
<h3>${statusIcon} ${statusText}</h3>
|
|
|
500 |
${downloadHtml}
|
501 |
</div>
|
502 |
`;
|
503 |
+
|
504 |
+
// NOUVELLE PARTIE : Affichage des images
|
505 |
+
if (status.image_urls && status.image_urls.length > 0) {
|
506 |
+
previewSection.style.display = 'block';
|
507 |
+
|
508 |
+
status.image_urls.forEach(url => {
|
509 |
+
// Vérifier si l'image n'est pas déjà affichée
|
510 |
+
if (!document.querySelector(`img[src="${url}"]`)) {
|
511 |
+
const img = document.createElement('img');
|
512 |
+
img.src = url;
|
513 |
+
img.alt = `Page générée pour la tâche ${currentTaskId}`;
|
514 |
+
img.onload = () => img.style.opacity = 1; // Effet d'apparition
|
515 |
+
img.style.opacity = 0;
|
516 |
+
img.style.transition = 'opacity 0.5s';
|
517 |
+
imageGrid.appendChild(img);
|
518 |
+
}
|
519 |
+
});
|
520 |
+
}
|
521 |
}
|
522 |
|
523 |
+
// ... (Le reste du script JS reste identique)
|
524 |
function showAlert(message, type) {
|
525 |
const alertClass = type === 'error' ? 'alert-error' : 'alert-success';
|
526 |
const alertHtml = `<div class="alert ${alertClass}">${message}</div>`;
|
527 |
|
528 |
statusContainer.innerHTML = alertHtml + statusContainer.innerHTML;
|
529 |
|
|
|
530 |
setTimeout(() => {
|
531 |
const alert = statusContainer.querySelector('.alert');
|
532 |
if (alert) {
|
|
|
535 |
}, 5000);
|
536 |
}
|
537 |
|
|
|
538 |
window.addEventListener('beforeunload', () => {
|
539 |
if (statusInterval) {
|
540 |
clearInterval(statusInterval);
|
541 |
}
|
542 |
});
|
543 |
|
|
|
544 |
jsonInput.addEventListener('blur', () => {
|
545 |
try {
|
546 |
const parsed = JSON.parse(jsonInput.value);
|
547 |
jsonInput.value = JSON.stringify(parsed, null, 2);
|
548 |
} catch (error) {
|
549 |
+
// Ignorer les erreurs
|
550 |
}
|
551 |
});
|
552 |
</script>
|