Skip to content

Instantly share code, notes, and snippets.

@jcipriano
Created September 30, 2016 18:59
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jcipriano/91bff4cb4ea51c355453161b6da02986 to your computer and use it in GitHub Desktop.
Save jcipriano/91bff4cb4ea51c355453161b6da02986 to your computer and use it in GitHub Desktop.
Twitter Media Upload Async
var request = require('request');
var fs = require('fs');
var MEDIA_ENDPOINT_URL = 'https://upload.twitter.com/1.1/media/upload.json'
var POST_TWEET_URL = 'https://api.twitter.com/1.1/statuses/update.json'
var OAUTH = {
consumer_key: '',
consumer_secret: '',
token: '',
token_secret: ''
}
/**
* Video Tweet constructor
**/
var VideoTweet = function (data) {
var self = this;
self.file_path = data.file_path;
self.tweet_text = data.tweet_text;
self.total_bytes = undefined;
self.media_id = undefined;
self.processing_info = undefined;
// retreives file info and inits upload on complete
fs.stat(self.file_path, function (error, stats) {
self.total_bytes = stats.size
self.upload_init();
});
};
/**
* Inits media upload
*/
VideoTweet.prototype.upload_init = function () {
console.log('INIT');
var self = this;
form_data = {
'command': 'INIT',
'media_type': 'video/mp4',
'total_bytes': self.total_bytes,
'media_category': 'tweetvideo'
}
// inits media upload
request.post({url: MEDIA_ENDPOINT_URL, oauth: OAUTH, formData: form_data}, function (error, response, body) {
data = JSON.parse(body)
// store media ID for later reference
self.media_id = data.media_id_string;
// start appening media segments
self.upload_append();
});
}
/**
* Uploads/appends video file segments
*/
VideoTweet.prototype.upload_append = function () {
var buffer_length = 5000000;
var buffer = new Buffer(buffer_length);
var bytes_sent = 0;
var self = this;
// open and read video file
fs.open(self.file_path, 'r', function(error, file_data) {
var bytes_read, data,
segment_index = 0,
segments_completed = 0;
// upload video file in chunks
while (bytes_sent < self.total_bytes) {
console.log('APPEND');
bytes_read = fs.readSync(file_data, buffer, 0, buffer_length, null);
data = bytes_read < buffer_length ? buffer.slice(0, bytes_read) : buffer;
var form_data = {
command: 'APPEND',
media_id: self.media_id,
segment_index: segment_index,
media_data: data.toString('base64')
};
request.post({url: MEDIA_ENDPOINT_URL, oauth: OAUTH, formData: form_data}, function () {
segments_completed = segments_completed + 1;
console.log('segment_completed');
if (segments_completed == segment_index) {
console.log('Upload chunks complete');
self.upload_finalize();
}
});
bytes_sent = bytes_sent + buffer_length;
segment_index = segment_index + 1;
}
});
}
/**
* Finalizes media segments uploaded
*/
VideoTweet.prototype.upload_finalize = function () {
console.log('FINALIZE');
var self = this;
form_data = {
'command': 'FINALIZE',
'media_id': self.media_id
}
// finalize uploaded chunck and check processing status on compelete
request.post({url: MEDIA_ENDPOINT_URL, oauth: OAUTH, formData: form_data}, function(error, response, body) {
data = JSON.parse(body)
self.check_status(data.processing_info);
});
}
/**
* Checks status of uploaded media
*/
VideoTweet.prototype.check_status = function (processing_info) {
var self = this;
// if response does not contain any processing_info, then video is ready
if (!processing_info) {
self.tweet();
return;
}
console.log('STATUS');
request_params = {
'command': 'STATUS',
'media_id': self.media_id
}
// check processing status
request.get({url: MEDIA_ENDPOINT_URL, oauth: OAUTH, qs: request_params}, function(error, response, body) {
data = JSON.parse(body)
console.log('Media processing status is ' + processing_info.state);
if (processing_info.state == 'succeeded') {
self.tweet();
return
}
else if (processing_info.state == 'failed') {
return;
}
// check status again after specified duration
var timeout_length = data.processing_info.check_after_secs ? data.processing_info.check_after_secs * 1000 : 0;
console.log('Checking after ' + timeout_length + ' milliseconds');
setTimeout(function () {
self.check_status(data.processing_info)
}, timeout_length);
});
}
/**
* Tweets text with attached media
*/
VideoTweet.prototype.tweet = function () {
var self = this;
request_data = {
'status': self.tweet_text,
'media_ids': self.media_id
}
// publish Tweet
request.post({url: POST_TWEET_URL, oauth: OAUTH, form: request_data}, function(error, response, body) {
data = JSON.parse(body)
console.log(data);
});
}
/**
* Instantiates a VideoTweet
*/
videoTweet = new VideoTweet({
file_path: 'oceans.m4v',
tweet_text: 'I just uploaded a video with the @TwitterAPI and #nodejs.'
});
@pietrop
Copy link

pietrop commented Sep 25, 2018

Thanks for this example script!

Got an error when running it due to a buffer deprecation warning

DeprecationWarning: Buffer() is deprecated due to security and usability issues. Please use the Buffer.alloc(), Buffer.allocUnsafe(), or Buffer.from() methods instead. APPEND

Thought I'd leave a link here on Porting to the Buffer.from()/Buffer.alloc() API in case anyone else runs into the same issue in case it helps.

@mvrozanti
Copy link

INIT
(node:15247) [DEP0005] DeprecationWarning: Buffer() is deprecated due to security and usability issues. Please use the Buffer.alloc(), Buffer.allocUnsafe(), or Buffer.from() methods instead.
APPEND
/mnt/4ADE1465DE144C17/Programming/javascript/babui/node_modules/form-data/lib/form_data.js:226
  } else if (options.filename || value.name || value.path) {
                                       ^

TypeError: Cannot read property 'name' of undefined
    at FormData._getContentDisposition (/mnt/4ADE1465DE144C17/Programming/javascript/babui/node_modules/form-data/lib/form_data.js:226:40)
    at FormData._multiPartHeader (/mnt/4ADE1465DE144C17/Programming/javascript/babui/node_modules/form-data/lib/form_data.js:177:33)
    at FormData.append (/mnt/4ADE1465DE144C17/Programming/javascript/babui/node_modules/form-data/lib/form_data.js:70:21)
    at appendFormValue (/mnt/4ADE1465DE144C17/Programming/javascript/babui/node_modules/request/request.js:326:21)
    at Request.init (/mnt/4ADE1465DE144C17/Programming/javascript/babui/node_modules/request/request.js:337:11)
    at new Request (/mnt/4ADE1465DE144C17/Programming/javascript/babui/node_modules/request/request.js:127:8)
    at request (/mnt/4ADE1465DE144C17/Programming/javascript/babui/node_modules/request/index.js:53:10)
    at Function.post (/mnt/4ADE1465DE144C17/Programming/javascript/babui/node_modules/request/index.js:61:12)
    at /mnt/4ADE1465DE144C17/Programming/javascript/babui/upload-async.js:100:15
    at FSReqCallback.args [as oncomplete] (fs.js:146:20)

This does not work

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment