Vimeo is a popular video sharing website in which users can upload, share , view and stream videos. If you are planning a project which contains sharing and streaming videos then Vimeo can take care of that for you. Vimeo provides API endpoints for all the tasks so that you can implement it in any platform using any language. We’ll be implementing video upload feature in an Angular application with tus js client.
Step 1: Setting up Vimeo account for upload access
Create a Vimeo account and verify your email address, go to https://developer.vimeo.com/apps to register your application by providing necessary details. Now you need to request for video upload access in your app settings > Permissions
and wait for approval. After you get permission for upload you can generate your access token in Authentication > Generate Access Token
. Give "Edit" and "Upload" scopes to your token and click "Generate".
Copy or save your access token. You will be using this access token for authenticating while making API calls. Test your access token by making a HTTP GET
request to "https://api.vimeo.com/me"
with Authorization>Bearer Token
and paste your access token in postman.
You should get a JSON response with user details for a successful API call. The API docs is available here Vimeo API playground.
Step 2: Creating the uploader
There are 3 steps to upload your video successfully to Vimeo.
- Create the video
- Upload the video file
- Verify the upload
Create the video
Prerequisites: Knowledge about RxJS concepts like observables, map, pipe, expand, EMPTY.
In this step you are required to make an authenticated HTTP POST request to https://api.vimeo.com/me/videos with the video details like file size and upload approach in the request body so that a video will be created in Vimeo with the given details. Video name and other attributes can also be sent in this request but the above 2 attributes are mandatory.
{
"name": "{filename}",
"upload": {
"approach": "tus",
"size": "{size}"
}
}
The POST request should contain the following headers.
Authorization bearer {access_token}
Content-Type application/json
Accept application/vnd.vimeo.*+json;version=3.4
I’ll be creating a service “upload” for this and inject this service in the required component. ng g service upload
Now in your upload service import the following
//Used for asynchronous http request handling
import { Observable } from 'rxjs';
//Required to set custom headers and make http requests
import { HttpHeaders, HttpClient } from '@angular/common/http';
Pass HttpClient as constructor parameter.
constructor(private http: HttpClient) { }
Set your access token and Vimeo API endpoint.
private api = 'https://api.vimeo.com/me/videos';
//It's advised not to hardcode the API token, instead fetch it from the server after authentication.
private accessToken = 'youraccesstoken';
Now we’ll write the createVideo function which is responsible for creating a video in your Vimeo account. The function takes a single video file of type File as a parameter and returns an observable i.e. the HPPT POST request.
Current progress:
//Filename: upload.service.ts
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { HttpHeaders, HttpClient } from '@angular/common/http';
@Injectable({
providedIn: 'root'
})
export class UploadService {
constructor(private http: HttpClient) { }
private api = 'https://api.vimeo.com/me/videos';
private accessToken = 'youraccesstoken';
createVideo(file: File): Observable<any> {
const body = {
name: file.name,
upload: {
approach: 'tus',
size: file.size
}
};
console.log(file);
const header: HttpHeaders = new HttpHeaders()
.set('Authorization', 'bearer ' + this.accessToken)
.set('Content-Type', 'application/json')
.set('Accept', 'application/vnd.vimeo.*+json;version=3.4');
return this.http.post(this.api, body, {
headers: header,
observe: 'response'
});
}
}
Now go to the component where you are implementing the upload feature. I’ll be implementing it in upload.component.ts
.
- Create a file input that accepts videos only.
- Add an upload button to start the process.
- Inject your upload service into the component.
I’ll be creating a class uploadFiles
for storing the video name, web address for uploading the video & URI of the uploaded video on Vimeo.
// Filename: upload.compopnent.ts
export class uploadFiles {
constructor(public video: File, public path: string, public uploadURI: string) {
this.video = video;
this.path = path;
this.uploadURI = uploadURI;
}
}
Create a function getLink
which takes video file, current video index, video array as parameters. This function will pass the video to the createVideo
function in the upload service. Here you can see that the index is incremented so that the process can be repeated on the entire array.
For every response received, you need to create a new uploafFiles
object and save the upload link and video link from the successful response body. I've created a pendingFiles
array to store all the video's response details for uploading later.
// Filename: upload.component.ts
getLink = (video: File, index, arr) => {
console.log('index: ' + index);
return this.upload.createVideo(video).pipe(
map(response => {
const videoFile = new uploadFiles(video, response.body.link, response.body.upload.upload_link);
this.pendingFiles.push(videoFile);
console.log('response: ' + response);
return {
data: response,
index: index + 1,
arr: arr
};
})
);
}
Create a start
function to run when "Upload" button is clicked. The above getLink
function has to be called for all the videos in the array, so we use the rxjs expand
operator to return the next video for upload until the last video in the array.
// Filename: upload.component.ts
public start(files: FileList) {
this.videoList = files;
console.log(this.videoList);
const recursion = this.getLink(this.videoList[0], 0, this.videoList).pipe(expand(res => {
return res.index > res.arr.length - 1 ?
EMPTY : this.getLink(this.videoList[res.index], res.index, this.videoList);
}));
recursion.subscribe(x => {
if (x.index > x.arr.length - 1) {
console.log('Links generated, Starting upload');
// All links have been generated now you can start the upload process here
}
});
}
Now you can test the upload link generation feature. Once the video is created and the link is generated, you can go to your Vimeo account and see the new videos created. The response will contain upload.upload_link
which is the URL for uploading that video.
Current progress:
upload.component.ts
import { Component, OnInit } from '@angular/core';
import { UploadService } from '../upload.service';
import { map, expand } from 'rxjs/operators';
import { EMPTY } from 'rxjs';
export class uploadFiles {
constructor(public video: File, public path: string, public uploadURI: string) {
this.video = video;
this.path = path;
this.uploadURI = uploadURI;
}
}
@Component({
selector: 'app-upload',
template: `<div style="text-align:center">
<input type="file" #file name="video" id="video" accept="video/*" multiple>
<input type="button" value="Upload" (click)="start(file.files);"> </div>`,
styleUrls: ['./upload.component.css']
})
export class UploadComponent implements OnInit {
title = 'vimeo-uploader';
videoList: FileList;
videoLinks = [];
pendingFiles: uploadFiles[] = [];
constructor(private upload: UploadService) { }
ngOnInit() {}
public start(files: FileList) {
this.videoList = files;
console.log(this.videoList);
const recursion = this.getLink(this.videoList[0], 0, this.videoList).pipe(expand(res => {
return res.index > res.arr.length - 1 ?
EMPTY : this.getLink(this.videoList[res.index], res.index, this.videoList);
}));
recursion.subscribe(x => {
if (x.index > x.arr.length - 1) {
console.log('Link generated, Starting upload');
// All links have been generated now you can start the upload
}
});
}
getLink = (video: File, index, arr) => {
console.log('index: ' + index);
return this.upload.createVideo(video).pipe(
map(response => {
const videoFile = new uploadFiles(video, response.body.link, response.body.upload.upload_link);
this.pendingFiles.push(videoFile);
console.log('response: ' + response);
return {
data: response,
index: index + 1,
arr: arr
};
})
);
}
}
upload.service.ts
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { HttpHeaders, HttpClient } from '@angular/common/http';
@Injectable({
providedIn: 'root'
})
export class UploadService {
constructor(private http: HttpClient) { }
private api = 'https://api.vimeo.com/me/videos';
private accessToken = 'yourapitoken';
createVideo(file: File): Observable<any> {
const body = {
name: file.name,
upload: {
approach: 'tus',
size: file.size
}
};
console.log(file);
const header: HttpHeaders = new HttpHeaders()
.set('Authorization', 'bearer ' + this.accessToken)
.set('Content-Type', 'application/json')
.set('Accept', 'application/vnd.vimeo.*+json;version=3.4');
return this.http.post(this.api, body, {
headers: header,
observe: 'response'
});
}
}
Upload the video
We’ll be using tus-js-client library for uploading the videos. Install the library & its type definitions and import the library.
npm install --save tus-js-client
npm install --save-dev @types/tus-js-client
import * as tus from 'tus-js-client';
Since we are using tus-js-client it will handle the process of uploading, we just have to specify the URL for uploading that we get in the previous successful response and set the retry delays according to your requirements and thats it.
Create a new tus.Upload
object with necessary parameters inside a function in upload service so that it can be called from component.
// Filename: upload.service.ts
public tusUpload(
file: uploadFiles,
i: number,
videoArray: uploadFiles[],
uploadArray: tus.Upload[],
success: any,
): tus.Upload {
const upload = new tus.Upload(file.video, {
uploadUrl: file.uploadURI,
endpoint: file.uploadURI,
retryDelays: [0, 1000, 3000, 5000],
onError: error => {
console.log('Failed: ' + file.video.name + error);
},
onProgress: (bytesUploaded, bytesTotal) => {
const percentage = ((bytesUploaded / bytesTotal) * 100).toFixed(2);
console.log(
'file: ' + i + ' of ' + (videoArray.length - 1) + ':',
bytesUploaded,
bytesTotal,
percentage + '%'
);
},
onSuccess: () => {
console.log('Download' + file.video.name + 'from' + upload.url);
if (i < videoArray.length - 1) {
uploadArray[i + 1].start();
} else {
success();
console.log('Videos uploaded successfully');
}
}
});
return upload;
}
Create one more function in the upload component or you can directly call the tusUpload
function after successfully creating the links.
// Filename: upload.component.ts
videoUpload() {
const success = () => {
console.log('after video upload section');
};
const upload: Array<any> = [];
for (let i = 0; i < this.pendingFiles.length; i++) {
upload.push(
this.upload.tusUpload(
this.pendingFiles[i],
i,
this.pendingFiles,
upload,
success
)
);
}
console.log('start video upload sequentially');
upload[0].start();
}
Note: Don’t forget to call videoUpload
function after generating links, otherwise the video wont start uploading.
Current progress:
Note: This is one way to implement, if you want less memory usage then you can skip the unnecessary array implementations.
If one video fails then next video can start to upload by using similar implementation of onSuccess
method of tus.Upload
in onError
method. DevTools console and network tab will come handy during debugging.😉
Originally published at zocada.