Skip to content

Commit

Permalink
Merge pull request fent#1 from andrewrk/master
Browse files Browse the repository at this point in the history
expose dowloadFromInfo and drop some dependencies
  • Loading branch information
fent committed Jul 3, 2014
2 parents 786443d + c76c9df commit a4b503c
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 87 deletions.
13 changes: 10 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ ytdl(url, { filter: function(format) { return format.container === 'mp4'; } })

The returned readable stream emits these additional events.

### Event: 'info'
#### Event: 'info'
* `Object` - Info.
* `Object` - Format.

Expand All @@ -48,10 +48,17 @@ Use this if you only want to get metainfo from a video.

`options` gets passed to the `request()`, it can also have a `downloadURL` property set to `true` if you want ytdl to include the download url instead of the regular one. In some cases, a signature needs to be deciphered, and will require ytdl to make additional requests.

### ytdl.cache
### ytdl.downloadFromInfo(info, options)

A [memory cache](https://github.com/hij1nx/EventVat) is used to store information about recently retrieved videos. This is used to prevent double requests on videos that you want to retrieve the info of, and then download.
Once you have received metadata from a video with the `getInfo` function,
you may pass that `info`, along with other `options` to `downloadFromInfo`.

The returned readable stream emits these additional events:

#### Event: 'format'
* `Object` - Format.

Emitted when a format metadata has been chosen. `format.size` will also be available.

# Install

Expand Down
42 changes: 7 additions & 35 deletions lib/crequest.js
Original file line number Diff line number Diff line change
@@ -1,39 +1,11 @@
var request = require('request');
var EventVat = require('eventvat');

var ytdl = {};
// Circular dependency.
process.nextTick(function() {
// Cache for recently looked up video infos.
ytdl = require('./index');
});

/**
* Makes a request and caches the body so that additional requests
* to the same url don't require network IO.
*
* @param {Object|String} options
* @param {Function} callback
*/
module.exports = function(options, callback) {
var url = options.url || options.uri || options;
if (ytdl.cache && ytdl.cache.exists(url)) {
process.nextTick(function() {
callback(null, ytdl.cache.get(url));
});
} else {
request(options, function(err, res, body) {
if (err) return callback(err);
if (res.statusCode !== 200) {
return callback(new Error('status code ' + res.statusCode));
}
if (ytdl.cache) {
ytdl.cache.set(url, body);
}
callback(null, body);
});
}
request(options, function(err, res, body) {
if (err) return callback(err);
if (res.statusCode !== 200) {
return callback(new Error('status code ' + res.statusCode));
}
callback(null, body);
});
};

// Export cache.
module.exports.cache = new EventVat({ autoexpire: 30 });
112 changes: 71 additions & 41 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -1,65 +1,95 @@
var http = require('http');
var streamify = require('streamify');
var request = require('request');
var _ = require('underscore');
var getInfo = require('./info');
var crequest = require('./crequest');
var util = require('./util');
var http = require('http');
var PassThrough = require('stream').PassThrough;
var request = require('request');
var _ = require('underscore');
var getInfo = require('./info');
var crequest = require('./crequest');
var util = require('./util');

module.exports = ytdl;
ytdl.getInfo = getInfo;
ytdl.downloadFromInfo = downloadFromInfo;

/**
* @param {String} link
* @param {Object} options
* @return {ReadableStream}
*/
var ytdl = module.exports = function ytdl(link, options) {
var stream = streamify({
superCtor: http.ClientResponse,
readable: true,
writable: false
});

function ytdl(link, options) {
options = options || {};
options.downloadURL = true;

var stream = new PassThrough();

getInfo(link, options, function(err, info) {
if (err) {
stream.emit('error', err);
stream.readable = false;
return;
}

var format = util.chooseFormat(info.formats, options);
if (format instanceof Error) {
stream.emit('error', format);
return;
}

var requestOptions = _.clone(options);
requestOptions.url = format.url;
if (requestOptions.range) {
requestOptions.url += '&range=' + requestOptions.range;
}
delete requestOptions.quality;
delete requestOptions.range;
delete requestOptions.filter;

// Start downloading the video.
var req = request(requestOptions);
req.on('response', function(res) {
if (res.statusCode !== 200) {
stream.emit('error', new Error('status code ' + res.statusCode));
downloadFromInfoCallback(info, options, function(err, format, videoStream) {
if (err) {
stream.emit('error', err);
return;
}

format.size = res.headers['content-length'];
stream.emit('info', info, format);
videoStream.pipe(stream);
});
stream.resolve(req);
});

return stream;
};
}

ytdl.getInfo = getInfo;
ytdl.cache = crequest.cache;
function downloadFromInfoCallback(info, options, callback) {
options = options || {};

var format = util.chooseFormat(info.formats, options);
if (format instanceof Error) {
// The caller expects this function to be async.
setImmediate(callbackWithFormatError);
return;
}

var requestOptions = _.clone(options);
requestOptions.url = format.url;
if (requestOptions.range) {
requestOptions.url += '&range=' + requestOptions.range;
}
delete requestOptions.quality;
delete requestOptions.range;
delete requestOptions.filter;

// Start downloading the video.
var req = request(requestOptions);
req.on('error', callback);
req.on('response', function(res) {
if (res.statusCode !== 200) {
callback(new Error('status code ' + res.statusCode));
return;
}

format.size = res.headers['content-length'];
callback(null, format, req);
});

function callbackWithFormatError() {
callback(format);
}
}

function downloadFromInfo(info, options) {
var stream = new PassThrough();
options = options || {};

downloadFromInfoCallback(info, options, function(err, format, videoStream) {
if (err) {
stream.emit('error', err);
return;
}

stream.emit('format', format);
videoStream.pipe(stream);
});

return stream;
}
19 changes: 11 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
{
"name": "ytdl-core",
"description": "Youtube video downloader in pure javascript.",
"keywords": ["youtube", "video", "download"],
"keywords": [
"youtube",
"video",
"download"
],
"version": "0.1.0",
"repository": {
"type": "git",
Expand All @@ -13,20 +17,19 @@
"test": "mocha -R spec -t 2000 test/*-test.js"
},
"dependencies": {
"eventvat": "~0.2.1",
"jstream": "~0.2.7",
"request": "~2.36.0",
"streamify": "~0.2.0",
"underscore": "~1.6.0"
},
"devDependencies": {
"mocha": "*",
"nock": "~0.34.1",
"stream-equal": "~0.1.0"

},
"licenses": [{
"type": "MIT",
"url" : "http://github.com/fent/node-ytdl-core/raw/master/LICENSE"
}]
"licenses": [
{
"type": "MIT",
"url": "http://github.com/fent/node-ytdl-core/raw/master/LICENSE"
}
]
}

0 comments on commit a4b503c

Please sign in to comment.