Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

ytdl-core

Package Overview
Dependencies
Maintainers
1
Versions
186
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

ytdl-core - npm Package Compare versions

Comparing version 0.9.0 to 0.9.1

test/files/videos/_HSylqgVYQI/dashmpd.xml

22

lib/index.js

@@ -14,6 +14,3 @@ var PassThrough = require('stream').PassThrough;

var ytdl = module.exports = function ytdl(link, options) {
options = options || {};
var stream = new PassThrough();
stream.destroy = function() { stream._isDestroyed = true; };
var stream = createStream();
getInfo(link, options, function(err, info) {

@@ -38,2 +35,9 @@ if (err) {

function createStream() {
var stream = new PassThrough();
stream.destroy = function() { stream._isDestroyed = true; };
return stream;
}
/**

@@ -47,2 +51,3 @@ * Chooses a format to download.

function downloadFromInfoCallback(stream, info, options) {
options = options || {};
var format = util.chooseFormat(info.formats, options);

@@ -88,6 +93,6 @@ if (format instanceof Error) {

var req = myrequest(url, options.requestOptions);
var myres;
stream.destroy = function() {
req.abort();
stream.emit('abort');
if (myres) {

@@ -97,2 +102,3 @@ myres.destroy();

}
stream.emit('abort');
};

@@ -122,2 +128,4 @@

});
stream.emit('request', req);
}

@@ -135,5 +143,3 @@

ytdl.downloadFromInfo = function(info, options) {
var stream = new PassThrough();
stream.destroy = function() { stream._isDestroyed = true; };
options = options || {};
var stream = createStream();
setImmediate(function() {

@@ -140,0 +146,0 @@ downloadFromInfoCallback(stream, info, options);

@@ -1,2 +0,2 @@

var format = require('url').format;
var urllib = require('url');
var querystring = require('querystring');

@@ -47,6 +47,8 @@ var sax = require('sax');

// Check if this video exists.
var unavailableMsg = util.between(body, '<h1 id="unavailable-message" class="message">', '</h1>');
if (unavailableMsg) {
if (unavailableMsg.trim() == 'This video does not exist.') {
return callback(new Error('Video not found'));
var unavailableMsg = util.between(body, '<div id="player-unavailable"', '>');
if (!/\bhid\b/.test(util.between(unavailableMsg, 'class="', '"'))) {
unavailableMsg = util.between(body, '<h1 id="unavailable-message" class="message">', '</h1>').trim();
// Getting around age restricted videos.
if (unavailableMsg !== 'Content Warning') {
return callback(new Error(unavailableMsg));
}

@@ -118,3 +120,3 @@ }

}
var url = format({
var url = urllib.format({
protocol: 'https',

@@ -138,2 +140,4 @@ host: INFO_HOST,

info = config.args;
} else if (info.requires_purchase === '1') {
return callback(new Error(info.ypc_video_rental_bar_text));
}

@@ -170,3 +174,4 @@

config.args.dashmpd || info.dashmpd || info.hlsvp) {
sig.getTokens(config.assets.js, options, function(err, tokens) {
var html5playerfile = urllib.resolve(VIDEO_URL, config.assets.js);
sig.getTokens(html5playerfile, options, function(err, tokens) {
if (err) return callback(err);

@@ -292,3 +297,3 @@

var req = myrequest(url, options.requestOptions);
var req = myrequest(urllib.resolve(VIDEO_URL, url), options.requestOptions);
req.on('error', callback);

@@ -318,3 +323,3 @@ req.on('response', function(res) {

var myrequest = options.request || request;
myrequest(url, options.requestOptions, function(err, body) {
myrequest(urllib.resolve(VIDEO_URL, url), options.requestOptions, function(err, body) {
if (err) return callback(err);

@@ -321,0 +326,0 @@

@@ -27,3 +27,3 @@ var http = require('http');

if (options) { util.objectAssign(parsed, options, true); }
util.objectAssign(parsed, options, true);
var req = httpLib.get(parsed);

@@ -30,0 +30,0 @@ if (callback) {

@@ -16,3 +16,3 @@ var fs = require('fs');

* @param {Object} options
* @param {Function(!Error, Array.<String>)}
* @param {Function(!Error, Array.<String>)} callback
*/

@@ -27,3 +27,3 @@ exports.getTokens = function(html5playerfile, options, callback) {

} else {
console.warn('could not extract html5player key:', html5playerfile);
console.warn('Could not extract html5player key:', html5playerfile);
}

@@ -33,3 +33,2 @@ if (cachedTokens) {

} else {
html5playerfile = url.resolve(VIDEO_URL, html5playerfile);
var myrequest = options.request || request;

@@ -40,15 +39,12 @@ myrequest(html5playerfile, options.requestOptions, function(err, body) {

var tokens = exports.extractActions(body);
if (!tokens || !tokens.length) {
if (key && (!tokens || !tokens.length)) {
if (options.debug) {
var filename = key + '.js';
var filepath = path.resolve(
__dirname, '../test/files/html5player/' + filename);
fs.writeFile(filepath, body);
__dirname, '../test/files/html5player/' + key + '.js');
fs.writeFileSync(filepath, body);
var html5player = require('../test/html5player.json');
if (!html5player[key]) {
html5player[key] = [];
fs.writeFile(
path.resolve(__dirname, '../test/html5player.json'),
JSON.stringify(html5player, null, 2));
}
html5player[key] = [];
fs.writeFileSync(
path.resolve(__dirname, '../test/html5player.json'),
JSON.stringify(html5player, null, 2));
}

@@ -68,72 +64,2 @@ callback(

/**
* @param {Object} format
* @param {Array.<String>} tokens
* @param {Boolean} debug
*/
exports.setDownloadURL = function(format, sig, debug) {
var decodedUrl;
if (format.url) {
decodedUrl = format.url;
} else if (format.stream) {
if (format.conn) {
if (format.conn.indexOf('rtmp') === 0) {
format.rtmp = true;
}
decodedUrl = format.conn;
if (decodedUrl[decodedUrl.length - 1] !== '/') {
decodedUrl += '/';
}
decodedUrl += format.stream;
} else {
decodedUrl = format.stream;
}
} else {
if (debug) {
console.warn('download url not found for itag ' + format.itag);
}
return;
}
try {
decodedUrl = decodeURIComponent(decodedUrl);
} catch (err) {
if (debug) {
console.warn('could not decode url: ' + err.message);
}
return;
}
// Make some adjustments to the final url.
var parsedUrl = url.parse(decodedUrl, true);
// Deleting the `search` part is necessary otherwise changes to
// `query` won't reflect when running `url.format()`
delete parsedUrl.search;
var query = parsedUrl.query;
query.ratebypass = 'yes';
if (sig) {
query.signature = sig;
}
format.url = url.format(parsedUrl);
};
/**
* Applies `sig.decipher()` to all format URL's.
*
* @param {Array.<Object>} formats
* @param {Array.<String} tokens
* @param {Boolean} debug
*/
exports.decipherFormats = function(formats, tokens, debug) {
formats.forEach(function(format) {
var sig = tokens && format.s ? exports.decipher(tokens, format.s) : null;
exports.setDownloadURL(format, sig, debug);
});
};
/**
* Decipher a signature based on action tokens.

@@ -221,3 +147,3 @@ *

/**
* Extracts the actions that should be taken to decypher a signature.
* Extracts the actions that should be taken to decipher a signature.
*

@@ -243,5 +169,4 @@ * This searches for a function that performs string manipulations on

var objResult = actionsObjRegexp.exec(body);
if (!objResult) { return null; }
var funcResult = actionsFuncRegexp.exec(body);
if (!funcResult) { return null; }
if (!objResult || !funcResult) { return null; }

@@ -267,14 +192,14 @@ var obj = objResult[1].replace(/\$/g, '\\$');

switch (result[1]) {
case swapKey:
tokens.push('w' + result[2]);
break;
case reverseKey:
tokens.push('r');
break;
case sliceKey:
tokens.push('s' + result[2]);
break;
case spliceKey:
tokens.push('p' + result[2]);
break;
case swapKey:
tokens.push('w' + result[2]);
break;
case reverseKey:
tokens.push('r');
break;
case sliceKey:
tokens.push('s' + result[2]);
break;
case spliceKey:
tokens.push('p' + result[2]);
break;
}

@@ -284,1 +209,58 @@ }

};
/**
* @param {Object} format
* @param {String} sig
* @param {Boolean} debug
*/
exports.setDownloadURL = function(format, sig, debug) {
var decodedUrl;
if (format.url) {
decodedUrl = format.url;
} else {
if (debug) {
console.warn('Download url not found for itag ' + format.itag);
}
return;
}
try {
decodedUrl = decodeURIComponent(decodedUrl);
} catch (err) {
if (debug) {
console.warn('Could not decode url: ' + err.message);
}
return;
}
// Make some adjustments to the final url.
var parsedUrl = url.parse(decodedUrl, true);
// Deleting the `search` part is necessary otherwise changes to
// `query` won't reflect when running `url.format()`
delete parsedUrl.search;
var query = parsedUrl.query;
query.ratebypass = 'yes';
if (sig) {
query.signature = sig;
}
format.url = url.format(parsedUrl);
};
/**
* Applies `sig.decipher()` to all format URL's.
*
* @param {Array.<Object>} formats
* @param {Array.<String>} tokens
* @param {Boolean} debug
*/
exports.decipherFormats = function(formats, tokens, debug) {
formats.forEach(function(format) {
var sig = tokens && format.s ? exports.decipher(tokens, format.s) : null;
exports.setDownloadURL(format, sig, debug);
});
};

@@ -7,2 +7,5 @@ var qs = require('querystring');

var VIDEO_URL = 'https://www.youtube.com/watch?v=';
/**

@@ -317,3 +320,3 @@ * Parses a string representation of amount of milliseconds.

name: channelMatch[2],
avatar: exports.between(ownerinfo, 'data-thumb="', '"'),
avatar: url.resolve(VIDEO_URL, exports.between(ownerinfo, 'data-thumb="', '"')),
user: userMatch ? userMatch[1] : null,

@@ -320,0 +323,0 @@ channel_url: 'https://www.youtube.com/channel/' + channelMatch[1],

@@ -9,3 +9,3 @@ {

],
"version": "0.9.0",
"version": "0.9.1",
"repository": {

@@ -28,3 +28,5 @@ "type": "git",

"mocha": "*",
"muk-prop": "^0.5.3",
"nock": "*",
"sinon": "^1.17.6",
"stream-equal": "~0.1.0"

@@ -31,0 +33,0 @@ },

@@ -8,10 +8,8 @@ var assert = require('assert');

var VIDEO_BASE = 'https://www.youtube.com/watch?v=';
describe('Download video', function() {
var id = '_HSylqgVYQI';
var link = VIDEO_BASE + id;
var video = path.resolve(__dirname, 'files/' + id + '/video.flv');
var video = path.resolve(__dirname, 'files/videos/' + id + '/video.flv');
var filter = function(format) { return format.container === 'mp4'; };
var testInfo = require('./files/videos/pJk0p-98Xzc/expected_info.json');

@@ -28,7 +26,5 @@ beforeEach(function() {

});
var stream = ytdl(link, { filter: filter });
var stream = ytdl(id, { filter: filter });
var infoEmitted = false;
stream.on('info', function(info, format) {
infoEmitted = true;
scope.urlReplyWithFile(format.url, 200, video);

@@ -41,3 +37,2 @@ });

scope.done();
assert.ok(infoEmitted);
assert.ok(equal);

@@ -50,7 +45,2 @@ done();

it('Should download file after redirect', function(done) {
var id = '_HSylqgVYQI';
var link = VIDEO_BASE + id;
var video = path.resolve(__dirname, 'files/' + id + '/video.flv');
var filter = function(format) { return format.container === 'mp4'; };
var scope = nock(id, {

@@ -61,3 +51,3 @@ dashmpd: true,

});
var stream = ytdl(link, { filter: filter });
var stream = ytdl(id, { filter: filter });

@@ -79,2 +69,32 @@ stream.on('info', function(info, format) {

});
describe('too many times', function() {
it('Emits error after 3 retries', function(done) {
var id = '_HSylqgVYQI';
var scope = nock(id, {
dashmpd: true,
get_video_info: true,
player: 'player-en_US-vflV3n15C',
});
var stream = ytdl(id);
stream.on('info', function(info, format) {
scope.urlReply(format.url, 302, '', {
Location: 'http://somehost.com/redirect1.mp4'
});
scope.urlReply('http://somehost.com/redirect1.mp4', 302, '', {
Location: 'http://somehost.com/redirect2.mp4'
});
scope.urlReply('http://somehost.com/redirect2.mp4', 302, '', {
Location: 'http://somehost.com/redirect3.mp4'
});
});
stream.on('error', function(err) {
assert.ok(err);
scope.done();
assert.equal(err.message, 'Too many redirects');
done();
});
});
});
});

@@ -90,6 +110,9 @@

});
var stream = ytdl(link, { filter: filter });
var stream = ytdl(id, { filter: filter });
stream.destroy();
stream.on('request', function() {
done(new Error('Should not emit `request`'));
stream.on('response', function() {
});
done(new Error('Should not emit `response`'));

@@ -104,2 +127,22 @@ });

describe('right after request is made', function() {
it('Doesn\'t start the download', function(done) {
var scope = nock(id, {
dashmpd: true,
get_video_info: true,
player: 'player-en_US-vflV3n15C',
});
var stream = ytdl(id, { filter: filter });
stream.on('request', function() {
stream.destroy();
scope.done();
done();
});
stream.on('response', function() {
done(new Error('Should not emit `response`'));
});
});
});
describe('after download has started', function() {

@@ -112,3 +155,3 @@ it('Download is incomplete', function(done) {

});
var stream = ytdl(link, { filter: filter });
var stream = ytdl(id, { filter: filter });

@@ -130,2 +173,27 @@ stream.on('info', function(info, format) {

});
describe('With range', function() {
it('Range added to download URL', function(done) {
var stream = ytdl.downloadFromInfo(testInfo, { range: '500-1000' });
stream.on('info', function(info, format) {
nock.url(format.url + '&range=500-1000').reply(200, '');
});
stream.resume();
stream.on('error', done);
stream.on('end', done);
});
});
describe('With a bad filter', function() {
it('Emits error', function(done) {
var stream = ytdl.downloadFromInfo(testInfo, {
filter: function() {}
});
stream.on('error', function(err) {
assert.ok(err);
assert.ok(/No formats found/.test(err.message));
done();
});
});
});
});
{
"dynamic_allocation_ad_tag": "https://ad.doubleclick.net/N4061/pfadx/com.ytpwatch.music/main_10481;sz=WIDTHxHEIGHT;kvid=pJk0p-98Xzc;kpu=WuTangClanVEVO;kpeid=1wNaX00osCIK4VjwboFqzA;kpid=10481;mpvid=AAT-TvWQhVcS1PHy;ssl=1;afv=1;afvbase=eJxdUl1zqyAQ_TX1zQ4qGnnwIe0kzWdrPm7r7QuDSKMJAlHUmF9_ienc6b0MM-zZsyy7Z8l5xIRFZVUxTrSsIgcC33cgCkeBF1iiKTHJ6sixco2LLPJCJwiga_U6Y21BmSFKcjEhOGsqogspIifwAbDaImMSU1VGARiWRXMiBOPRuhDPqoQhuIzAntXaeXCfyFeLC1HripHSwHdW6YISjr1_gP8T-cj9CR3ggf8S4aY2nl5jcSvkm2xqVuGu0UQcKCeiZa38yRQZdrpXkgAg6-f5Er4fu1ROz9fxPVOpTF94PN7b-_Zjk7_TnRPP-jtHb3cBDJ2_jxI1mHel8N2vapyxL9JwPUDGDdSk4IocmGVk1L1i0SCepbHq24hdKG8yZuWy1hEl9u20VZPaEAIYINc07nujEUBWU_Eo11o9eKbaqdld1z32stFNyh6pNMJOh8TmVIsTUDYKkyu1uGRmsAiasRmBn5EZMAA3A_rAGzyeGRYcDIR88B3jDhQIfRgOwY7nI-uWyfwNrHQ0jlNv6iIIL9fV1vdtsFjGq81-1feLo70ZP-22-9ni6cTiWTsWo_OSpp-nbo7pJYR1ctq8aWbPwyPmu0lSn9xDj9e5i85X1K_d7W8RS3zWQfdLvIhRJus38JK9cZ1O4mLS592JTWKnpJ-vH-kqOyTzaZFMqj6-_gFCwvVc;dc_backfill=1;dc_yt=1;k5=3_35_592_1030;kclt=1;kga=-1;kgg=-1;klg=en;kmsrd=1;ko=p;kr=F;ktype=song;kvz=205;nlfb=1;yt3pav=1;yt_ec=9;yt_vrallowed=1;ytcat=10;ytdevice=1;ytexp=946505,916600,945035,934804,939950,916625,908548,941359;!c=10481;k2=3;k2=35;k2=592;k2=1030;kvlg=en;yt1st=1;",
"url_encoded_fmt_stream_map": "s=D6D118F186CA4925EE6A72D17F1BB079EFEDD985.57CA8CEEE6AAFDD4CFED5F644384D63CAEBE191E915&quality=medium&itag=43&fallback_host=tc.v6.cache6.googlevideo.com&url=http%3A%2F%2Fr18---sn-ab5l6ne6.googlevideo.com%2Fvideoplayback%3Fgcr%3Dus%26sparams%3Dgcr%252Cid%252Cinitcwndbps%252Cip%252Cipbits%252Citag%252Cratebypass%252Csource%252Cupn%252Cexpire%26source%3Dyoutube%26upn%3D-UoupiUZdKw%26fexp%3D902408%252C908548%252C916600%252C916625%252C924213%252C924217%252C924222%252C930008%252C934024%252C934030%252C934804%252C939950%252C941359%252C945035%252C946505%26key%3Dyt5%26ip%3D198.255.191.225%26mv%3Dm%26mt%3D1405514836%26initcwndbps%3D1517000%26expire%3D1405537200%26ratebypass%3Dyes%26itag%3D43%26mws%3Dyes%26ipbits%3D0%26sver%3D3%26id%3Do-AJdt6VTJxG54d6aGuO7icka1Xfo40WZS0RMZaU2A8hYU%26ms%3Dau&type=video%2Fwebm%3B+codecs%3D%22vp8.0%2C+vorbis%22,s=E8E486C31BECC58187450F20ED9B018F61AE7E03.6A3B39247112A8B2FF31124D0C3672D878B97253255&quality=medium&itag=18&fallback_host=tc.v4.cache3.googlevideo.com&url=http%3A%2F%2Fr18---sn-ab5l6ne6.googlevideo.com%2Fvideoplayback%3Fgcr%3Dus%26sparams%3Dgcr%252Cid%252Cinitcwndbps%252Cip%252Cipbits%252Citag%252Cratebypass%252Csource%252Cupn%252Cexpire%26source%3Dyoutube%26upn%3D-UoupiUZdKw%26fexp%3D902408%252C908548%252C916600%252C916625%252C924213%252C924217%252C924222%252C930008%252C934024%252C934030%252C934804%252C939950%252C941359%252C945035%252C946505%26key%3Dyt5%26ip%3D198.255.191.225%26mv%3Dm%26mt%3D1405514836%26initcwndbps%3D1517000%26expire%3D1405537200%26ratebypass%3Dyes%26itag%3D18%26mws%3Dyes%26ipbits%3D0%26sver%3D3%26id%3Do-AJdt6VTJxG54d6aGuO7icka1Xfo40WZS0RMZaU2A8hYU%26ms%3Dau&type=video%2Fmp4%3B+codecs%3D%22avc1.42001E%2C+mp4a.40.2%22,s=8881FC171C290290EEC1677803C9F4E45C5061AE.65DB412A2B3267DF13FEFD0759FB9B884757F20F207&quality=small&itag=5&fallback_host=tc.v4.cache6.googlevideo.com&url=http%3A%2F%2Fr18---sn-ab5l6ne6.googlevideo.com%2Fvideoplayback%3Fgcr%3Dus%26sparams%3Dgcr%252Cid%252Cinitcwndbps%252Cip%252Cipbits%252Citag%252Csource%252Cupn%252Cexpire%26source%3Dyoutube%26upn%3D-UoupiUZdKw%26fexp%3D902408%252C908548%252C916600%252C916625%252C924213%252C924217%252C924222%252C930008%252C934024%252C934030%252C934804%252C939950%252C941359%252C945035%252C946505%26key%3Dyt5%26ip%3D198.255.191.225%26mv%3Dm%26mt%3D1405514836%26initcwndbps%3D1517000%26expire%3D1405537200%26mws%3Dyes%26itag%3D5%26ipbits%3D0%26sver%3D3%26id%3Do-AJdt6VTJxG54d6aGuO7icka1Xfo40WZS0RMZaU2A8hYU%26ms%3Dau&type=video%2Fx-flv,s=33C7176BDB8375E5B7D6D26D189C5599DB80A7EE.58D060CCCC4129783E5659CCF0D63D81A63F98D58DE&quality=small&itag=36&fallback_host=tc.v7.cache7.googlevideo.com&url=http%3A%2F%2Fr18---sn-ab5l6ne6.googlevideo.com%2Fvideoplayback%3Fgcr%3Dus%26sparams%3Dgcr%252Cid%252Cinitcwndbps%252Cip%252Cipbits%252Citag%252Csource%252Cupn%252Cexpire%26source%3Dyoutube%26upn%3D-UoupiUZdKw%26fexp%3D902408%252C908548%252C916600%252C916625%252C924213%252C924217%252C924222%252C930008%252C934024%252C934030%252C934804%252C939950%252C941359%252C945035%252C946505%26key%3Dyt5%26ip%3D198.255.191.225%26mv%3Dm%26mt%3D1405514836%26initcwndbps%3D1517000%26expire%3D1405537200%26mws%3Dyes%26itag%3D36%26ipbits%3D0%26sver%3D3%26id%3Do-AJdt6VTJxG54d6aGuO7icka1Xfo40WZS0RMZaU2A8hYU%26ms%3Dau&type=video%2F3gpp%3B+codecs%3D%22mp4v.20.3%2C+mp4a.40.2%22,s=E336742BA65B3CB35FADA97FEA996C19925A0831.90CC96F33502177C163F1758D95D31523F2AC923923&quality=small&itag=17&fallback_host=tc.v23.cache6.googlevideo.com&url=http%3A%2F%2Fr18---sn-ab5l6ne6.googlevideo.com%2Fvideoplayback%3Fgcr%3Dus%26sparams%3Dgcr%252Cid%252Cinitcwndbps%252Cip%252Cipbits%252Citag%252Csource%252Cupn%252Cexpire%26source%3Dyoutube%26upn%3D-UoupiUZdKw%26fexp%3D902408%252C908548%252C916600%252C916625%252C924213%252C924217%252C924222%252C930008%252C934024%252C934030%252C934804%252C939950%252C941359%252C945035%252C946505%26key%3Dyt5%26ip%3D198.255.191.225%26mv%3Dm%26mt%3D1405514836%26initcwndbps%3D1517000%26expire%3D1405537200%26mws%3Dyes%26itag%3D17%26ipbits%3D0%26sver%3D3%26id%3Do-AJdt6VTJxG54d6aGuO7icka1Xfo40WZS0RMZaU2A8hYU%26ms%3Dau&type=video%2F3gpp%3B+codecs%3D%22mp4v.20.3%2C+mp4a.40.2%22",
"url_encoded_fmt_stream_map": "s=D6D118F186CA4925EE6A72D17F1BB079EFEDD985.57CA8CEEE6AAFDD4CFED5F644384D63CAEBE191E915&quality=medium&itag=43&fallback_host=tc.v6.cache6.googlevideo.com&url=http%3A%2F%2Fr18---sn-ab5l6ne6.googlevideo.com%2Fvideoplayback%3Fgcr%3Dus%26sparams%3Dgcr%252Cid%252Cinitcwndbps%252Cip%252Cipbits%252Citag%252Cratebypass%252Csource%252Cupn%252Cexpire%26source%3Dyoutube%26upn%3D-UoupiUZdKw%26fexp%3D902408%252C908548%252C916600%252C916625%252C924213%252C924217%252C924222%252C930008%252C934024%252C934030%252C934804%252C939950%252C941359%252C945035%252C946505%26key%3Dyt5%26ip%3D198.255.191.225%26mv%3Dm%26mt%3D1405514836%26initcwndbps%3D1517000%26expire%3D1405537200%26ratebypass%3Dyes%26itag%3D43%26mws%3Dyes%26ipbits%3D0%26sver%3D3%26id%3Do-AJdt6VTJxG54d6aGuO7icka1Xfo40WZS0RMZaU2A8hYU%26ms%3Dau&type=video%2Fwebm%3B+codecs%3D%22vp8.0%2C+vorbis%22,s=E8E486C31BECC58187450F20ED9B018F61AE7E03.6A3B39247112A8B2FF31124D0C3672D878B97253255&quality=medium&itag=18&fallback_host=tc.v4.cache3.googlevideo.com&url=http%3A%2F%2Fr18---sn-ab5l6ne6.googlevideo.com%2Fvideoplayback%3Fgcr%3Dus%26sparams%3Dgcr%252Cid%252Cinitcwndbps%252Cip%252Cipbits%252Citag%252Cratebypass%252Csource%252Cupn%252Cexpire%26source%3Dyoutube%26upn%3D-UoupiUZdKw%26fexp%3D902408%252C908548%252C916600%252C916625%252C924213%252C924217%252C924222%252C930008%252C934024%252C934030%252C934804%252C939950%252C941359%252C945035%252C946505%26key%3Dyt5%26ip%3D198.255.191.225%26mv%3Dm%26mt%3D1405514836%26initcwndbps%3D1517000%26expire%3D1405537200%26ratebypass%3Dyes%26itag%3D18%26mws%3Dyes%26ipbits%3D0%26sver%3D3%26id%3Do-AJdt6VTJxG54d6aGuO7icka1Xfo40WZS0RMZaU2A8hYU%26ms%3Dau&type=video%2Fmp4%3B+codecs%3D%22avc1.42001E%2C+mp4a.40.2%22,s=8881FC171C290290EEC1677803C9F4E45C5061AE.65DB412A2B3267DF13FEFD0759FB9B884757F20F207&quality=small&itag=5&fallback_host=tc.v4.cache6.googlevideo.com&url=http%3A%2F%2Fr18---sn-ab5l6ne6.googlevideo.com%2Fvideoplayback%3Fgcr%3Dus%26sparams%3Dgcr%252Cid%252Cinitcwndbps%252Cip%252Cipbits%252Citag%252Csource%252Cupn%252Cexpire%26source%3Dyoutube%26upn%3D-UoupiUZdKw%26fexp%3D902408%252C908548%252C916600%252C916625%252C924213%252C924217%252C924222%252C930008%252C934024%252C934030%252C934804%252C939950%252C941359%252C945035%252C946505%26key%3Dyt5%26ip%3D198.255.191.225%26mv%3Dm%26mt%3D1405514836%26initcwndbps%3D1517000%26expire%3D1405537200%26mws%3Dyes%26itag%3D5%26ipbits%3D0%26sver%3D3%26id%3Do-AJdt6VTJxG54d6aGuO7icka1Xfo40WZS0RMZaU2A8hYU%26ms%3Dau&type=video%2Fx-flv,s=33C7176BDB8375E5B7D6D26D189C5599DB80A7EE.58D060CCCC4129783E5659CCF0D63D81A63F98D58DE&quality=small&itag=36&fallback_host=tc.v7.cache7.googlevideo.com&url=http%3A%2F%2Fr18---sn-ab5l6ne6.googlevideo.com%2Fvideoplayback%3Fgcr%3Dus%26sparams%3Dgcr%252Cid%252Cinitcwndbps%252Cip%252Cipbits%252Citag%252Csource%252Cupn%252Cexpire%26source%3Dyoutube%26upn%3D-UoupiUZdKw%26fexp%3D902408%252C908548%252C916600%252C916625%252C924213%252C924217%252C924222%252C930008%252C934024%252C934030%252C934804%252C939950%252C941359%252C945035%252C946505%26key%3Dyt5%26ip%3D198.255.191.225%26mv%3Dm%26mt%3D1405514836%26initcwndbps%3D1517000%26expire%3D1405537200%26mws%3Dyes%26itag%3D36%26ipbits%3D0%26sver%3D3%26id%3Do-AJdt6VTJxG54d6aGuO7icka1Xfo40WZS0RMZaU2A8hYU%26ms%3Dau&type=video%2F3gpp%3B+codecs%3D%22mp4v.20.3%2C+mp4a.40.2%22,s=E336742BA65B3CB35FADA97FEA996C19925A0831.90CC96F33502177C163F1758D95D31523F2AC923923&quality=small&itag=17&fallback_host=tc.v23.cache6.googlevideo.com&url=http%3A%2F%2Fr18---sn-ab5l6ne6.googlevideo.com%2Fvideoplayback%3Fgcr%3Dus%26sparams%3Dgcr%252Cid%252Cinitcwndbps%252Cip%252Cipbits%252Citag%252Csource%252Cupn%252Cexpire%26source%3Dyoutube%26upn%3D-UoupiUZdKw%26fexp%3D902408%252C908548%252C916600%252C916625%252C924213%252C924217%252C924222%252C930008%252C934024%252C934030%252C934804%252C939950%252C941359%252C945035%252C946505%26key%3Dyt5%26ip%3D198.255.191.225%26mv%3Dm%26mt%3D1405514836%26initcwndbps%3D1517000%26expire%3D1405537200%26mws%3Dyes%26itag%3D17%26ipbits%3D0%26sver%3D3%26id%3Do-AJdt6VTJxG54d6aGuO7icka1Xfo40WZS0RMZaU2A8hYU%26ms%3Dau&type=video%2F3gpp%3B+codecs%3D%22mp4v.20.3%2C+mp4a.40.2%22,itag=-42",
"t": "1",

@@ -5,0 +5,0 @@ "gut_tag": "/4061/ytpwatch/main_10481",

@@ -5,5 +5,3 @@ var assert = require('assert-diff');

var VIDEO_BASE = 'https://www.youtube.com/watch?v=';
describe('ytdl.getInfo()', function() {

@@ -16,4 +14,3 @@ beforeEach(function() {

var id = 'pJk0p-98Xzc';
var url = VIDEO_BASE + id;
var expectedInfo = require('./files/' + id + '/info.json');
var expectedInfo = require('./files/videos/' + id + '/expected_info.json');

@@ -27,3 +24,3 @@ it('Retrieves correct metainfo', function(done) {

ytdl.getInfo(url, function(err, info) {
ytdl.getInfo(id, function(err, info) {
assert.ifError(err);

@@ -37,3 +34,3 @@ scope.done();

describe('use `ytdl.downloadFromInfo()`', function() {
describe('Use `ytdl.downloadFromInfo()`', function() {
it('Retrives video file', function(done) {

@@ -65,3 +62,3 @@ var stream = ytdl.downloadFromInfo(expectedInfo);

var called = 0;
ytdl.getInfo(url, {
ytdl.getInfo(id, {
request: function(url, options, callback) {

@@ -89,3 +86,3 @@ called++;

ytdl.getInfo(url, {
ytdl.getInfo(id, {
requestOptions: { headers: { 'X-Hello': '42' }}

@@ -103,10 +100,9 @@ }, function(err) {

var id = 'unknown-vid';
var url = VIDEO_BASE + id;
it('Should give an error', function(done) {
var scope = nock(id);
ytdl.getInfo(url, function(err) {
ytdl.getInfo(id, function(err) {
scope.done();
assert.ok(err);
assert.equal(err.message, 'Video not found');
assert.equal(err.message, 'This video does not exist.');
done();

@@ -119,7 +115,7 @@ });

var id = 'rIqCiJKWx9I';
var url = VIDEO_BASE + id;
var expectedInfo = require('./files/' + id + '/info.json');
var expectedInfo = require('./files/videos/' + id + '-age-restricted/expected_info.json');
it('Returns correct video metainfo', function(done) {
var scope = nock(id, {
type: 'age-restricted',
dashmpd: true,

@@ -130,3 +126,3 @@ embed: true,

});
ytdl.getInfo(url, function(err, info) {
ytdl.getInfo(id, function(err, info) {
assert.ifError(err);

@@ -139,2 +135,18 @@ scope.done();

});
describe('from a rental', function() {
var id = 'SyKPsFRP_Oc';
it('Returns a detailed error about it', function(done) {
var scope = nock(id, {
type: 'rental',
get_video_info: true,
});
ytdl.getInfo(id, function(err) {
assert.ok(err);
scope.done();
assert.ok(/requires payment/.test(err.message));
done();
});
});
});
});

@@ -12,9 +12,11 @@ var path = require('path');

module.exports = function(id, opts) {
exports = module.exports = function(id, opts) {
opts = opts || {};
var scopes = [];
var dirpath = 'files/videos/' + id + (opts.type ? '-' + opts.type : '');
scopes.push(nock(YT_HOST, { reqheaders: opts.headers })
.get(VIDEO_PATH + id)
.replyWithFile(200,
path.resolve(__dirname, 'files/' + id + '/watch.html')));
path.resolve(__dirname, dirpath + '/watch.html')));

@@ -26,3 +28,3 @@ if (opts.dashmpd) {

.replyWithFile(200,
path.resolve(__dirname, 'files/' + id + '/dashmpd.xml')));
path.resolve(__dirname, dirpath + '/dashmpd.xml')));
}

@@ -35,3 +37,3 @@

.replyWithFile(opts.dashmpd2[1] || 200,
path.resolve(__dirname, 'files/' + id + '/dashmpd2.xml')));
path.resolve(__dirname, dirpath + '/dashmpd2.xml')));
}

@@ -44,3 +46,3 @@

.replyWithFile(200,
path.resolve(__dirname, 'files/' + id + '/' + opts.player + '.js')));
path.resolve(__dirname, dirpath + '/' + opts.player + '.js')));
}

@@ -52,3 +54,3 @@

.replyWithFile(200,
path.resolve(__dirname, 'files/' + id + '/embed.html')));
path.resolve(__dirname, dirpath + '/embed.html')));
}

@@ -66,3 +68,3 @@

.replyWithFile(200,
path.resolve(__dirname, 'files/' + id + '/get_video_info')));
path.resolve(__dirname, dirpath + '/get_video_info')));
}

@@ -77,6 +79,6 @@

urlReply: function(uri, statusCode, body, headers) {
scopes.push(module.exports.url(uri).reply(statusCode, body, headers));
scopes.push(exports.url(uri).reply(statusCode, body, headers));
},
urlReplyWithFile: function(uri, statusCode, file) {
scopes.push(module.exports.url(uri).replyWithFile(statusCode, file));
scopes.push(exports.url(uri).replyWithFile(statusCode, file));
},

@@ -87,3 +89,3 @@ };

module.exports.url = function(uri) {
exports.url = function(uri) {
var parsed = url.parse(uri);

@@ -94,3 +96,3 @@ return nock(parsed.protocol + '//' + parsed.host).get(parsed.path);

module.exports.disableNetConnect = nock.disableNetConnect;
module.exports.enableNetConnect = nock.enableNetConnect;
exports.disableNetConnect = nock.disableNetConnect;
exports.enableNetConnect = nock.enableNetConnect;

@@ -6,2 +6,4 @@ var sig = require('../lib/sig');

var nock = require('./nock');
var spy = require('sinon').spy;
var muk = require('muk-prop');

@@ -13,9 +15,9 @@ var html5player = require('./html5player.json');

var key = 'en_US-vfljDEtYP';
var url = '//s.ytimg.com/yts/jsbin/player-en_US-vfljDEtYP/base.js';
var url = 'https://s.ytimg.com/yts/jsbin/player-en_US-vfljDEtYP/base.js';
var filepath = path.resolve(__dirname, 'files/html5player/' + key + '.js');
it('Returns a set of tokens', function(done) {
var filepath = path.resolve(__dirname, 'files/html5player/' + key + '.js');
var scope = nock.url('https:' + url).replyWithFile(200, filepath);
var scope = nock.url(url).replyWithFile(200, filepath);
sig.getTokens(url, true, function(err, tokens) {
if (err) return done(err);
assert.ifError(err);
scope.done();

@@ -27,7 +29,10 @@ assert.ok(tokens.length);

describe('hit the same video twice', function() {
describe('Hit the same video twice', function() {
after(function() {
nock.enableNetConnect();
});
it('Gets html5player tokens from cache', function(done) {
nock.disableNetConnect();
sig.getTokens(url, true, function(err, tokens) {
if (err) return done(err);
sig.getTokens(url, {}, function(err, tokens) {
assert.ifError(err);
assert.ok(tokens.length);

@@ -38,6 +43,77 @@ done();

});
describe('Get a bad html5player file', function() {
it('Gives an error', function(done) {
var url = 'https://s.ytimg.com/yts/jsbin/player-en_US-bad/base.js';
var scope = nock.url(url).reply(404, 'uh oh');
sig.getTokens(url, {}, function(err) {
assert.ok(err);
scope.done();
done();
});
});
});
describe('Unable to find key in filename', function() {
var warn = spy();
muk(console, 'warn', warn);
after(muk.restore);
it('Warns the console, still attempts to get tokens', function(done) {
var url = 'https://s.ytimg.com/badfilename.js';
var scope = nock.url(url).replyWithFile(200, filepath);
sig.getTokens(url, {}, function(err, tokens) {
assert.ifError(err);
scope.done();
assert.ok(warn.called);
assert.ok(tokens.length);
done();
});
});
});
describe('Unable to find tokens', function() {
var key = 'mykey';
var url = 'https://s.ytimg.com/yts/jsbin/player-' + key + '/base.js';
var contents = 'my personal contents';
it('Gives an error', function(done) {
var scope = nock.url(url).reply(200, contents);
sig.getTokens(url, {}, function(err) {
scope.done();
assert.ok(err);
assert.ok(/Could not extract/.test(err.message));
done();
});
});
describe('With debug on', function() {
var filepath = path.resolve(__dirname, 'files/html5player', key + '.js');
after(function(done) {
fs.unlink(filepath, function() {
var html5player = require('./html5player.json');
delete html5player[key];
fs.writeFile(
path.resolve(__dirname, 'html5player.json'),
JSON.stringify(html5player, null, 2), done);
});
});
it('Saves files with contents into test directory', function(done) {
var scope = nock.url(url).reply(200, contents);
sig.getTokens(url, { debug: true }, function(err) {
scope.done();
assert.ok(err);
fs.readFile(filepath, function(err, data) {
assert.ifError(err);
assert.equal(data, contents);
done();
});
});
});
});
});
});
describe('Signature decypher', function() {
describe('extract decyphering actions', function() {
describe('Signature decipher', function() {
describe('extract deciphering actions', function() {
it('Returns the correct set of actions', function() {

@@ -81,1 +157,64 @@ for (var name in html5player) {

});
describe('Set download URL', function() {
it('Adds signature to download URL', function() {
var format = {
fallback_host: 'tc.v9.cache7.googlevideo.com',
quality: 'small',
type: 'video/x-flv',
itag: '5',
url: 'https://r4---sn-p5qlsnsr.googlevideo.com/videoplayback?nh=IgpwZjAxLmlhZDI2Kgw3Mi4xNC4yMDMuOTU&upn=utAH1aBebVk&source=youtube&sparams=cwbhb%2Cdur%2Cid%2Cinitcwndbps%2Cip%2Cipbits%2Citag%2Clmt%2Cmime%2Cmm%2Cmn%2Cms%2Cmv%2Cnh%2Cpl%2Crequiressl%2Csource%2Cupn%2Cexpire&initcwndbps=772500&pl=16&ip=0.0.0.0&lmt=1309008098017854&key=yt6&id=o-AJj1D_OYO_EieAH08Qa2tRsP6zid9dsuPAvktizyDRlv&expire=1444687469&mm=31&mn=sn-p5qlsnsr&itag=5&mt=1444665784&mv=m&cwbhb=yes&fexp=9408208%2C9408490%2C9408710%2C9409069%2C9414764%2C9415435%2C9416126%2C9417224%2C9417380%2C9417488%2C9417707%2C9418448%2C9418494%2C9419445%2C9419802%2C9420324%2C9420348%2C9420982%2C9421013%2C9421170%2C9422341%2C9422540&ms=au&sver=3&dur=298.109&requiressl=yes&ipbits=0&mime=video%2Fx-flv&ratebypass=yes',
container: 'flv',
resolution: '240p',
encoding: 'Sorenson H.283',
profile: null,
bitrate: '0.25',
audioEncoding: 'mp3',
audioBitrate: 64
};
sig.setDownloadURL(format, 'mysiggy', false);
assert.ok(format.url.indexOf('signature=mysiggy') > -1);
});
describe('With a badly formatted URL', function() {
var format = {
url: 'https://r4---sn-p5qlsnsr.googlevideo.com/videoplayback?%',
};
it('Does not set URL', function() {
sig.setDownloadURL(format, 'mysiggy', false);
assert.ok(format.url.indexOf('signature=mysiggy') === -1);
});
describe('With debug on', function() {
it('Logs to console', function() {
var warn = spy();
muk(console, 'warn', warn);
after(muk.restore);
sig.setDownloadURL(format, 'mysiggy', true);
assert.ok(warn.called);
assert.ok(format.url.indexOf('signature=mysiggy') === -1);
});
});
});
describe('Without a URL', function() {
var format = { bla: 'blu' };
it('Does not set URL', function() {
sig.setDownloadURL(format, 'nothing', false);
assert.deepEqual(format, { bla: 'blu' });
});
describe('With debug on', function() {
it('Logs to console', function() {
var warn = spy();
muk(console, 'warn', warn);
after(muk.restore);
sig.setDownloadURL(format, 'nothing', true);
assert.ok(warn.called);
assert.deepEqual(format, { bla: 'blu' });
});
});
});
});
var ytdl = require('..');
var util = require('../lib/util');
var assert = require('assert-diff');
var fs = require('fs');
var path = require('path');
var assert = require('assert-diff');
var spy = require('sinon').spy;
var muk = require('muk-prop');

@@ -34,2 +36,3 @@

bitrate : '0.15-0.3',
rtmp : true,
audioEncoding : null,

@@ -82,2 +85,20 @@ audioBitrate : null },

audioBitrate : 128 },
{ itag : '139',
type : 'audio/mp4; codecs="mp4a.40.2"',
quality : null,
container : 'mp4',
resolution : null,
enoding : null,
bitrate : null,
audioEncoding : null,
audioBitrate : null },
{ itag : '138',
type : 'audio/mp4; codecs="mp4a.40.2"',
quality : null,
container : 'mp4',
resolution : null,
enoding : null,
bitrate : null,
audioEncoding : null,
audioBitrate : null },
];

@@ -104,3 +125,3 @@ var getItags = function(format) { return format.itag; };

describe('util.sortFormats()', function() {
describe('with `highest` given', function() {
describe('With `highest` given', function() {
it('Sorts available formats from highest to lowest quality', function() {

@@ -111,3 +132,3 @@ var sortedFormats = formats.slice();

assert.deepEqual(itags, [
'43', '18', '5', '36', '17', '133', '160', '140'
'43', '18', '5', '36', '17', '133', '160', '140', '139', '138'
]);

@@ -134,10 +155,10 @@ });

describe('with lowest quality wanted', function() {
describe('With lowest quality wanted', function() {
it('Chooses lowest itag', function() {
var format = util.chooseFormat(sortedFormats, { quality: 'lowest' });
assert.equal(format.itag, '140');
assert.equal(format.itag, '138');
});
});
describe('with itag given', function() {
describe('With itag given', function() {
it('Chooses matching format', function() {

@@ -156,3 +177,3 @@ var format = util.chooseFormat(sortedFormats, { quality: 5 });

describe('with list of itags given', function() {
describe('With list of itags given', function() {
it('Chooses matching format', function() {

@@ -163,2 +184,34 @@ var format = util.chooseFormat(sortedFormats, { quality: [99, 160, 18] });

});
describe('With format object given', function() {
it('Chooses given format without searching', function() {
var format = util.chooseFormat(sortedFormats, { format: formats[0] });
assert.equal(format, formats[0]);
});
});
describe('With filter given', function() {
describe('that matches a format', function() {
it('Chooses a format', function() {
var format = util.chooseFormat(sortedFormats, {
filter: function(format) { return format.container === 'mp4'; }
});
assert.equal(format.itag, '18');
});
});
describe('that does not match a format', function() {
it('Returns an error', function() {
var err = util.chooseFormat(sortedFormats, { filter: function() {} });
assert.equal(err.message, 'No formats found with custom filter');
});
});
});
describe('Get an rtmp format (not supported)', function() {
it('Returns an error', function() {
var err = util.chooseFormat(sortedFormats, { quality: 133 });
assert.equal(err.message, 'rtmp protocol not supported');
});
});
});

@@ -171,3 +224,3 @@

var itags = util.filterFormats(formats, filter).map(getItags);
assert.deepEqual(itags, ['18', '133', '160', '140']);
assert.deepEqual(itags, ['18', '133', '160', '140', '139', '138']);
});

@@ -186,3 +239,3 @@

describe('with `video` given', function() {
describe('With `video` given', function() {
it('Returns only matching formats', function() {

@@ -194,3 +247,3 @@ var itags = util.filterFormats(formats, 'video').map(getItags);

describe('with `videoonly` given', function() {
describe('With `videoonly` given', function() {
it('Returns only matching formats', function() {

@@ -202,3 +255,3 @@ var itags = util.filterFormats(formats, 'videoonly').map(getItags);

describe('with `audio` given', function() {
describe('With `audio` given', function() {
it('Returns only matching formats', function() {

@@ -210,3 +263,3 @@ var itags = util.filterFormats(formats, 'audio').map(getItags);

describe('with `audioonly` given', function() {
describe('With `audioonly` given', function() {
it('Returns only matching formats', function() {

@@ -240,2 +293,7 @@ var itags = util.filterFormats(formats, 'audioonly').map(getItags);

});
it('`right` not found', function() {
var rs = util.between('something [around[ somewhere', '[', ']');
assert.equal(rs, '');
});
});

@@ -257,2 +315,5 @@

assert(id, 'RAW_VIDEOID');
assert.throws(function() {
util.getVideoID('https://www.twitch.tv/user/v/1234');
}, Error, /No video id found/);
assert.throws(function () {

@@ -269,8 +330,22 @@ util.getVideoID('www.youtube.com');

describe('util.parseFormats()', function() {
var info = require('./files/info/pJk0p-98Xzc_preparsed.json');
it('Retrieves video formats from info', function() {
var info = require('./files/info/pJk0p-98Xzc_preparsed.json');
var formats = util.parseFormats(info);
var myinfo = util.objectAssign({}, info);
var formats = util.parseFormats(myinfo);
assert.ok(formats);
assert.equal(formats.length, 14);
assert.equal(formats.length, 15);
});
describe('With `debug` on', function() {
it('Retrieves video formats from info', function() {
var myinfo = util.objectAssign({}, info);
var warn = spy();
muk(console, 'warn', warn);
after(muk.restore);
var formats = util.parseFormats(myinfo, true);
assert.ok(formats);
assert.equal(formats.length, 15);
assert.ok(warn.called);
});
});
});

@@ -302,3 +377,3 @@

name: 'NoCopyrightSounds',
avatar: 'hisprofile.pic',
avatar: 'https://www.youtube.com/hisprofile.pic',
user: 'NoCopyrightSounds',

@@ -360,9 +435,7 @@ channel_url: 'https://www.youtube.com/channel/UC_aEa8K-EOJ3D6gOs7HcyNg',

funcs.push(function(i, callback) {
setTimeout(function() {
callback(null, i);
}, ~~(Math.random() * 50));
setTimeout(function() { callback(null, i); }, ~~(Math.random() * 10));
}.bind(null, i));
}
util.parallel(funcs, function(err, results) {
assert.ok(!err);
assert.ifError(err);
for (var i = 0, len = results.length; i < len; i++) {

@@ -374,2 +447,23 @@ assert.equal(results[i], i);

});
describe('where one of them errors', function() {
it('Gives an error', function(done) {
var funcs = [];
for (var i = 0; i < 5; i++) {
funcs.push(function(i, callback) {
setImmediate(function() {
if (i === 0) {
callback(new Error('Something went wrong'));
} else {
callback(null, i);
}
});
}.bind(null, i));
}
util.parallel(funcs, function(err) {
assert.ok(err);
setImmediate(done);
});
});
});
});

@@ -376,0 +470,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc