Search code examples
djangovimeo-api

AttributeError at /upload_to_vimeo/ 'TypeError' object has no attribute 'message'


while uploading videos to vimeo it throws this error following are codes views

def upload_to_vimeo(request):
    token = 'xxxxx'
    if request.method == 'POST':
        form = VideoUploadForm(request.POST, request.FILES)
        if form.is_valid():
            v = vimeo.VimeoClient(
                token=token,
            )
            v.upload(request.FILES['video_file'])
            return HttpResponseRedirect(request.META.get("HTTP_REFERER"))
    else:
        form = VideoUploadForm()
    return render(request, 'forms/video_upload_form.html', {'form': form})

template

 <form action="" method="POST" enctype="multipart/form-data">
                    {% csrf_token %}
                    <input type="file" name="video_file">
                    <input type="submit" class="button inline-block bg-theme-1 text-white mt-5" value="Upload">
                </form>

Traceback:

File "/home/biju/Desktop/Dev/multitenant/lib/python3.8/site-packages/vimeo/upload.py" in __perform_tus_upload
  143.             with io.open(filename, 'rb') as fs:

During handling of the above exception (expected str, bytes or os.PathLike object, not TemporaryUploadedFile), another exception occurred:

File "/home/biju/Desktop/Dev/multitenant/lib/python3.8/site-packages/django/core/handlers/exception.py" in inner
  34.             response = get_response(request)

File "/home/biju/Desktop/Dev/multitenant/lib/python3.8/site-packages/django/core/handlers/base.py" in _get_response
  115.                 response = self.process_exception_by_middleware(e, request)

File "/home/biju/Desktop/Dev/multitenant/lib/python3.8/site-packages/django/core/handlers/base.py" in _get_response
  113.                 response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "/home/biju/Documents/mtenant/client_admin/views.py" in upload_to_vimeo
  787.             v.upload(request.FILES['video_file'])

File "/home/biju/Desktop/Dev/multitenant/lib/python3.8/site-packages/vimeo/upload.py" in upload
  71.         return self.__perform_tus_upload(filename, attempt, chunk_size=chunk_size)

File "/home/biju/Desktop/Dev/multitenant/lib/python3.8/site-packages/vimeo/upload.py" in __perform_tus_upload
  152.             raise exceptions.VideoUploadFailure(

File "/home/biju/Desktop/Dev/multitenant/lib/python3.8/site-packages/vimeo/exceptions.py" in __init__
  93.         super().__init__(response, message)

File "/home/biju/Desktop/Dev/multitenant/lib/python3.8/site-packages/vimeo/exceptions.py" in __init__
  30.         self.message = self.__get_message(response)

File "/home/biju/Desktop/Dev/multitenant/lib/python3.8/site-packages/vimeo/exceptions.py" in __get_message
  23.             message = getattr(response, 'message')

Exception Type: AttributeError at /upload_to_vimeo/
Exception Value: 'TypeError' object has no attribute 'message'

Solution

  • Some sloppy coding on Vimeo's end, the subtle difference is shown here:

           try:
      ...:     int("not an int")
      ...: except ValueError as e:
      ...:     print("e is an exception: {} / {}".format(type(e) is Exception, isinstance(e, Exception)))
      ...:     
    e is an exception: False / True
    

    So their exception check fails for any subclass of Exception (read: always). And since they don't throw the exception with a message attribute, you end up with an AttributeError.

    But you wouldn't have had much to go on anyway, cause what they've told you if they coded it correctly would be: 'Unexpected error when uploading through tus.'. It boils down to you failing to get an offset (where to restart the upload for the current chunk) from Tus within 3 retries. Good luck hunting!

    Addressing comment

    request.FILES is an array of file objects. Not an array of file names. The api wants file names and they don't check if they got them or have support for file-like objects. They also do scary stuff:

            try:
                return os.path.getsize(filename)
            except TypeError:
                return len(filename.read())
    

    So theoretically they support file-like objects, but apparently not in full. Also, len(filename.read()) is very dangerous for your server memory, especially with video files in parallel, because the entire file will be read into memory. The more I read this API, the more I don't want to use it ;).

    Anyway, you should add the following to settings:

    FILE_UPLOAD_HANDLERS = [
     'django.core.files.uploadhandler.TemporaryFileUploadHandler'
    ]  # Removed MemoryFileUploadHandler
    

    As commented this removes the MemoryFileUploadHandler, so we can rely on temporary_file_path being available to us. In addition, Django switches between Memory and disk storage based on file size (about 3MB last I checked to go from memory to on-disk). This is a) rarely used and b) the API won't work with them.

    Then just pass the path to Vimeo:

    file_obj = request.FILES['video_file']
    path = file_obj.temporary_file_path()  # method not attribute
    try:
        v.upload(path)
    except VimeoUploadFailure:
        print("Uh oh")
    finally:
        file_obj.close()  # Theoretically this should remove the file
        if os.path.exists(path):
            os.unlink(path)  # But this will do it, barring permissions