OpenVeo Publish server

API Docs for: 8.0.0
Show:

File: app/server/providers/mediaPlatforms/youtube/YoutubeResumableUpload.js

'use strict';

/**
 * @module providers
 */

var fs = require('fs');
var request = require('request');
var EventEmitter = require('events').EventEmitter;
var mime = require('mime');
var util = require('util');

/**
 * TODO
 *
 * @class ResumableUpload
 * @constructor
 */
function ResumableUpload() {
  Object.defineProperties(this, {

    /**
     * TODO
     *
     * @property byteCount
     * @type Number
     */
    byteCount: {value: 0, writable: true},

    /**
     * TODO
     *
     * @property tokens
     * @type Object
     */
    tokens: {value: {}, writable: true},

    /**
     * TODO
     *
     * @property filepath
     * @type String
     */
    filepath: {value: '', writable: true},

    /**
     * TODO
     *
     * @property metadata
     * @type Object
     */
    metadata: {value: {}, writable: true},

    /**
     * TODO
     *
     * @property retry
     * @type Number
     */
    retry: {value: -1, writable: true},

    /**
     * TODO
     *
     * @property host
     * @type String
     */
    host: {value: 'www.googleapis.com'},

    /**
     * TODO
     *
     * @property api
     * @type String
     */
    api: {value: '/upload/youtube/v3/videos'},

    /**
     * TODO
     *
     * @property stats
     * @type Object
     */
    stats: {value: {}, writable: true}
  });
}

util.inherits(ResumableUpload, EventEmitter);

/**
 * Inits the upload by POSTing google for an upload URL (saved to self.location).
 *
 * @method upload
 */
ResumableUpload.prototype.upload = function() {
  var self = this;
  var options = {
    url: 'https://' + self.host + self.api + '?uploadType=resumable&part=snippet,status,contentDetails',
    headers: {
      Host: self.host,
      Authorization: 'Bearer ' + self.tokens.access_token
    },
    body: JSON.stringify(self.metadata)
  };
  options.headers['Content-Length'] = Buffer.from(JSON.stringify(self.metadata)).length;
  options.headers['Content-Type'] = 'application/json';
  options.headers['X-Upload-Content-Length'] = self.stats.size;
  options.headers['X-Upload-Content-Type'] = mime.lookup(self.filepath);

  // Send request and start upload if success
  request.post(options, function(err, res, body) {
    if (err || !res.headers.location) {
      self.emit('error', new Error(err));
      if ((self.retry > 0) || (self.retry <= -1)) {
        self.emit('progress', 'Retrying ...');
        self.retry--;
        self.upload();// retry
      } else {
        if (err)
          self.emit('error', new Error(err));
        else
          self.emit('error', new Error('max try reached'));
        return;
      }
    } else {
      self.location = res.headers.location;
      self.send();
    }
  });
};

/**
 * Pipes uploadPipe to self.location (Google's Location header).
 *
 * @method send
 */
ResumableUpload.prototype.send = function() {
  var self = this;
  var uploadPipe;

  // self.location becomes the Google-provided URL to PUT to
  var options = {
    url: self.location,
    headers: {
      Authorization: 'Bearer ' + self.tokens.access_token
    }
  };
  options.headers['Content-Length'] = self.stats.size - self.byteCount;
  options.headers['Content-Type'] = mime.lookup(self.filepath);

  try {
    // creates file stream, pipes it to self.location
    uploadPipe = fs.createReadStream(self.filepath, {
      start: self.byteCount,
      end: self.stats.size
    });
  } catch (e) {
    self.emit('error', new Error(e));
    return;
  }

  var health = setInterval(function() {
    self.getProgress(function(err, res, body) {
      if (!err && typeof res.headers.range !== 'undefined') {
        self.emit('progress', res.headers.range.substring(8));
      }
    });
  }, 5000);

  uploadPipe.pipe(request.put(options, function(error, response, body) {
    clearInterval(health);
    if (!error) {
      self.emit('success', body);
      return;
    }
    self.emit('error', new Error(error));
    if ((self.retry > 0) || (self.retry <= -1)) {
      self.retry--;
      self.getProgress(function(err, res, b) {
        if (res && res.headers && typeof res.headers.range !== 'undefined') {
          self.byteCount = res.headers.range.substring(8); // parse response
        } else {
          self.byteCount = 0;
        }
        self.send();
      });
    }
  }));
};

/**
 * TODO.
 *
 * @method getProgress
 * @param {} handler TODO
 */
ResumableUpload.prototype.getProgress = function(handler) {
  var self = this;
  var options = {
    url: self.location,
    headers: {
      Authorization: 'Bearer ' + self.tokens.access_token
    }
  };
  options.headers['Content-Length'] = 0;
  options.headers['Content-Range'] = 'bytes */' + self.stats.size;
  request.put(options, handler);
};

module.exports = ResumableUpload;