ytdl-core
Advanced tools
Comparing version 0.9.0 to 0.9.1
@@ -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) { |
178
lib/sig.js
@@ -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 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
58
14006481
7
73809