You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

212 lines
3.8 KiB

6 years ago
'use strict';
var path = require('path');
var url = require('url');
var caw = require('caw');
var concatStream = require('concat-stream');
var decompress = require('gulp-decompress');
var eachAsync = require('each-async');
var filenamify = require('filenamify');
var got = require('got');
var isUrl = require('is-url');
var objectAssign = require('object-assign');
var readAllStream = require('read-all-stream');
var rename = require('gulp-rename');
var streamCombiner = require('stream-combiner2');
var PassThrough = require('readable-stream/passthrough');
var Vinyl = require('vinyl');
var vinylFs = require('vinyl-fs');
var Ware = require('ware');
/**
* Initialize a new `Download`
*
* @param {Object} opts
* @api public
*/
function Download(opts) {
if (!(this instanceof Download)) {
return new Download(opts);
}
this.opts = objectAssign({encoding: null}, opts);
this.ware = new Ware();
}
module.exports = Download;
/**
* Get or set URL to download
*
* @param {String} url
* @param {String} dest
* @api public
*/
Download.prototype.get = function (url, dest) {
if (!arguments.length) {
return this._get;
}
this._get = this._get || [];
this._get.push({
url: url,
dest: dest
});
return this;
};
/**
* Get or set the destination folder
*
* @param {String} dir
* @api public
*/
Download.prototype.dest = function (dir) {
if (!arguments.length) {
return this._dest;
}
this._dest = dir;
return this;
};
/**
* Rename the downloaded file
*
* @param {Function|String} name
* @api public
*/
Download.prototype.rename = function (name) {
if (!arguments.length) {
return this._rename;
}
this._rename = name;
return this;
};
/**
* Add a plugin to the middleware stack
*
* @param {Function} plugin
* @api public
*/
Download.prototype.use = function (plugin) {
this.ware.use(plugin);
return this;
};
/**
* Run
*
* @param {Function} cb
* @api public
*/
Download.prototype.run = function (cb) {
cb = cb || function () {};
var files = [];
eachAsync(this.get(), function (get, i, done) {
if (!isUrl(get.url)) {
done(new Error('Specify a valid URL'));
return;
}
var protocol = url.parse(get.url).protocol;
if (protocol) {
protocol = protocol.slice(0, -1);
}
var agent = caw(this.opts.proxy, {protocol: protocol});
var stream = got.stream(get.url, objectAssign(this.opts, {agent: agent}));
stream.on('response', function (res) {
stream.headers = res.headers;
stream.statusCode = res.statusCode;
this.ware.run(stream, get.url);
}.bind(this));
var hasHttpError = false;
readAllStream(stream, null, function (err, data) {
if (hasHttpError) {
return;
}
if (err) {
if (err instanceof got.HTTPError) {
hasHttpError = true;
}
done(err);
return;
}
var dest = get.dest || this.dest();
var fileStream = this.createStream(this.createFile(get.url, data), dest);
fileStream.on('error', done);
fileStream.pipe(concatStream({encoding: 'object'}, function (items) {
files = files.concat(items);
done();
}));
}.bind(this));
}.bind(this), function (err) {
if (err) {
cb(err);
return;
}
cb(null, files);
});
};
/**
* Create vinyl file
*
* @param {String} url
* @param {Buffer} data
* @api private
*/
Download.prototype.createFile = function (url, data) {
return objectAssign(new Vinyl({
contents: data,
path: filenamify(path.basename(url))
}), {url: url});
};
/**
* Create stream
*
* @param {Object} file
* @param {String} dest
* @api private
*/
Download.prototype.createStream = function (file, dest) {
var stream = new PassThrough({objectMode: true});
var streams = [stream];
stream.end(file);
if (this.opts.extract) {
streams.push(decompress(this.opts));
}
if (this.rename()) {
streams.push(rename(this.rename()));
}
if (dest) {
streams.push(vinylFs.dest(dest, this.opts));
}
return streamCombiner.obj(streams);
};