javascriptuploadfetch-apiprogressvimeo-api

Vimeo API Upload Progress


I'm using the Vimeo API to upload videos and am trying to track the progress of the upload.

The documentation here is pretty straightforward:

https://developer.vimeo.com/api/upload/videos

However, I can't seem to figure out how to retrieve Upload-Length and Upload-Offset from the HEAD response.

I call the "uploadVideo" function below to upload the video to Vimeo (this function does as it should). I then call the "getProgress" function and this is where things go awry. I've tried many variations of this code, but none have worked.

async function uploadVideo(upload_link : string) {
    const uploadResponse = await fetch(upload_link, {
        method: 'PATCH',
        headers: {
            'Tus-Resumable': '1.0.0',
            'Upload-Offset': '0',
            'Content-Type': 'application/offset+octet-stream'
        },
        body: accepted
    });
}

async function getProgress(upload_link : string) {
    const progress = await fetch(upload_link, {
        method: 'HEAD',
        headers: {
            'Tus-Resumable': '1.0.0',
            'Accept': 'application/vnd.vimeo.*+json;version=3.4'
        },
    });

    const currentProgress = await progress;
    console.log(currentProgress);

    // if (currentProgress.upload_length != currentProgress.upload_offset) {
    //     getProgress(upload_link)
    // }
}

If I await progress.json(), I get a SyntaxError: Unexpected end of JSON input

I'm somewhat surprised that there are no up-to-date JavaScript examples of this process out there on the interwebs. Any assistance would be greatly appreciated.

Thank you for your time.


Solution

  • As @Clive pointed out above, to access the necessary headers, one would use:

    uploadLength = progress.headers.get('upload-length');
    uploadOffset = progress.headers.get('upload-offset');
    

    This answers my specific question.

    However, if you're only using the Vimeo API, you'll find that there's another challenge once this is complete. In the original code posted above, you'll never be able to track the progress of the upload with a HEAD request because the "upload-offset" value is always 0 until the initial PATCH request is completed, i.e. it's 0 until the PATCH request is complete and once it's complete it jumps directly to 100%.

    To get around this issue, I decided to use "tus-js-client." So, if you've made it to where my code above leaves off, instead of using the above functions you could just pass the link (in this example, "upload_link") and the file (in this example, "accepted") to:

    async function uploadVideo(upload_link : string) {
        // Create the tus upload similar to the example from above
        var upload = new tus.Upload(accepted, {
            uploadUrl: upload_link,
            onError: function(error) {
                console.log("Failed because: " + error)
            },
            onProgress: function(bytesUploaded, bytesTotal) {
                var percentage = (bytesUploaded / bytesTotal * 100).toFixed(2)
                console.log(bytesUploaded, bytesTotal, percentage + "%")
            },
            onSuccess: function() {
                console.log("Download %s from %s", upload.file.path, upload.url)
            }
        })
    
        // Start the upload
        upload.start()
    }
    

    And here's the server-side code to get the "upload_link":

    export const actions: Actions = {
        upload: async ({ request }) => {
            const uploadFormData = await request.formData();
            const accepted = uploadFormData.get('accepted-file') as File;
    
            const response = await fetch(`https://api.vimeo.com/me/videos`, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                    'Authorization': `bearer ${import.meta.env.VITE_VIMEO_ACCESS_TOKEN}`,
                    'Accept': 'application/vnd.vimeo.*+json;version=3.4'
                },
                body: JSON.stringify({
                    upload: {
                        "approach": "tus",
                        "size": accepted.size
                    }
                })
            });
            const dataResponse = await response.json();
        
            return {
                upload: dataResponse.upload
            }
        }
    }
    

    This server response is returned to a client-side "handleSubmit" function, which in turn calls the "uploadVideo" function, like so uploadVideo(result.data.upload.upload_link).

    I was initially using "vimeo-upload" to accomplish this. The problems with vimeo-upload are (1) it exposes your access token to the browser and (2) the code base is outdated. I'd advise to stay away from vimeo-upload at all costs!

    For what it's worth, this is a SvelteKit implementation.

    If you're using SvelteKit, best to not use an import.meta.env.VITE prefixed environment variable; it should be a "private" environment variable as shown here:

    https://joyofcode.xyz/sveltekit-environment-variables

    I had such a hard time figuring out how to do this. I hope that this example will help someone in the future.