How to upload list of videos in FastAPI using JavaScript/ReactJS and process them with OpenCV?
Asked Answered
A

1

0

I'm trying to switch from uploading a single video to uploading and processing multiple videos; however, it seems that my code saves/reads the first video only. I can't seem to figure out why, as when I print the list of files uploaded, it does include all the subsequent videos that get ignored.

Frontend: ReactJS enter image description here

Backend: FastAPI

This is what the code looks like in the backend:

@app.post("/upload")
def upload_video(fileList: Optional[List[UploadFile]] = File(None)):

    videofiles = []

    for file in fileList:
        print("Uploading:", file.filename)
        print(".................................")

        extension = file.filename.split(".")[-1] in ("mp4", "avi")
        if not extension:
            return "Video must be in mp4 or avi format!"
    try:
        try:
            contents = file.file.read()
            with temp as f:
                print("what's happening: ")
                f.write(contents)
                videofiles.append(cv2.VideoCapture(temp.name))

                print('list of videos uploaded :')
                for vidcap in videofiles:
                    print("video:", vidcap)

                    # Check if camera opened successfully
                    if (vidcap.isOpened() == False):
                        print("Error opening video file")

                    # get height, width and frame count of the video
                    width, height = (
                        int(vidcap.get(cv2.CAP_PROP_FRAME_WIDTH)),
                        int(vidcap.get(cv2.CAP_PROP_FRAME_HEIGHT))
                    )

                    print(f"width: {width}")
                    print(f"height: {height}")

                    # count the number of frames
                    frames = vidcap.get(cv2.CAP_PROP_FRAME_COUNT)
                    fps = vidcap.get(cv2.CAP_PROP_FPS)

                    # calculate duration of the video
                    seconds = round(frames / fps)
                    video_time = datetime.timedelta(seconds=seconds)
                    print(f"duration in seconds: {seconds}")
                    print(f"video time: {video_time}")

        except Exception:
            return {"message": "There was an error uploading the file"}
        finally:
            file.file.close()
    except Exception:
        return {"message": "There was an error processing the file"}
    finally:
        os.remove(temp.name)

    count = 0;
    for vid in videofiles:
        count += 1
    print("number of video capture objects uploaded:", count)


return {"uploadStatus": "Complete", "filenames": [f.filename for f in fileList]}

This is what I last got from this code: enter image description here

I have a feeling this has to do with video-capture but I thought this was addressed when I switched from looping through the list of videos with a single video capture to a list of video captures for each video uploaded. But as you can see from the screenshot, the list of video captures only has the one object for the first video.

Any idea on what might be causing this?

Edit: I made use of this question for the single video upload and built on the same logic to iterate through the list of videos, but that didn't work either.

Argueta answered 15/12, 2022 at 9:49 Comment(2)
Does this answer your question? How to pass a video uploaded via FastAPI to OpenCV VideoCapture?Blastopore
@Blastopore Hi, no it doesn't unfortunately. I used that same logic to iterate through the list of videos that I'm passing but it doesn't seem to work that way. I put the try-except within the for loop but the video capture list still only contains the capture for the first video. Thank you for pointing it out btw! :)Wilie
B
0

Below is an example on how to upload a list of videos using Fetch API in the frontend and FastAPI in the backend, as well as OpenCV to process the files. This example is based on this answer and this answer.

Working Example

app.py

from fastapi import FastAPI, File, UploadFile, Request
from tempfile import NamedTemporaryFile
from typing import List, Optional
from fastapi.templating import Jinja2Templates
import cv2
import os

app = FastAPI()
templates = Jinja2Templates(directory='templates')

def process_video(filename):
    print('Processing ' + filename)
    cap = cv2.VideoCapture(filename)
    while cap.isOpened():
        ret, frame = cap.read()
        if not ret:
            break
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        cv2.imshow('frame', gray)
        if cv2.waitKey(1) == ord('q'):
            break
    cap.release()
    cv2.destroyAllWindows()


@app.post('/submit')
def upload(files: Optional[List[UploadFile]] = File(None)):
    for file in files:
        temp = NamedTemporaryFile(delete=False)
        try:
            try:
                contents = file.file.read()
                with temp as f:
                    f.write(contents);
            except Exception:
                return {'message': 'There was an error uploading the file'}
            finally:
                file.file.close()
            
            process_video(temp.name)
        except Exception:
            return {'message': 'There was an error processing the file'}
        finally:
            #temp.close()  # the `with` statement above takes care of closing the file
            os.remove(temp.name)
        
    return {'Files Uploaded': [f.filename for f in files]}
    

@app.get('/')
def index(request: Request):
    return templates.TemplateResponse('index.html', {'request': request})

templates/index.html

<!DOCTYPE html>
<html>
   <body>
      <input type="file" id="fileInput" name="file" multiple><br>
      <input type="button" value="Upload video files" onclick="submit()">
      <script>
         function submit() {
             var fileInput = document.getElementById('fileInput');
             if (fileInput.files[0]) {
                var formData = new FormData();
                for (const file of fileInput.files)
                    formData.append('files', file);
                    
                 fetch('/submit', {
                       method: 'POST',
                       body: formData,
                     })
                     .then(res => res.text())
                     .then(data => {
                       console.log(data);
                     })
                     .catch(error => {
                       console.error(error);
                     });
             }
         }
      </script>
   </body>
</html>
Blastopore answered 15/12, 2022 at 10:29 Comment(1)
Hi, thank you so much. I'm still trying to figure out why your answer works and mine didn't, but this solved my problem!Wilie

© 2022 - 2024 — McMap. All rights reserved.