Upload a video
  • 17 Jan 2024

Upload a video


Article summary

REST API video upload feature

The REST API's video upload feature is useful for creating a custom upload portal or UI, and for system integrators to synchronize VideoManager Pro with a content management system.

Uploading with the API

Uploading with the API is done via chunking. You can optionally include metadata, such as a title, description, and keywords. You can also define the channel ID and group ID that the uploaded video will be assigned to.

To upload a video via the REST API, follow these steps:

  1. Create a video entity (with a filename) in a specified VideoManager.

  2. Get the upload URL from the asset-management endpoint.

  3. Upload the video.

The following chapters describe how to perform each step using cURL to demonstrate usage in the examples.

URLs

The URLs in the methods throughout the following chapters refer to the general live instance of movingimage. Customers using VideoManager Pro on a custom domain must adjust the URLs accordingly.

Create a new video entity

To start the upload process, you need to first create a video entity. Creating the entity will allow you to collect the video ID, which you will need to finish uploading your video.

Request

curl -v -X POST -H "Authorization: Bearer <ACCESS_TOKEN>" -H "Content-Type: application/json" -d "{ 'title': '<VIDEO_TITLE>', 'description': '<DESCRIPTION_OF_VIDEO>', 'keywords': ['<FOO>', '<BAR>'], 'group': <ID_OF_OWNER_GROUP>, 'channel': <ID_OF_CHANNEL>, 'fileName': '<FILENAME>', 'autoPublish': true }" https://api.video-cdn.net/v1/vms/<VIDEOMANAGER_ID>/videos/

Parameters

  • VIDEOMANAGER_ID Integer mandatory: ID of the VideoManager, to which you intend to upload the video.

  • Headers

    • ACCESS_TOKEN String mandatory: Access token (see "Authentication" for more information).

  • JSON Body

    • title String optional: Title of the video

    • description String optional: Description of the video

    • keywords List of Strings optional: Keywords of the video

    • group Integer optional: ID of the owner group

    • channel Integer optional: ID of the channel

    • fileName String mandatory: Filename of the video (see suffix note below)

    • autoPublish Boolean optional: If true, the video will automatically be published after upload is complete and the 360p mp4 has been transcoded.

Suffix of the fileName

The suffix of the filename is used to determine the mime type. The following table lists all supported suffixes.

File Extension

MIME Type

mp4

video/mp4

mov

video/quicktime

flv

video/x-flv

wmv

video/x-ms-wmv

mpg

video/mpeg

avi

video/x-msvideo

mp3

audio/mpeg

mpeg

video/mpeg

m4v

video/x-m4v

vob

video/mpeg

f4v

video/x-flv

wav

audio/vnd.wave

3gp

video/3gpp

rm

audio/x-pn-realaudio

mts

video/mpeg

m2v

video/mpeg

wma

audio/x-ms-wma

mxf

application/mxf

m2p

video/mpeg

m2t

video/mpeg

mkv

video/x-matroska

webm

video/webm

qt

video/quicktime

mp2

audio/mpeg

m2ts

video/mpeg

ogv

video/ogg

Response

201 CREATED The location data of the response header will return a URL containing the newly created video ID. You will need this video ID for the next step.

Location: https://api.video-cdn.net/v1/vms/<VIDEOMANAGER_ID>/videos/<VIDEO_ID>

Field

Type

Description

VIDEOMANAGER_ID

Integer

ID of the VideoManager

VIDEO_ID

String

Newly created video ID for the new video entity.

Getting the Upload URL

After the video entity has been successfully created, you must get the upload URL.

Request

curl -v -X GET -H "Authorization: Bearer <ACCESS_TOKEN>" https://api.video-cdn.net/v1/vms/<VIDEOMANAGER_ID>/videos/<VIDEO_ID>/url

Parameters

  • VIDEOMANAGER_ID Integer mandatory: The ID of the VideoManager, in which the new video entity has been created in the first step.

  • VIDEO_ID String mandatory: The video ID that the system has created for the new entity (see "Creating a Video Entity").

  • Headers

Response

201 CREATED

The upload URL is sent back in the HTTP response header under location.

Response format

Location: https://asset-in.video-cdn.net/chunks/vms/<VIDEOMANAGER_ID>/videos/<VIDEO_ID>?bucketId=<INTERNAL_BUCKET_ID>&fileId=<INTERNAL_FILE_ID>&userId=<USER_ID>&__token__=<EXPIRY_TIME>~<HEX_VALUE>

Notes

  • The values <INTERNAL_BUCKET_ID>, <INTERNAL_FILE_ID>, <USER_ID>, <EXPIRY_TIME> and <HEX_VALUE> are system-generated and must not be changed.

  • The character between <EXPIRY_TIME> and <HEX_VALUE> is a tilde (~), not a dash.

The URL has an time-limited token, valid for four hours. This means that if the video upload takes longer than four hours to complete, an error will occur. If this happens, perform this request again to generate a new upload URL.

Chunked upload with retry handling

The movingimage REST API has a rate limit of 100 requests per minute. Any requests that exceed this limit will be denied.

To upload your video, you can use the chunked upload method with retry handling. This method involves uploading the video in a series of smaller chunks, and then retrying any failed chunks.

Steps involved

  1. Generate the upload URL.

  2. Chunk the video into smaller pieces.

  3. Upload each chunk, retrying any failed chunks.

  4. Once all chunks have been uploaded, the video will be complete.

Tips for optimization

  • Use a large number of small chunks. This will make it more likely that the upload will succeed, even if some of the chunks fail.

  • Implement retry handling with a backoff strategy. This means that you should wait for an incrementally longer time between each retry. This will help to prevent you from overloading the API with too many requests.

  • Put a limit on the number of times you retry each chunk. This will help to prevent you from getting stuck in an infinite loop if the chunk is unable to be uploaded.

Example of a chunked upload request

curl -X POST -H "Mi24-Upload-Total-Chunks: 10" -H "Mi24-Upload-Current-Chunk: 1" -H "Content-Type:application/octet-stream" --data-binary "@/<FILENAME>" "<UPLOAD_URL>"

Response codes

  • 201 CREATED: The chunk was uploaded successfully.

  • 200 OK: The chunk has already been uploaded.

  • 4XX ERROR: The request failed due to a client-side error.

  • 5XX ERROR: The request failed due to a server-side error.

If the response code is not 201 CREATED, the chunk will be retried. The retry handling strategy is up to you, but it is important to wait for an incrementally longer time between each retry to prevent overloading the API.

Retry handling

If the response to a chunk upload request is not 201 CREATED, the chunk must be retried. This can happen for a few reasons, such as:

  • Failure in the connection across the Internet

  • Expired upload token

  • Rate-limiting

  • Timeout in receiving a response from the API

To avoid these situations interrupting your upload, you should implement retry handling in your code. This means that you should:

  1. Identify the conditions that would cause a chunk to fail. For example, you might retry if the response code is not 201 CREATED.

  2. Implement a backoff strategy. This means that you should wait for an incrementally longer or random amount of time between each retry. This will help to prevent you from overloading the API with too many requests.

  3. Set a limit on the number of times you retry. This will help to prevent you from getting stuck in an infinite loop if the chunk is unable to be uploaded.

Here are some examples of backoff strategies:

  • Fixed backoff: Wait for a fixed amount of time between each retry, such as 1 second.

  • Exponential backoff: Wait for an exponentially longer amount of time between each retry, such as 1, 2, 4, 8, 16, ... seconds.

  • Randomized backoff: Wait for a random amount of time between each retry, such as between 1 and 5 seconds.

The best backoff strategy for your specific needs will depend on the frequency of failures and the amount of time you are willing to wait for the upload to complete.

Java 11 Chunk Upload Example

Java 11 Chunk Upload Example

package mi.uploader;
 
import java.io.*;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
 
public class App {
 
    public static Integer CHUNK_SIZE = 2_097_152;
    public static Integer MAX_RETRIES = 10;
 
    public static HttpClient client = HttpClient.newBuilder()
            .version(HttpClient.Version.HTTP_1_1)
            .followRedirects(HttpClient.Redirect.NORMAL)
            .connectTimeout(Duration.ofSeconds(20))
            .build();
 
    public static void main(String[] args) throws IOException, InterruptedException {
 
        /*
         Step 1. https://doc.movingimage.com/display/LT/Creating+a+Video+Entity
         Step 2. https://doc.movingimage.com/display/LT/Getting+the+Upload+URL
         Step 3. Upload
         */
 
        URI url = URI.create("{URL obtained via API}");
        File f = new File("sample_600s_25fps_1080.mp4");
        InputStream inputStream = new FileInputStream(f);
 
        long totalChunks = calculateChunks(f.length());
 
 
        for (int i = 1; i <= totalChunks; i++) {
 
            byte[] data = new byte[CHUNK_SIZE];
 
            inputStream.read(data);
 
            int statusCode = 0;
            int retryAttempts = 0;
            do {
                if (statusCode >= 500) {
                    Thread.sleep(2000);
                }
 
                if (retryAttempts++ >= MAX_RETRIES) {
                    throw new RuntimeException("Upload failed. Please try again later.");
                }
 
                HttpRequest request = HttpRequest.newBuilder()
                        .POST(HttpRequest.BodyPublishers.ofByteArray(data))
                        .uri(url)
                        .setHeader("Mi24-Upload-Total-Chunks", String.valueOf(totalChunks)) // add request header
                        .setHeader("Mi24-Upload-Current-Chunk", String.valueOf(i)) // add request header
                        .build();
 
                HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
 
                statusCode = response.statusCode();
 
                System.out.println("Status: "+statusCode);
 
            } while (statusCode >= 500);
 
        }
 
        System.out.println("Done");
 
    }
 
    public static long calculateChunks(long fileSize) {
 
        long remainder = fileSize % CHUNK_SIZE;
        int totalChunks = Math.toIntExact(fileSize / CHUNK_SIZE);
 
        if (remainder > 0) {
            totalChunks = totalChunks + 1;
        }
 
        return totalChunks;
    }
}



Was this article helpful?