简体   繁体   中英

OSError: MoviePy error: the file guitar.mp4 could not be found

I'm working on a video to audio converter with react and flask/python. I have received a 500 with this error:

raise IOError(("MoviePy error: the file %s could not be found!\n"
OSError: MoviePy error: the file guitar.mp4 could not be found!
Please check that you entered the correct path.

EDIT: As stated in comments, moviepy VideoFileClip is looking for a path. Per suggestion, I am now attempting to write the incoming video file to a temp directory housed in the backend of the app. The updated stack trace shows the filepath printing, however when presented to VideoFileClip it is still unhappy.

The following snippet is the onSubmit for the video file upload:

const onSubmit = async (e) => {
    e.preventDefault()
    const data = new FormData()
    console.log('hopefully the mp4', videoData)
    data.append('mp3', videoData)
    console.log('hopefully a form object with mp4', data)
    const response = await fetch('/api/convert', {
      method: "POST",
      body: data
    })
    if (response.ok) {
      const converted = await response.json()
      setMp3(converted)
      console.log(mp3)
    } else {
      window.alert("something went wrong :(");
    }
  }

Here is a link to an image depicting the console output of my file upload from within init .py

app = Flask(__name__)

app.config.from_object(Config)
app.register_blueprint(convert, url_prefix='/api/convert')

CORS(app)

from within converter.py

import os
from flask import Blueprint, jsonify, request
import imageio
from moviepy.editor import *


convert = Blueprint('convert', __name__)

@convert.route('', methods=['POST'])
def convert_mp4():
  if request.files['mp3'].filename:
    os.getcwd()
    filename = request.files['mp3'].filename
    print('hey its a file again', filename)
    safe_filename = secure_filename(filename)
    video_file = os.path.join("/temp/", safe_filename)
    print('hey its the file path', video_file)
    video_clip = VideoFileClip(video_file)
    print('hey its the VideoFileClip', video_clip)
    audio_clip = video_clip.audio
    audio_clip.write_audiofile(os.path.join("/temp/", f"{safe_filename}-converted.mp3"))

    video_clip.close()
    audio_clip.close()

    return jsonify(send_from_directory(os.path.join("/temp/", f"{safe_filename}-converted.mp3")))
  else:
    return {'error': 'something went wrong :('}


In the stack trace below you can see file printing the name of the video, my only other thought on why this may not be working was because it was getting lost in the post request, however the fact it is printing after my if file: check is leaving me pretty confused.

hey its a file again guitar.mp4
hey its the file path /temp/guitar.mp4
127.0.0.1 - - [22/Apr/2021 12:12:15] "POST /api/convert HTTP/1.1" 500 -
Traceback (most recent call last):
  File "/home/jasondunn/projects/audioconverter/.venv/lib/python3.8/site-packages/flask/app.py", line 2464, in __call__
    return self.wsgi_app(environ, start_response)
  File "/home/jasondunn/projects/audioconverter/.venv/lib/python3.8/site-packages/flask/app.py", line 2450, in wsgi_app
    response = self.handle_exception(e)
  File "/home/jasondunn/projects/audioconverter/.venv/lib/python3.8/site-packages/flask_cors/extension.py", line 161, in wrapped_function
    return cors_after_request(app.make_response(f(*args, **kwargs)))
  File "/home/jasondunn/projects/audioconverter/.venv/lib/python3.8/site-packages/flask/app.py", line 1867, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/home/jasondunn/projects/audioconverter/.venv/lib/python3.8/site-packages/flask/_compat.py", line 39, in reraise
    raise value
  File "/home/jasondunn/projects/audioconverter/.venv/lib/python3.8/site-packages/flask/app.py", line 2447, in wsgi_app
    response = self.full_dispatch_request()
  File "/home/jasondunn/projects/audioconverter/.venv/lib/python3.8/site-packages/flask/app.py", line 1952, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/home/jasondunn/projects/audioconverter/.venv/lib/python3.8/site-packages/flask_cors/extension.py", line 161, in wrapped_function
    return cors_after_request(app.make_response(f(*args, **kwargs)))
  File "/home/jasondunn/projects/audioconverter/.venv/lib/python3.8/site-packages/flask/app.py", line 1821, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/home/jasondunn/projects/audioconverter/.venv/lib/python3.8/site-packages/flask/_compat.py", line 39, in reraise
    raise value
  File "/home/jasondunn/projects/audioconverter/.venv/lib/python3.8/site-packages/flask/app.py", line 1950, in full_dispatch_request
    rv = self.dispatch_request()
  File "/home/jasondunn/projects/audioconverter/.venv/lib/python3.8/site-packages/flask/app.py", line 1936, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/home/jasondunn/projects/audioconverter/back/api/converter.py", line 20, in convert_mp4
    video_clip = VideoFileClip(video_file)
  File "/home/jasondunn/projects/audioconverter/.venv/lib/python3.8/site-packages/moviepy/video/io/VideoFileClip.py", line 88, in __init__
    self.reader = FFMPEG_VideoReader(filename, pix_fmt=pix_fmt,
  File "/home/jasondunn/projects/audioconverter/.venv/lib/python3.8/site-packages/moviepy/video/io/ffmpeg_reader.py", line 35, in __init__
    infos = ffmpeg_parse_infos(filename, print_infos, check_duration,
  File "/home/jasondunn/projects/audioconverter/.venv/lib/python3.8/site-packages/moviepy/video/io/ffmpeg_reader.py", line 270, in ffmpeg_parse_infos
    raise IOError(("MoviePy error: the file %s could not be found!\n"
OSError: MoviePy error: the file /temp/guitar.mp4 could not be found!
Please check that you entered the correct path.

thanks in advance for taking a look/future advice. First official post on Stack Overflow:)

I think the issue is with how you are using file = request.files['mp3'].filename .

The value assigned to file isn't a pointer to the uploaded file. It is just the name of the file, a string. Just request.files['mp3'] is an instance of the werkzeug.datastructures.FileStorage class documented here .

The libraries you are passing that string to are interpreting it as a path to the file they are supposed to open.

Since you haven't saved the file anywhere they library isn't finding anything.

I'm not familiar with the libraries you are using, but they might have a way to send the in-memory file data to them directly without having to save the file then have them open it up again.

If not, then you will probably want to save the file to some temporary location and then have the library open and read the file.

Looks like python cannot find guitar.mp4 :(

I appears that you need to save the file contents on disk before processing. Looking at the docs for MoviePy you need to pass in the file name or absolute path into VideoFileClip constructor, this object will open the file on disk and handle processing after instantiation.

Saving the file within the request should be simple enough. The code below should be able to handle this

file.save(os.path.join("/path/to/some/dir", filename))

Now you can give VideoFileClip a proper URI to the file.

video_clip = VideoFileClip(os.path.join("/path/to/some/dir", filename))

This is what I would write for convert_mp4 although it isn't tested.

@convert.route('', methods=["POST"])
def convert_mp4():
    if request.files.get("mp3"):

        # clean the filename
        safe_filename = secure_filename(request.files["mp3"].filename)

        # save file to some directory on disk
        request.files["mp3"].save(os.path.join("/path/to/some/dir", safe_filename))
        video_clip = VideoFileClip(os.path.join("/path/to/some/dir", safe_filename))
        audio_clip = video_clip.audio  # convert to audio

        # you may need to change the name or save to a different directory
        audio_clip.write_audiofile(os.path.join("/path/to/some/dir", f"{safe_filename}.mp3"))

        # close resources, maybe use a context manager for better readability
        video_clip.close()
        audio_clip.close()

        # responds with data from a specific file
        send_from_directory("/path/to/some/dir", f"{safe_filename}.mp3")
    else:
      return jsonify(error="File 'mp3' does not exist!")

Whenever you are saving data to disk through flask you should use secure_filename which is built into flask from the werkzeug project. This function will clean the input name so attackers cannot create malicious filenames.

I would suggest even going a bit further, maybe create 2 endpoints. One for submitting the data to process, and the second for retrieving it. This keeps your requests fast and allows flask to handle other requests in the meantime (however you will need some background process to handle the conversion).

Update April 30th, 2021

I know we solved this on Discord but I wanted to document the solution.

Your MP4 data is not being saved to disk using the save method (see this ). You can check the above code that implements this.

Once that is done, we now know where this data is and can instantiate the VideoFileClip object using the known file path, this will allow the conversion to take place and you will need to then save the converted MP3 file on a location within your filesystem.

Once the MP3 is saved to disk, you can use the flask send_from_directory function to send the data back within your response. This response cannot contain JSON content as the content type is already set to audio/mpeg based on the MP3 file contents.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM