function Vimeo(client_id, client_secret, access_token) {
this._client_id = client_id;
this._client_secret = client_secret;
if (access_token) {
this.access_token = access_token;
}
}n/a
function FileStreamer(file_path, upload_endpoint, progress_callback) {
if (!file_path) {
throw new Error('You must provide a file path');
}
if (!upload_endpoint) {
throw new Error('You must provide an upload endpoint');
}
this._endpoint = url_module.parse(upload_endpoint);
this._path = file_path;
this.progress_callback = progress_callback;
}n/a
function Vimeo(client_id, client_secret, access_token) {
this._client_id = client_id;
this._client_secret = client_secret;
if (access_token) {
this.access_token = access_token;
}
}n/a
_applyDefaultRequestOptions = function (options) {
var request_options = {
protocol : options.protocol || module.exports.request_defaults.protocol,
host : options.hostname || module.exports.request_defaults.hostname,
port : options.port || module.exports.request_defaults.port,
method : options.method || module.exports.request_defaults.method,
headers : options.headers || {},
body : '',
path : options.path
};
var key = null;
// Apply the default headers
if (module.exports.request_defaults.headers) {
for (key in module.exports.request_defaults.headers) {
if (!request_options.headers[key]) {
request_options.headers[key] = module.exports.request_defaults.headers[key];
}
}
}
return request_options;
}n/a
_applyQuerystringParams = function (request_options, options) {
var querystring = '';
if (!options.query) {
return request_options.path;
}
// If we have parameters, apply them to the url
if (Object.keys(options.query).length) {
if (request_options.path.indexOf('?') < 0) {
// If the existing path does not contain any parameters, apply them as the only options
querystring = '?' + qs_module.stringify(options.query);
} else {
// If the user already added parameters to the url, we want to add them as additional parameters
querystring = '&' + qs_module.stringify(options.query);
}
}
return request_options.path + querystring;
}n/a
_buildRequestOptions = function (options) {
// set up the request object. we always use the options paramter first, and if no value is provided we fall back to request defaults
var request_options = this._applyDefaultRequestOptions(options);
// Apply the access tokens
if (this.access_token) {
request_options.headers.Authorization = 'Bearer ' + this.access_token;
} else if (this._client_id && this._client_secret) {
request_options.headers.Authorization = 'Basic ' + new Buffer(this._client_id + ':' + this._client_secret).toString('base64');
}
// Set proper headers for POST, PATCH and PUT bodies
if (['POST','PATCH','PUT','DELETE'].indexOf(request_options.method) !== -1 && !request_options.headers['Content-Type']) {
request_options.headers['Content-Type'] = 'application/json';
// Apply parameters to the url for GET requests
} else if (request_options.method === 'GET') {
request_options.path = this._applyQuerystringParams(request_options, options);
}
return request_options;
}n/a
_handleRequest = function (callback) {
return function (res) {
res.setEncoding('utf8');
var buffer = '';
res.on('readable', function () {
buffer += res.read() || '';
});
if (res.statusCode >= 400) {
// failed api calls should wait for the response to end and then call the callback with an error.
res.on('end', function () {
var err = new Error(buffer);
callback(err, buffer, res.statusCode, res.headers);
});
} else {
// successful api calls should wait for the response to end and then call the callback with the response body
res.on('end', function () {
try {
var body = buffer.length ? JSON.parse(buffer) : {};
} catch (e) {
return callback(buffer, buffer, res.statusCode, res.headers);
}
callback(null, body, res.statusCode, res.headers);
});
}
};
}n/a
accessToken = function (code, redirect_uri, fn) {
var _self = this;
this.request({
method : 'POST',
hostname : module.exports.request_defaults.hostname,
path : auth_endpoints.accessToken,
query : {
grant_type : 'authorization_code',
code : code,
redirect_uri : redirect_uri
},
headers : {
'Content-Type' : 'application/x-www-form-urlencoded'
}
}, function (err, body, status, headers) {
if (err) {
return fn(err, null, status, headers);
} else {
fn(null, body, status, headers);
}
});
}...
3. If the user accepts your app, they will be redirected back to your redirect\_uri with a ````code```` and ````state```` query
parameter (eg. http://yourredirect.com?code=abc&state=xyz).
1. You must validate that the ```status``` matches your status from step 1.
2. If the status is valid, you can exchange your code and redirect\_uri for an access token.
```JavaScript
// redirect_uri must be provided, and must match your configured uri
lib.accessToken(code, redirect_uri, function (err, token) {
if (err) {
return response.end("error\n" + err);
}
if (token.access_token) {
// At this state the code has been successfully exchanged for an access token
lib.access_token = token.access_token;
...buildAuthorizationEndpoint = function (redirect_uri, scope, state) {
var query = {
response_type : 'code',
client_id : this._client_id,
redirect_uri : redirect_uri
};
if (scope) {
if (Array.isArray(scope)) {
query.scope = scope.join(' ');
} else {
query.scope = scope;
}
} else {
query.scope = 'public';
}
if (state) {
query.state = state;
}
return module.exports.request_defaults.protocol + '//' + module.exports.request_defaults.hostname + auth_endpoints.authorization
+ '?' + qs_module.stringify(query);
}...
### Authenticated
1. Build a link to Vimeo so your users can authorize your app
```JavaScript
var url = lib.buildAuthorizationEndpoint(redirect_uri, scopes, state)
```
Name | Type | Description
-------------|----------|------------
redirect_uri | string | The uri the user is redirected to in step 3. This value must be provided to every step of the authorization
process including creating your app, building your authorization endpoint and exchanging your authorization code for an access
token
scope | array | An array of permissions your token needs to access. You can read more at https://developer.vimeo.com/api
/authentication#scopes
state | string | A value unique to this authorization request. You should generate it randomly, and validate it in step
3.
...generateClientCredentials = function (scope, fn) {
var query = {
grant_type : 'client_credentials',
}
if (scope) {
if (Array.isArray(scope)) {
query.scope = scope.join(' ');
} else {
query.scope = scope;
}
} else {
query.scope = 'public';
}
this.request({
method : 'POST',
hostname : module.exports.request_defaults.hostname,
path : auth_endpoints.clientCredentials,
query : query,
headers : {
'Content-Type' : 'application/x-www-form-urlencoded'
}
}, function (err, body, status, headers) {
if (err) {
return fn(err, null, status, headers);
} else {
fn(null, body, status, headers);
}
});
}...
### Unauthenticated
Unauthenticated API requests must generate an access token. You should not generate a new access token for each request, you should
request an access token once and use it forever.
```JavaScript
// scope is an array of permissions your token needs to access. You can read more at https://developer.vimeo.com/api/authentication
#supported-scopes
lib.generateClientCredentials(scope, function (err, access_token) {
if (err) {
throw err;
}
var token = access_token.access_token;
// Other useful information is included alongside the access token
...function vimeo_request(options, callback) {
var client = null;
// if a url was provided, build an options object
if (typeof options === "string") {
options = url_module.parse(options, true);
options.method = "GET";
}
// if we don't have a path at this point, error. a path is the only required field. we have defaults for everything else important
if (typeof options.path !== "string") {
return callback(new Error('You must provide an api path'));
}
// Add leading slash to path if missing
if (options.path.charAt(0) !== '/') {
options.path = '/' + options.path;
}
// Turn the provided options into options that are valid for client.request
var request_options = this._buildRequestOptions(options);
// Locate the proper client from the request protocol
client = request_options.protocol === 'https:' ? https_module : http_module;
// Write the request body
if (['POST','PATCH','PUT','DELETE'].indexOf(request_options.method) !== -1) {
if (request_options.headers['Content-Type'] === 'application/json') {
request_options.body = JSON.stringify(options.query);
} else {
request_options.body = qs_module.stringify(options.query);
}
if (request_options.body) {
request_options.headers['Content-Length'] = Buffer.byteLength(request_options.body, 'utf8');
} else {
request_options.headers['Content-Length'] = 0;
}
}
// Perform the vimeo api request
var req = client.request(request_options, this._handleRequest(callback));
// Write the request body
if (request_options.body) {
req.write(request_options.body);
}
// notify user of any weird connection/request errors
req.on('error', function(e) {
callback(e);
});
// send the request
req.end();
}...
error | error | If this is provided, it means the request failed. The other parameters may, or may not contain additional
information. You should check the status code to understand exactly what error you have encountered.
body | object | The parsed request body. All responses are JSON so we parse this for you, and give you the result.
status_code | number | The HTTP status code of the response. This partially informs you about the success of your API request.
headers | object | An object containing all of the response headers.
lib.request(/*options*/{
// This is the path for the videos contained within the staff picks channels
path : '/channels/staffpicks/videos',
// This adds the parameters to request page two, and 10 items per page
query : {
page : 2,
per_page : 10
}
...streamingUpload = function (path, video_uri, callback, progress_callback) {
var _self = this;
if (typeof video_uri === 'function') {
progress_callback = callback;
callback = video_uri;
video_uri = undefined;
}
var options = {
method : video_uri ? 'PUT' : 'POST',
path : video_uri ? video_uri + '/files' : '/me/videos',
query : {
type : 'streaming'
}
};
this.request(options, function (err, ticket, status, headers) {
if (err) {
return callback(err);
}
var file = new FileStreamer(path, ticket.upload_link_secure, progress_callback);
file.ready(function () {
_self.request({
method : 'DELETE',
path : ticket.complete_uri
}, callback);
});
file.error(callback);
file.upload();
});
}...
file | string | Full path to the upload file on the local system
video_uri | string | (Optional) Uri of an existing video. If provided, the uploaded video will replace the source file of this
video.
callback | function | A callback that will be executed when the upload is comple, or has failed. It will match the callback of
an [API request](#callback).
progress_callback | function | A callback that will be executed periodically during the file upload. This callback receives two
parameters, the total bytes uploaded and the total file size. Note that `console.log` is slow, and frequent logs can negatively
impact your performance speed.
**Upload**
lib.streamingUpload('/home/aaron/Downloads/ada.mp4', function (error,
body, status_code, headers) {
if (error) {
throw error;
}
lib.request(headers.location, function (error, body, status_code, headers) {
console.log(body);
});
...function FileStreamer(file_path, upload_endpoint, progress_callback) {
if (!file_path) {
throw new Error('You must provide a file path');
}
if (!upload_endpoint) {
throw new Error('You must provide an upload endpoint');
}
this._endpoint = url_module.parse(upload_endpoint);
this._path = file_path;
this.progress_callback = progress_callback;
}n/a
_closeFile = function () {
if (this._fd) {
fs_module.close(this._fd, function (close_err) {
if (close_err) {
this._error(close_err);
}
});
this._fd = null;
}
}...
FileStreamer.prototype._ready = function () {
var _self = this;
// If we think we are ready to complete, check with the server and see if they have the whole file
this._getNewStart(function (err, start) {
if (err) {
// If the check fails, close the file and error out immediately
_self._closeFile();
return _self._error(err);
}
if (start >= _self._file_size) {
// If the server says they have the whole file, close it and then return back to the user
_self._closeFile()
_self._user_ready();
..._error = function (error) {
this.error = function (fn) {
fn(error);
}
}...
var _self = this;
// If we think we are ready to complete, check with the server and see if they have the whole file
this._getNewStart(function (err, start) {
if (err) {
// If the check fails, close the file and error out immediately
_self._closeFile();
return _self._error(err);
}
if (start >= _self._file_size) {
// If the server says they have the whole file, close it and then return back to the user
_self._closeFile()
_self._user_ready();
} else {
..._getNewStart = function (next) {
var _self = this;
this._upload_endpoint_request({
method : 'PUT',
headers : {
'Content-Range' : 'bytes */*',
'Content-Type' : 'application/octet-stream'
}
}, function (err, status, headers) {
if (err) {
return next(err);
}
if (status === 308) {
return next(null, parseInt(headers.range.split('-')[1]));
} else {
return next(new Error('Invalid http status returned from range query: [' + status + ']'));
}
}).end();
}...
*
* Do not call this outside of the library.
*/
FileStreamer.prototype._ready = function () {
var _self = this;
// If we think we are ready to complete, check with the server and see if they have the whole file
this._getNewStart(function (err, start) {
if (err) {
// If the check fails, close the file and error out immediately
_self._closeFile();
return _self._error(err);
}
if (start >= _self._file_size) {
..._putFile = function (start, callback) {
var _self = this;
var file = fs_module.createReadStream(_self._path, {
start : start
});
file.on('error', function (err) {
callback(err);
});
var uploaded_size = start || 0;
file.on('data', function(chunk) {
uploaded_size += chunk.length || 0;
if (_self.progress_callback) {
_self.progress_callback(uploaded_size, _self._file_size);
}
});
var headers = {
'Content-Length' : _self._file_size,
'Content-Type' : 'video/mp4'
};
headers['Content-Range'] = 'bytes ' + start + '-' + _self._file_size + '/' + _self._file_size;
var req = _self._upload_endpoint_request({
method : 'PUT',
headers : headers
}, callback);
file.pipe(req);
}...
/**
* Send a file chunk, starting at byte [start] and ending at the end of the file
*
* @param {Number} start
*/
FileStreamer.prototype._streamChunk = function (start) {
var _self = this;
_self._putFile(start, function (put_err, code, headers) {
// Catches a rare vimeo server bug and exits out early
if (put_err && code) {
_self._closeFile();
return _self._error(put_err);
}
..._ready = function () {
var _self = this;
// If we think we are ready to complete, check with the server and see if they have the whole file
this._getNewStart(function (err, start) {
if (err) {
// If the check fails, close the file and error out immediately
_self._closeFile();
return _self._error(err);
}
if (start >= _self._file_size) {
// If the server says they have the whole file, close it and then return back to the user
_self._closeFile()
_self._user_ready();
} else {
// If the server does not have the whole file, upload from where we left off
_self._streamChunk(start);
}
});
}...
// Catches a rare vimeo server bug and exits out early
if (put_err && code) {
_self._closeFile();
return _self._error(put_err);
}
_self._ready();
});
};
/**
* Make the HTTP put request sending a part of a file up to the upload server
*
* @param {Number} start The first byte of the file
..._streamChunk = function (start) {
var _self = this;
_self._putFile(start, function (put_err, code, headers) {
// Catches a rare vimeo server bug and exits out early
if (put_err && code) {
_self._closeFile();
return _self._error(put_err);
}
_self._ready();
});
}...
if (start >= _self._file_size) {
// If the server says they have the whole file, close it and then return back to the user
_self._closeFile()
_self._user_ready();
} else {
// If the server does not have the whole file, upload from where we left off
_self._streamChunk(start);
}
});
};
/**
* Assign a callback to be called whenever the file is done uploading.
*
..._upload_endpoint_request = function (options, callback) {
var request_options = {
protocol : this._endpoint.protocol,
host : this._endpoint.hostname,
port : this._endpoint.port,
query : this._endpoint.query,
headers : options.headers,
path : this._endpoint.path,
method : options.method
};
var client = request_options.protocol === 'https:' ? https_module : http_module;
var req = client.request(request_options);
req.on('response', function (res) {
res.setEncoding('utf8');
var buffer = '';
res.on('readable', function () {
buffer += res.read();
});
if (res.statusCode > 399) {
// failed api calls should wait for the response to end and then call the callback with an error.
res.on('end', function () {
callback(new Error('[' + buffer + ']'), res.statusCode, res.headers);
});
} else {
// successful api calls should wait for the response to end and then call the callback with the response body
res.on('end', function () {
callback(null, res.statusCode, res.headers);
});
}
});
// notify user of any weird connection/request errors
req.on('error', function(e) {
callback(e);
});
return req;
}...
var headers = {
'Content-Length' : _self._file_size,
'Content-Type' : 'video/mp4'
};
headers['Content-Range'] = 'bytes ' + start + '-' + _self._file_size + '/' + _self._file_size
;
var req = _self._upload_endpoint_request({
method : 'PUT',
headers : headers
}, callback);
file.pipe(req);
};
..._user_ready = function () {
this.ready = function (fn) {
fn();
}
}...
_self._closeFile();
return _self._error(err);
}
if (start >= _self._file_size) {
// If the server says they have the whole file, close it and then return back to the user
_self._closeFile()
_self._user_ready();
} else {
// If the server does not have the whole file, upload from where we left off
_self._streamChunk(start);
}
});
};
...error = function (fn) {
this._error = fn;
}n/a
ready = function (fn) {
this._user_ready = fn;
}n/a
upload = function () {
var _self = this;
fs_module.stat(_self._path, function (stat_err, stats) {
if (stat_err) {
return _self._error(stat_err);
}
_self._file_size = stats.size;
fs_module.open(_self._path, 'r', function(open_err, fd) {
if (open_err) {
return this._error(open_err);
}
_self._fd = fd;
_self._streamChunk(0);
});
});
}n/a
function Vimeo(client_id, client_secret, access_token) {
this._client_id = client_id;
this._client_secret = client_secret;
if (access_token) {
this.access_token = access_token;
}
}n/a