PWA QR Scanner doesn't work

What I’m trying to do:
I am trying to control my mobile devices camera when the website is installed as a PWA on the home screen. The video feed of the camera is displayed fine when used on a browser, but when the web app is added to the home screen, the feed will no longer appear and an error pops up.

What I’ve tried and what’s not working:

I tried using the forum popular version here:

When this didn’t work I created my own version:

to gain a better understanding of whats going on. Unfortunately the same issue arises and did didn’t help me much.

Here is the console error, which isn’t much help:

: AbortError: The operation was aborted.

Clone link:
share a copy of your app

I have a work around.

First on the html side, I check to see if the video is streaming.

  function startScanner(callbackName) {
    navigator.mediaDevices
      .getUserMedia({ video: { facingMode: "environment" } })
      .then(function(stream) {
        qrResult.hidden = true;
        video.setAttribute("playsinline", true); // required to tell iOS safari we don't want fullscreen
        video.srcObject = stream;
        video.play();

        // Check if the video starts playing
        videoTimeout = setTimeout(() => {
          if (video.readyState !== 4) { // 4 means 'HAVE_ENOUGH_DATA'
            handleNoVideo(); // Trigger alternative function
          }
        }, 5000); // Wait 5 seconds to check if video starts

        video.addEventListener("playing", () => {
          clearTimeout(videoTimeout); // Clear the timeout if video is playing
          const scanInterval = setInterval(() => {
            if (!scanning) {
              clearInterval(scanInterval); // Stop scanning if the flag is false
              return;
            }
            canvasElement.width = video.videoWidth;
            canvasElement.height = video.videoHeight;
            canvas.drawImage(video, 0, 0, canvasElement.width, canvasElement.height);

            const imageData = canvas.getImageData(0, 0, canvasElement.width, canvasElement.height);
            const code = jsQR(imageData.data, canvasElement.width, canvasElement.height);

            if (code) {
              qrCodeCallback(code.data, callbackName);
              scanning = false;
            }
          }, 500); // Scan every 500ms
        });
      })
      .catch(() => {
        handleNoVideo(); // Handle if the user denies camera access
      });
  }

  function handleNoVideo() {
    alert("Unable to access the camera. Please check your device settings.");
    anvil.call(qrResult,'upload_file')
  }

Then I have a file upload button where you can upload an image using the camera and process that image instead.

Python

  def upload_file(self):
    self.button_scan_qr.visible = False
    self.file_loader.visible = True

  def button_scan_qr_click(self, **event_args):
    """This method is called when the button is clicked"""
    self.start_scanning()

  def file_loader_change(self, file, **event_args):
    """This method is called when a new file is loaded into this FileLoader"""
    if file.content_type.startswith("image/"):
      # Read the file as a URL-safe base64 string
      new_file = anvil.image.generate_thumbnail(file, 300) #phones will high definition photos overwhelm the QR processing code
      file_data = new_file.get_bytes()
      base64_data = base64.b64encode(file_data).decode("utf-8")
      
      # Create a Data URL with the Base64 string
      data_url = f"data:{file.content_type};base64,{base64_data}"
      # Pass the base64 string to JavaScript for processing
      anvil.js.call_js("processImage", data_url)
    else:
        alert("Please upload a valid image file.")

Javascript


  function processImage(base64Image) {
      // const canvasElement = document.createElement("canvas");
      // const canvas = canvasElement.getContext("2d");
  
      const img = new Image();
      img.onload = () => {
        // Resize the canvas to match the image dimensions
        canvasElement.width = img.width;
        canvasElement.height = img.height;
  
        // Draw the image onto the canvas
        canvas.drawImage(img, 0, 0, img.width, img.height);
  
        // Extract image data from the canvas
        const imageData = canvas.getImageData(0, 0, img.width, img.height);
  
        // Pass the image data to jsQR
        const code = jsQR(imageData.data, img.width, img.height);
        if (code) {
          alert(`QR Code Data: ${code.data}`); // Display the QR code data
        } else {
          alert("No QR code found in the image.");
        }
      };
      img.src = base64Image; // Set the image source to the base64 string
    }

Cheers!