- 17 Jan 2024
- Print
Upload a video
- Updated on 17 Jan 2024
- Print
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:
Create a video entity (with a filename) in a specified VideoManager.
Get the upload URL from the asset-management endpoint.
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 videodescription
String optional: Description of the videokeywords
List of Strings optional: Keywords of the videogroup
Integer optional: ID of the owner groupchannel
Integer optional: ID of the channelfileName
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
ACCESS_TOKEN
String mandatory: Access token (see Access and Refresh Tokens).
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
Generate the upload URL.
Chunk the video into smaller pieces.
Upload each chunk, retrying any failed chunks.
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:
Identify the conditions that would cause a chunk to fail. For example, you might retry if the response code is not
201 CREATED
.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.
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;
}
}