Multiple Folder Image Minipulation

I have created an image tool which crops, scales, resizes. I now want to create a bulk image feature where it will take multiple folders with images inside, extract the images and then creates the a new folder with the original file name and put’s the processed images back into the folders created.

I’m 95% there with creating this tool but I’m stuck on trying to process multiple folders which is where it’s going wrong.

I’ve managed to get it so I can process one folder, extract the images, create a new folder with the original file name and put the processed image back into that folder.

I’m stuck on what to do next. I have tried searching the problem but I think my situation is a bit niche so I didn’t find much. So if anyone has a solution that would be much appreciated.

My specific question is:
How can I modify my current code to correctly process multiple folders, extract the images, and place the processed images back into the respective new folders?

document.addEventListener('DOMContentLoaded', function() {
  const folderInput = document.getElementById('folderInput');
  folderInput.addEventListener('change', function() {
    updateSelectedFilesPreview('folderInput', 'bulk_selectedFilesPreview');
    console.log('Folder input changed. Files selected:', folderInput.files.length);
  });
});

function processFolderImages() {
  const folderInput = document.getElementById('folderInput');
  const output = document.getElementById('bulk_output');
  const finalSize = 2000;
  const maxScaleSize = 1900;
  const compressionQuality = 0.5;

  const zip = new JSZip();
  const promises = [];

  const oldButton = document.querySelector('.download-bulkimg-button');
  if (oldButton) {
    output.removeChild(oldButton);
  }

  const folderMap = {};

  // Iterate through the selected files and categorize them by their folder paths
  for (let i = 0; i < folderInput.files.length; i++) {
    const file = folderInput.files[i];
    const filePath = file.webkitRelativePath || file.relativePath || file.name;
    const folderPath = filePath.substring(0, filePath.lastIndexOf('/'));

    console.log(`Processing file: ${file.name}`);
    console.log(`File path: ${filePath}`);
    console.log(`Folder path: ${folderPath}`);

    if (!folderMap[folderPath]) {
      folderMap[folderPath] = [];
    }

    folderMap[folderPath].push(file);
  }

  console.log('Folder map:', folderMap);

  for (const folderPath in folderMap) {
    const files = folderMap[folderPath];
    const folder = zip.folder(folderPath);

    console.log(`Processing folder: ${folderPath}`);

    files.forEach(file => {
      const reader = new FileReader();

      promises.push(
        new Promise((resolve) => {
          reader.onload = function(e) {
            console.log(`Reading file: ${file.name}`);
            const img = new Image();
            img.src = e.target.result;
            img.onload = function() {
              console.log(`Image loaded: ${file.name}`);
              const cropCanvas = document.createElement('canvas');
              const cropCtx = cropCanvas.getContext('2d');
              const {
                left,
                top,
                width,
                height
              } = getBoundingRectangle(img);

              cropCanvas.width = width;
              cropCanvas.height = height;
              cropCtx.drawImage(img, left, top, width, height, 0, 0, width, height);

              const scaleCanvas = document.createElement('canvas');
              const scaleCtx = scaleCanvas.getContext('2d');
              let scaleWidth, scaleHeight;
              if (width > height) {
                scaleWidth = maxScaleSize;
                scaleHeight = (height / width) * maxScaleSize;
              } else {
                scaleWidth = (width / height) * maxScaleSize;
                scaleHeight = maxScaleSize;
              }

              scaleCanvas.width = scaleWidth;
              scaleCanvas.height = scaleHeight;
              scaleCtx.drawImage(cropCanvas, 0, 0, width, height, 0, 0, scaleWidth, scaleHeight);

              const canvas = document.createElement('canvas');
              const ctx = canvas.getContext('2d');
              const canvasSize = finalSize;
              canvas.width = canvasSize;
              canvas.height = canvasSize;

              let sourceWidth, sourceHeight;
              if (scaleWidth > scaleHeight) {
                sourceWidth = canvasSize;
                sourceHeight = (scaleHeight / scaleWidth) * canvasSize;
              } else {
                sourceWidth = (scaleWidth / scaleHeight) * canvasSize;
                sourceHeight = canvasSize;
              }

              const xOffset = (canvasSize - sourceWidth) / 2;
              const yOffset = (canvasSize - sourceHeight) / 2;

              ctx.fillStyle = 'white';
              ctx.fillRect(0, 0, canvasSize, canvasSize);
              ctx.drawImage(scaleCanvas, 0, 0, scaleWidth, scaleHeight, xOffset, yOffset, sourceWidth, sourceHeight);

              canvas.toBlob(function(blob) {
                console.log(`Image processing complete: ${file.name}`);
                compressImage(blob, compressionQuality, function(compressedBlob) {
                  folder.file(file.name, compressedBlob);
                  resolve();
                });
              }, 'image/jpeg', 1);
            };
          };

          reader.readAsDataURL(file);
        })
      );
    });
  }

  Promise.all(promises).then(() => {
    console.log('All images processed. Generating ZIP...');
    if (!document.querySelector('.download-folder-img-button')) {
      zip.generateAsync({
        type: 'blob'
      }).then(function(content) {
        console.log('ZIP generated.');
        const button = document.createElement('button');
        button.innerText = 'Download';
        button.classList.add('download-bulkimg-button');
        button.addEventListener('click', function(event) {
          event.preventDefault();
          const a = document.createElement('a');
          a.href = URL.createObjectURL(content);
          a.download = 'processed-images.zip';
          a.style.display = 'none';
          document.body.appendChild(a);
          a.click();
          document.body.removeChild(a);
        });
        output.appendChild(button);
      }).catch(error => {
        console.error('Error generating ZIP:', error);
        output.innerHTML = 'An error occurred while generating the ZIP.';
      });
    }
  }).catch(error => {
    console.error('Error processing images:', error);
    output.innerHTML = 'An error occurred during processing.';
  });
}

function getBoundingRectangle(img) {
  const offscreenCanvas = document.createElement('canvas');
  const offscreenCtx = offscreenCanvas.getContext('2d');
  offscreenCanvas.width = img.width;
  offscreenCanvas.height = img.height;
  offscreenCtx.drawImage(img, 0, 0);

  const imageData = offscreenCtx.getImageData(0, 0, offscreenCanvas.width, offscreenCanvas.height);
  const data = imageData.data;

  let left = offscreenCanvas.width;
  let top = offscreenCanvas.height;
  let right = 0;
  let bottom = 0;

  for (let y = 0; y < offscreenCanvas.height; y++) {
    for (let x = 0; x < offscreenCanvas.width; x++) {
      const index = (y * offscreenCanvas.width + x) * 4;
      if (data[index] !== 255 || data[index + 1] !== 255 || data[index + 2] !== 255) {
        left = Math.min(left, x);
        right = Math.max(right, x);
        top = Math.min(top, y);
        bottom = Math.max(bottom, y);
      }
    }
  }

  const width = right - left;
  const height = bottom - top;

  console.log(`Bounding rectangle - left: ${left}, top: ${top}, width: ${width}, height: ${height}`);

  return {
    left,
    top,
    width,
    height
  };
}

function compressImage(blob, quality, callback) {
  const img = new Image();
  img.src = URL.createObjectURL(blob);
  img.onload = function() {
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');
    canvas.width = img.width;
    canvas.height = img.height;
    ctx.drawImage(img, 0, 0);
    canvas.toBlob(function(compressedBlob) {
      console.log('Image compressed.');
      callback(compressedBlob);
    }, 'image/jpeg', quality);
  };
}
<div id="bulkContent" class="tab-content">
  <div class="imageContent-centrecontainer" style="margin-left: 150px;">
    <p class="header-card">Bulk Image Tool</p>
    <div class="imagecontent-container">
      <div class="imageform-container">
        <form>
          <div class="custom-input-container">
            <!-- Updated input for folder selection -->
            <label for="folderInput" class="custom-button">
                                <i class="fa-solid fa-cloud-arrow-down" style="display: grid; justify-content: center;"></i>
                                Drag &amp; Drop Or Select
                            </label>
            <div id="fileList" class="file-list"></div>
            <!-- Folder input -->
            <input type="file" id="folderInput" class="custom-input" webkitdirectory multiple>
          </div>
          <button class="custom-button-process" type="button" onclick="processFolderImages()">Process Images</button>
          <div id="bulk_output"></div>
        </form>
      </div>
    </div>
  </div>
  <div class="imgdisplay-container">
    <div class="imgpreview-container" id="dragAndDropArea">
      <div id="bulk_selectedFilesPreview">

      </div>
    </div>
  </div>
</div>