bibibi12345 commited on
Commit
3eb2198
·
1 Parent(s): 74c8c36

change from base64 to file upload

Browse files
Files changed (2) hide show
  1. static/script.js +164 -11
  2. static/style.css +67 -0
static/script.js CHANGED
@@ -230,16 +230,23 @@ async function handleFileUpload(event) {
230
  event.target.value = '';
231
  }
232
 
233
- // Add image preview
234
  function addImagePreview(src, index) {
235
  const previewItem = document.createElement('div');
236
  previewItem.className = 'image-preview-item';
 
237
  const imageId = `upload-img-${Date.now()}-${index}`;
238
  previewItem.innerHTML = `
239
  <img id="${imageId}" src="${src}" alt="Upload ${index + 1}"
240
  onclick="openImageModal('${imageId}', '${src}', 'Uploaded Image ${index + 1}', 'Input Image')"
241
  style="cursor: pointer;">
242
  <button class="remove-btn" onclick="removeImage(${index})">×</button>
 
 
 
 
 
 
243
  `;
244
  imagePreview.appendChild(previewItem);
245
  }
@@ -368,9 +375,34 @@ function getImageSize() {
368
  return size;
369
  }
370
 
371
- // Upload image to FAL storage
372
- async function uploadImageToFal(imageData, apiKey) {
373
  try {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
374
  const response = await fetch('/api/upload-to-fal', {
375
  method: 'POST',
376
  headers: {
@@ -386,46 +418,146 @@ async function uploadImageToFal(imageData, apiKey) {
386
  }
387
 
388
  const data = await response.json();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
389
  return data.url;
390
  } catch (error) {
391
  console.error('Error uploading to FAL:', error);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
392
  throw error;
393
  }
394
  }
395
 
396
- // Prepare image URLs for API
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
397
  async function getImageUrlsForAPI() {
398
  const urls = [];
399
  const apiKey = getAPIKey();
400
 
 
 
 
 
 
 
 
 
 
 
 
 
 
401
  // Process uploaded base64 images - upload to FAL first
 
402
  for (let i = 0; i < uploadedImages.length; i++) {
403
  const imageData = uploadedImages[i];
404
 
405
  // If it's a base64 data URL, upload to FAL
406
  if (imageData.startsWith('data:')) {
 
407
  try {
408
- addLog(`Uploading image ${i + 1} to FAL storage...`);
409
- const falUrl = await uploadImageToFal(imageData, apiKey);
410
  urls.push(falUrl);
411
- addLog(`Image ${i + 1} uploaded successfully`);
 
 
 
 
 
412
  } catch (error) {
413
- addLog(`Failed to upload image ${i + 1}: ${error.message}`);
414
  throw error;
415
  }
416
  } else {
417
  // Already a URL, use as-is
418
  urls.push(imageData);
 
419
  }
420
  }
421
 
422
  // Add text URLs directly
423
- const textUrls = imageUrls.value.trim().split('\n').filter(url => url.trim());
 
 
 
424
  for (const url of textUrls) {
425
  urls.push(url);
 
426
  await getImageDimensionsFromUrl(url);
427
  }
428
 
 
 
 
 
 
429
  return urls.slice(0, 10);
430
  }
431
 
@@ -470,13 +602,34 @@ async function generateEdit() {
470
  generateBtn.querySelector('.spinner').style.display = 'block';
471
 
472
  // Clear current results
473
- currentResults.innerHTML = '<div class="empty-state"><p>Generating...</p></div>';
474
  currentInfo.innerHTML = '';
475
  clearLogs();
476
 
477
- showStatus('Preparing images and connecting to FAL API...', 'info');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
478
  progressLogs.classList.add('active');
479
 
 
 
 
 
 
480
  const requestData = {
481
  prompt: prompt,
482
  image_size: getImageSize(),
 
230
  event.target.value = '';
231
  }
232
 
233
+ // Add image preview with upload status indicator
234
  function addImagePreview(src, index) {
235
  const previewItem = document.createElement('div');
236
  previewItem.className = 'image-preview-item';
237
+ previewItem.dataset.imageIndex = index;
238
  const imageId = `upload-img-${Date.now()}-${index}`;
239
  previewItem.innerHTML = `
240
  <img id="${imageId}" src="${src}" alt="Upload ${index + 1}"
241
  onclick="openImageModal('${imageId}', '${src}', 'Uploaded Image ${index + 1}', 'Input Image')"
242
  style="cursor: pointer;">
243
  <button class="remove-btn" onclick="removeImage(${index})">×</button>
244
+ <div class="image-upload-status" style="display: none;">
245
+ <div class="upload-progress-mini">
246
+ <div class="upload-progress-mini-bar"></div>
247
+ </div>
248
+ <span class="upload-status-text"></span>
249
+ </div>
250
  `;
251
  imagePreview.appendChild(previewItem);
252
  }
 
375
  return size;
376
  }
377
 
378
+ // Upload image to FAL storage with progress tracking
379
+ async function uploadImageToFal(imageData, apiKey, imageIndex, totalImages, actualIndex) {
380
  try {
381
+ // Show individual image upload status if preview exists
382
+ const previewItem = document.querySelector(`.image-preview-item[data-image-index="${actualIndex}"]`);
383
+ if (previewItem) {
384
+ const statusDiv = previewItem.querySelector('.image-upload-status');
385
+ const statusText = previewItem.querySelector('.upload-status-text');
386
+ const progressBar = previewItem.querySelector('.upload-progress-mini-bar');
387
+
388
+ if (statusDiv) {
389
+ statusDiv.style.display = 'block';
390
+ statusText.textContent = 'Uploading...';
391
+ progressBar.style.width = '30%';
392
+ previewItem.classList.add('uploading');
393
+ }
394
+ }
395
+
396
+ // Update main progress bar
397
+ updateUploadProgress(imageIndex - 1, totalImages, `Uploading image ${imageIndex}/${totalImages}...`);
398
+
399
+ // Show upload start message
400
+ addLog(`Uploading image ${imageIndex}/${totalImages} to FAL storage...`);
401
+
402
+ // Calculate approximate size for logging
403
+ const sizeInMB = (imageData.length * 0.75 / 1024 / 1024).toFixed(2);
404
+ addLog(`Image ${imageIndex} size: ~${sizeInMB} MB`);
405
+
406
  const response = await fetch('/api/upload-to-fal', {
407
  method: 'POST',
408
  headers: {
 
418
  }
419
 
420
  const data = await response.json();
421
+ addLog(`✓ Image ${imageIndex}/${totalImages} uploaded successfully`);
422
+
423
+ // Update progress after successful upload
424
+ updateUploadProgress(imageIndex, totalImages, `Completed ${imageIndex}/${totalImages}`);
425
+
426
+ // Update individual image status for success
427
+ if (previewItem) {
428
+ const statusText = previewItem.querySelector('.upload-status-text');
429
+ const progressBar = previewItem.querySelector('.upload-progress-mini-bar');
430
+
431
+ if (progressBar && statusText) {
432
+ progressBar.style.width = '100%';
433
+ statusText.textContent = 'Uploaded ✓';
434
+ previewItem.classList.remove('uploading');
435
+ previewItem.classList.add('uploaded');
436
+
437
+ // Fade out status after 3 seconds
438
+ setTimeout(() => {
439
+ const statusDiv = previewItem.querySelector('.image-upload-status');
440
+ if (statusDiv) {
441
+ statusDiv.style.opacity = '0';
442
+ setTimeout(() => {
443
+ statusDiv.style.display = 'none';
444
+ statusDiv.style.opacity = '1';
445
+ previewItem.classList.remove('uploaded');
446
+ }, 300);
447
+ }
448
+ }, 3000);
449
+ }
450
+ }
451
+
452
  return data.url;
453
  } catch (error) {
454
  console.error('Error uploading to FAL:', error);
455
+ addLog(`✗ Failed to upload image ${imageIndex}: ${error.message}`);
456
+
457
+ // Update individual image status for error
458
+ if (previewItem) {
459
+ const statusText = previewItem.querySelector('.upload-status-text');
460
+ const progressBar = previewItem.querySelector('.upload-progress-mini-bar');
461
+
462
+ if (progressBar && statusText) {
463
+ progressBar.style.width = '100%';
464
+ progressBar.style.backgroundColor = '#dc3545';
465
+ statusText.textContent = 'Upload failed ✗';
466
+ previewItem.classList.remove('uploading');
467
+ previewItem.classList.add('upload-failed');
468
+ }
469
+ }
470
+
471
  throw error;
472
  }
473
  }
474
 
475
+ // Update upload progress bar
476
+ function updateUploadProgress(completed, total, message) {
477
+ const progressFill = document.getElementById('uploadProgressFill');
478
+ const progressText = document.getElementById('uploadProgressText');
479
+
480
+ if (progressFill && progressText) {
481
+ const percentage = Math.round((completed / total) * 100);
482
+ progressFill.style.width = `${percentage}%`;
483
+ progressText.textContent = `${message} (${percentage}%)`;
484
+
485
+ // Add animation class
486
+ if (percentage === 100) {
487
+ progressFill.classList.add('complete');
488
+ setTimeout(() => {
489
+ const container = document.getElementById('uploadProgressContainer');
490
+ if (container) {
491
+ container.style.opacity = '0';
492
+ setTimeout(() => container.remove(), 500);
493
+ }
494
+ }, 1500);
495
+ }
496
+ }
497
+ }
498
+
499
+ // Prepare image URLs for API with detailed progress tracking
500
  async function getImageUrlsForAPI() {
501
  const urls = [];
502
  const apiKey = getAPIKey();
503
 
504
+ // Count total images to upload
505
+ const base64Images = uploadedImages.filter(img => img.startsWith('data:'));
506
+ const urlImages = uploadedImages.filter(img => !img.startsWith('data:'));
507
+ const textUrls = imageUrls.value.trim().split('\n').filter(url => url.trim());
508
+
509
+ const totalUploads = base64Images.length;
510
+ const totalImages = uploadedImages.length + textUrls.length;
511
+
512
+ if (totalUploads > 0) {
513
+ addLog(`Preparing to upload ${totalUploads} image(s) to FAL storage...`);
514
+ showStatus(`Uploading ${totalUploads} image(s) to FAL storage...`, 'info');
515
+ }
516
+
517
  // Process uploaded base64 images - upload to FAL first
518
+ let uploadCount = 0;
519
  for (let i = 0; i < uploadedImages.length; i++) {
520
  const imageData = uploadedImages[i];
521
 
522
  // If it's a base64 data URL, upload to FAL
523
  if (imageData.startsWith('data:')) {
524
+ uploadCount++;
525
  try {
526
+ const falUrl = await uploadImageToFal(imageData, apiKey, uploadCount, totalUploads, i);
 
527
  urls.push(falUrl);
528
+
529
+ // Update progress status
530
+ if (uploadCount < totalUploads) {
531
+ const percentage = Math.round((uploadCount / totalUploads) * 100);
532
+ showStatus(`Upload progress: ${uploadCount}/${totalUploads} (${percentage}%)`, 'info');
533
+ }
534
  } catch (error) {
535
+ showStatus(`Upload failed for image ${uploadCount}: ${error.message}`, 'error');
536
  throw error;
537
  }
538
  } else {
539
  // Already a URL, use as-is
540
  urls.push(imageData);
541
+ addLog(`Using existing URL for image ${i + 1}`);
542
  }
543
  }
544
 
545
  // Add text URLs directly
546
+ if (textUrls.length > 0) {
547
+ addLog(`Processing ${textUrls.length} URL(s) from text input...`);
548
+ }
549
+
550
  for (const url of textUrls) {
551
  urls.push(url);
552
+ addLog(`Added URL: ${url.substring(0, 50)}...`);
553
  await getImageDimensionsFromUrl(url);
554
  }
555
 
556
+ if (totalUploads > 0) {
557
+ showStatus(`All ${totalUploads} image(s) uploaded successfully!`, 'success');
558
+ addLog(`Upload complete: ${totalImages} total image(s) ready for generation`);
559
+ }
560
+
561
  return urls.slice(0, 10);
562
  }
563
 
 
602
  generateBtn.querySelector('.spinner').style.display = 'block';
603
 
604
  // Clear current results
605
+ currentResults.innerHTML = '<div class="empty-state"><p>Preparing generation...</p></div>';
606
  currentInfo.innerHTML = '';
607
  clearLogs();
608
 
609
+ // Create and show progress bar container
610
+ const progressContainer = document.createElement('div');
611
+ progressContainer.id = 'uploadProgressContainer';
612
+ progressContainer.className = 'upload-progress-container';
613
+ progressContainer.innerHTML = `
614
+ <div class="upload-progress-bar">
615
+ <div class="upload-progress-fill" id="uploadProgressFill"></div>
616
+ </div>
617
+ <div class="upload-progress-text" id="uploadProgressText">Initializing...</div>
618
+ `;
619
+
620
+ // Insert progress container after status message
621
+ if (statusMessage.parentNode) {
622
+ statusMessage.parentNode.insertBefore(progressContainer, statusMessage.nextSibling);
623
+ }
624
+
625
+ showStatus('Starting generation process...', 'info');
626
  progressLogs.classList.add('active');
627
 
628
+ // Show initial status
629
+ if (!isTextToImage && imageUrlsArray.length > 0) {
630
+ addLog(`Processing ${imageUrlsArray.length} input image(s)...`);
631
+ }
632
+
633
  const requestData = {
634
  prompt: prompt,
635
  image_size: getImageSize(),
static/style.css CHANGED
@@ -400,6 +400,21 @@ body {
400
  transition: all 0.3s ease;
401
  }
402
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
403
  .image-preview-item:hover {
404
  border-color: #764ba2;
405
  transform: scale(1.05);
@@ -441,6 +456,58 @@ body {
441
  transform: scale(1.1);
442
  }
443
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
444
  /* Loading Preview */
445
  .loading-preview {
446
  display: flex;
 
400
  transition: all 0.3s ease;
401
  }
402
 
403
+ .image-preview-item.uploading {
404
+ border-color: #007bff;
405
+ box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.1);
406
+ }
407
+
408
+ .image-preview-item.uploaded {
409
+ border-color: #28a745;
410
+ box-shadow: 0 0 0 2px rgba(40, 167, 69, 0.1);
411
+ }
412
+
413
+ .image-preview-item.upload-failed {
414
+ border-color: #dc3545;
415
+ box-shadow: 0 0 0 2px rgba(220, 53, 69, 0.1);
416
+ }
417
+
418
  .image-preview-item:hover {
419
  border-color: #764ba2;
420
  transform: scale(1.05);
 
456
  transform: scale(1.1);
457
  }
458
 
459
+ /* Individual image upload status */
460
+ .image-upload-status {
461
+ position: absolute;
462
+ bottom: 0;
463
+ left: 0;
464
+ right: 0;
465
+ background: rgba(255, 255, 255, 0.95);
466
+ padding: 4px;
467
+ font-size: 10px;
468
+ text-align: center;
469
+ transition: opacity 0.3s ease;
470
+ z-index: 3;
471
+ }
472
+
473
+ .upload-progress-mini {
474
+ height: 3px;
475
+ background: #e9ecef;
476
+ border-radius: 2px;
477
+ overflow: hidden;
478
+ margin-bottom: 2px;
479
+ }
480
+
481
+ .upload-progress-mini-bar {
482
+ height: 100%;
483
+ background: linear-gradient(90deg, #007bff, #0056b3);
484
+ border-radius: 2px;
485
+ transition: width 0.3s ease;
486
+ width: 0;
487
+ }
488
+
489
+ .uploaded .upload-progress-mini-bar {
490
+ background: linear-gradient(90deg, #28a745, #218838);
491
+ }
492
+
493
+ .upload-failed .upload-progress-mini-bar {
494
+ background: linear-gradient(90deg, #dc3545, #c82333);
495
+ }
496
+
497
+ .upload-status-text {
498
+ display: block;
499
+ color: #495057;
500
+ font-weight: 500;
501
+ }
502
+
503
+ .uploaded .upload-status-text {
504
+ color: #28a745;
505
+ }
506
+
507
+ .upload-failed .upload-status-text {
508
+ color: #dc3545;
509
+ }
510
+
511
  /* Loading Preview */
512
  .loading-preview {
513
  display: flex;