Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

first commit

  • Loading branch information...
commit 43265a575e9797b42d864ef01106dfe333065175 0 parents
Roly Fentanes authored
1  .gitignore
@@ -0,0 +1 @@
+node_modules
4 .travis.yml
@@ -0,0 +1,4 @@
+language: node_js
+node_js:
+ - 0.6
+ - 0.8
19 LICENSE
@@ -0,0 +1,19 @@
+Copyright (C) 2012 by Roly Fentanes
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
53 README.md
@@ -0,0 +1,53 @@
+# node-ytdl [![Build Status](https://secure.travis-ci.org/fent/node-ytdl.png)](http://travis-ci.org/fent/node-ytdl)
+
+Yet another youtube downloading module. This time written with only Javascript and a more node-friendly streaming interface.
+
+
+# Usage
+
+```js
+var fs = require('fs');
+var ytdl = require('ytdl');
+
+ytdl('http://www.youtube.com/watch?v=A02s8omM_hI')
+ .pipe(fs.createWriteStream('video.mp4'));
+```
+
+
+# API
+### ytdl(url, options)
+
+Attempts to download a video from the given url. Returns a readable stream. `options` can have the following keys
+
+* `quality` - Video quality to download. Can be an [itag value](http://en.wikipedia.org/wiki/YouTube#Quality_and_codecs) value, `highest`, or `lowest`. Defaults to `highest`.
+* `start` - Where to begin downloading the video in milliseconds or in a time format ie `1m34s`.
+* `filter` - You can give a filtering function that gets called with each format available. Used to decide what format to download.
+
+`options` can also have any [request](https://github.com/mikeal/request) options.
+
+The returned readable stream emits these additional events.
+
+### Event: 'info'
+`function (info, format) { }`
+
+Emitted when the a video's `info` hash is fetched. Along with the chosen format metadata to download.
+
+### ytdl.getInfo(url, callback(err, info))
+
+Use this if you only want to get metainfo from a video. `info` may look like [this](https://gist.github.com/6c8251132e1addb5121e).
+
+
+# Install
+
+ npm install yt
+
+
+# Tests
+Tests are written with [mocha](http://visionmedia.github.com/mocha/)
+
+```bash
+npm test
+```
+
+# License
+MIT
223 lib/formats.js
@@ -0,0 +1,223 @@
+module.exports = {
+
+ '5': {
+ container: 'flv'
+ , resolution: '240p'
+ , encoding: 'Sorenson H.283'
+ , profile: null
+ , bitrate: '0.25'
+ , audioEncoding: 'mp3'
+ , audioBitrate: 64
+ }
+
+, '6': {
+ container: 'flv'
+ , resolution: '270p'
+ , encoding: 'Sorenson H.263'
+ , profile: null
+ , bitrate: '0.8'
+ , audioEncoding: 'mp3'
+ , audioBitrate: 64
+ }
+
+, '13': {
+ container: '3gp'
+ , resolution: null
+ , encoding: 'MPEG-4 Visual'
+ , profile: null
+ , bitrate: '0.5'
+ , audioEncoding: 'aac'
+ , audioBitrate: null
+ }
+
+, '17': {
+ container: '3gp'
+ , resolution: '144p'
+ , encoding: 'MPEG-4 Visual'
+ , profile: 'simple'
+ , bitrate: '0.05'
+ , audioEncoding: 'aac'
+ , audioBitrate: 24
+ }
+
+, '18': {
+ container: 'mp4'
+ , resolution: '360p'
+ , encoding: 'H.264'
+ , profile: 'baseline'
+ , bitrate: '0.5'
+ , audioEncoding: 'aac'
+ , audioBitrate: 96
+ }
+
+, '22': {
+ container: 'mp4'
+ , resolution: '720p'
+ , encoding: 'H.264'
+ , profile: 'high'
+ , bitrate: '2-2.9'
+ , audioEncoding: 'aac'
+ , audioBitrate: 152
+ }
+
+, '34': {
+ container: 'flv'
+ , resolution: '360p'
+ , encoding: 'H.264'
+ , profile: 'main'
+ , bitrate: '0.5'
+ , audioEncoding: 'aac'
+ , audioBitrate: 128
+ }
+
+, '35': {
+ container: 'flv'
+ , resolution: '280p'
+ , encoding: 'H.264'
+ , profile: 'main'
+ , bitrate: '0.8-1'
+ , audioEncoding: 'aac'
+ , audioBitrate: 128
+ }
+
+, '36': {
+ container: '3gp'
+ , resolution: '240p'
+ , encoding: 'MPEG-4 Visual'
+ , profile: 'simple'
+ , bitrate: '0.17'
+ , audioEncoding: 'aac'
+ , audioBitrate: 38
+ }
+
+, '37': {
+ container: 'mp4'
+ , resolution: '1080p'
+ , encoding: 'H.264'
+ , profile: 'high'
+ , bitrate: '3-4.3'
+ , audioEncoding: 'aac'
+ , audioBitrate: 152
+ }
+
+, '38': {
+ container: 'mp4'
+ , resolution: '3072p'
+ , encoding: 'H.264'
+ , profile: 'high'
+ , bitrate: '3.5-5'
+ , audioEncoding: 'aac'
+ , audioBitrate: 152
+ }
+
+, '43': {
+ container: 'webm'
+ , resolution: '360p'
+ , encoding: 'VP8'
+ , profile: null
+ , bitrate: '0.5'
+ , audioEncoding: 'vorbis'
+ , audioBitrate: 128
+ }
+
+, '44': {
+ container: 'webm'
+ , resolution: '480p'
+ , encoding: 'VP8'
+ , profile: null
+ , bitrate: '1'
+ , audioEncoding: 'vorbis'
+ , audioBitrate: 128
+ }
+
+, '45': {
+ container: 'webm'
+ , resolution: '720p'
+ , encoding: 'VP8'
+ , profile: null
+ , bitrate: '2'
+ , audioEncoding: 'vorbis'
+ , audioBitrate: 192
+ }
+
+, '46': {
+ container: 'webm'
+ , resolution: '1080p'
+ , encoding: 'vp8'
+ , profile: null
+ , bitrate: null
+ , audioEncoding: 'vorbis'
+ , audioBitrate: 192
+ }
+
+, '82': {
+ container: 'mp4'
+ , resolution: '360p'
+ , encoding: 'H.264'
+ , profile: '3d'
+ , bitrate: '0.5'
+ , audioEncoding: 'aac'
+ , audioBitrate: 96
+ }
+
+, '83': {
+ container: 'mp4'
+ , resolution: '240p'
+ , encoding: 'H.264'
+ , profile: '3d'
+ , bitrate: '0.5'
+ , audioEncoding: 'aac'
+ , audioBitrate: 96
+ }
+
+, '84': {
+ container: 'mp4'
+ , resolution: '720p'
+ , encoding: 'H.264'
+ , profile: '3d'
+ , bitrate: '2-2.9'
+ , audioEncoding: 'aac'
+ , audioBitrate: 152
+ }
+
+, '85': {
+ container: 'mp4'
+ , resolution: '520p'
+ , encoding: 'H.264'
+ , profile: '3d'
+ , bitrate: '2-2.9'
+ , audioEncoding: 'aac'
+ , audioBitrate: 152
+ }
+
+, '100': {
+ container: 'webm'
+ , resolution: '360p'
+ , encoding: 'VP8'
+ , profile: '3d'
+ , bitrate: null
+ , audioEncoding: 'vorbis'
+ , audioBitrate: 128
+ }
+
+, '101': {
+ container: 'webm'
+ , resolution: '360p'
+ , encoding: 'VP8'
+ , profile: '3d'
+ , bitrate: null
+ , audioEncoding: 'vorbis'
+ , audioBitrate: 192
+ }
+
+, '102': {
+ container: 'webm'
+ , resolution: '720p'
+ , encoding: 'VP8'
+ , profile: '3d'
+ , bitrate: null
+ , audioEncoding: 'vorbis'
+ , audioBitrate: 192
+ }
+
+};
237 lib/index.js
@@ -0,0 +1,237 @@
+var url = require('url');
+var qs = require('querystring');
+var http = require('http');
+var EventVat = require('eventvat');
+var request = require('request');
+var Streamify = require('streamify');
+var Hash = require('hashish');
+var FORMATS = require('./formats');
+var parseTime = require('./util').parseTime;
+
+
+var INFO_URL = 'http://www.youtube.com/get_video_info?hl=en_US&video_id=';
+var KEYS_TO_SPLIT = [
+ 'keywords'
+, 'fmt_list'
+, 'fexp'
+, 'watermark'
+, 'ad_channel_code_overlay'
+];
+
+
+// cache for recently looked up video infos
+var cache = new EventVat({ autoexpire: 30 });
+
+
+/**
+ * Checks cache to see if info for this video has been fetched recently.
+ * If not, get it, then download the video once the info is found.
+ *
+ * @param (string) link
+ * @param (Object) options
+ * @return (ReadableStream)
+ */
+function ytdl(link, options) {
+ var id = url.parse(link, true).query.v;
+ var stream = new Streamify({ superCtor: http.ClientResponse });
+
+ options = options || {};
+ options.quality = 'highest';
+
+ if (cache.exists(id)) {
+ var info = cache.get(id);
+ process.nextTick(function() {
+ download(link, options, stream, info);
+ });
+
+ } else {
+ ytdl.getInfo(link, options, function(err, info) {
+ if (err) {
+ stream.emit('error', err);
+ stream.readable = false;
+ return;
+ }
+
+ download(link, options, stream, info);
+ });
+ }
+
+ return stream;
+}
+
+
+/**
+ * Downloads a video.
+ *
+ * @param (string) link
+ * @param (Object) options
+ * @param (ReadableStream) stream
+ * @param (Object) info
+ * @return (ReadableStream)
+ */
+function download(link, options, stream, info) {
+ var formats = info.formats;
+ if (options.filter) {
+ formats = formats.filter(options.filter);
+ }
+
+ var format;
+ switch (options.quality) {
+ case 'highest':
+ format = formats[0];
+ break;
+
+ case 'lowest':
+ format = formats[formats.length - 1];
+ break;
+
+ default:
+ format = formats.filter(function(format) {
+ return format.itag === options.quality;
+ })[0];
+
+ }
+
+ if (!format) {
+ stream.emit('error', new Error('No such format found'));
+ return;
+ }
+
+ format = Hash.copy(format);
+ var url = format.url;
+ var requestOptions = Hash.copy(options);
+
+ if (options.start) {
+ url = url + '&begin=' + parseTime(options.start);
+ }
+
+ format.url = requestOptions.url = url;
+ stream.emit('info', info, format);
+
+ // start downloading the video
+ var res = request(requestOptions);
+ stream.resolve(res);
+}
+
+
+/**
+ * Gets info from a video.
+ *
+ * @param (string) link
+ * @param (Object) requestOptions
+ * @param (Function(Error, Object)) callback
+ */
+ytdl.getInfo = function getInfo(link, requestOptions, callback) {
+ if (typeof requestOptions === 'function') {
+ callback = requestOptions;
+ requestOptions = {};
+ } else {
+ requestOptions = Hash.copy(requestOptions);
+ }
+
+ var id = url.parse(link, true).query.v;
+ if (!id) {
+ process.nextTick(function() {
+ callback(new Error('Video ID not found'));
+ });
+ return;
+ }
+
+ requestOptions.url = INFO_URL + id;
+
+ request(requestOptions, function(err, res, body) {
+ if (err) return callback(err);
+ if (res.statusCode !== 200) {
+ return callback(new Error('Video not found: ' + url));
+ }
+
+ var info = { id: id };
+ var info = qs.parse(body);
+
+ if (info.status === 'fail') {
+ callback(new Error('Error ' + info.errorcode + ': ' + info.reason));;
+ return;
+ }
+
+ // split some keys by commas
+ KEYS_TO_SPLIT.forEach(function(key) {
+ info[key] = info[key].split(',').filter(function(v) { return v !== ''; });
+ });
+
+ // convert some strings to javascript numbers and booleans
+ info = Hash.map(info, function(val, key) {
+ var intVal = parseInt(val);
+ var floatVal = parseFloat(val);
+
+ if (intVal.toString() === val) {
+ return intVal;
+ } else if (floatVal.toString() === val) {
+ return floatVal;
+ } else if (val === 'True') {
+ return true;
+ } else if (val === 'False') {
+ return false;
+ } else {
+ return val;
+ }
+ });
+
+ // a few more manual data cleansing
+ var fmt_map = info.url_encoded_fmt_stream_map =
+ qs.parse(info.url_encoded_fmt_stream_map);
+
+ fmt_map.itag = fmt_map.itag
+ .map(function(itag) {
+ var split = itag.split(',');
+ return { itag: split[0], url: split[1] ? split[1].slice(4) : null };
+ })
+ ;
+
+ info.fmt_list = info.fmt_list.map(function(fmt) {
+ return fmt.split('/');
+ });
+
+ info.formats = info.fmt_list
+ .map(function(format, i) {
+ var itag = format[0];
+ format = FORMATS[itag];
+
+ if (!format) {
+ err = new Error('Unrecognized format itag: ' + itag);
+ return;
+ }
+
+ format = Hash.copy(format);
+ format.quality = fmt_map.quality[i];
+ format.fallback_host = fmt_map.fallback_host[i];
+ format.type = fmt_map.type[i];
+ format.url = fmt_map.itag[i].url;
+
+ return format;
+ })
+ .filter(function(format) {
+ return !!format.url;
+ })
+ ;
+
+ if (err) {
+ callback(err);
+ }
+
+ info.video_verticals = info.video_verticals
+ .slice(1, -1)
+ .split(', ')
+ .filter(function(val) { return val !== ''; })
+ .map(function(val) { return parseInt(val); })
+ ;
+
+ cache.set(id, info);
+ callback(null, info);
+ });
+}
+
+
+/**
+ * export
+ */
+module.exports = ytdl;
16 lib/util.js
@@ -0,0 +1,16 @@
+/**
+ * Parses a string representation of amount of milliseconds.
+ *
+ * @param (string) time
+ * @return (number)
+ */
+var regexp = /(?:(\d+)h)?(?:(\d+)m(?!s))?(?:(\d+)s)?(?:(\d+)(?:ms)?)?/;
+exports.parseTime = function(time) {
+ var result = regexp.exec(time.toString());
+ var hours = result[1] || 0;
+ var mins = result[2] || 0;
+ var secs = result[3] || 0;
+ var ms = result[4] || 0;
+
+ return hours * 3600000 + mins * 60000 + secs * 1000 + parseInt(ms);
+}
32 package.json
@@ -0,0 +1,32 @@
+{
+ "name": "ytdl",
+ "description": "A pure Javascript streaming youtube video downloader.",
+ "keywords": ["youtube", "download", "video"],
+ "version": "0.1.0",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/fent/node-ytdl.git"
+ },
+ "author": "Roly Fentanes (https://github.com/fent)",
+ "main": "./lib/index.js",
+ "scripts": {
+ "test": "mocha -R spec test/*-test.js"
+ },
+ "directories": {
+ "lib": "./lib"
+ },
+ "dependencies": {
+ "request": "~2.9.203",
+ "eventvat": "~0.2.0",
+ "streamify": "~0.1.0",
+ "hashish": "~0.0.4"
+ },
+ "devDependencies": {
+ "mocha": "x",
+ "nock": "0.13.x"
+ },
+ "licenses": [ {
+ "type": "MIT",
+ "url" : "http://github.com/fent/node-ytdl/raw/master/LICENSE"
+ }]
+}
1  test/files/info1.json
@@ -0,0 +1 @@
+{"account_playback_token":"","ptk":"FlippyCat%2Buser","iv_close_button":0,"mpvid":"AATE5NTWyGbB_mPR","allow_ratings":1,"keywords":["cat","meow","scream","loud","funny","scary","wtf"],"track_embed":1,"host_language":"en","iv3_module":"http://s.ytimg.com/yt/swfbin/iv3_module-vflGPy5-i.swf","no_get_video_log":1,"fmt_list":[["43","320x240","99","0","0"],["34","320x240","9","0","115"],["18","320x240","9","0","115"],["5","320x240","7","0","0"],["36","320x240","99","0","0"],["17","176x144","99","0","0"]],"title":"Meor 2 - Screaming cat goes nuts!","targeting_video_doc_id":"","sffb":true,"content_owner_name":"FlippyCat+user","cafe_experiment_id":"","timestamp":1342387009,"has_cc":false,"ad_host":"ca-host-pub-0749790722879797","ad_eurl":"http://www.youtube.com/video/_HSylqgVYQI","url_encoded_fmt_stream_map":{"url":"http://o-o.preferred.lax02s14.v6.lscache6.c.youtube.com/videoplayback?upn=fTo3mp8v07c&sparams=cp%2Cid%2Cip%2Cipbits%2Citag%2Cratebypass%2Csource%2Cupn%2Cexpire&fexp=903309%2C915102%2C910207%2C902519%2C907217%2C907335%2C921602%2C919306%2C922600%2C919316%2C919804%2C920704%2C924500%2C906831%2C924700%2C913542%2C919324%2C920706%2C924402%2C907344%2C912706%2C902518&ms=au&itag=43&ipbits=8&signature=0D8132A9D938B326416BDE94CD18D1DBEAB6FE75.4AE867D3A04FE7EED9ABF27BF607D12F5E8BCF96&sver=3&mt=1342386917&ratebypass=yes&source=youtube&expire=1342412032&key=yt1&ip=70.190.251.26&cp=U0hTR1RMUF9ITENOM19MRlRBOmZZcW1qZnJCc2U0&id=fc74b296a8156102","quality":["medium","medium","medium","small","small","small"],"fallback_host":["tc.v6.cache6.c.youtube.com","tc.v22.cache6.c.youtube.com","tc.v17.cache6.c.youtube.com","tc.v22.cache8.c.youtube.com","tc.v15.cache5.c.youtube.com","tc.v7.cache8.c.youtube.com"],"type":["video/webm; codecs=\"vp8.0, vorbis\"","video/x-flv","video/mp4; codecs=\"avc1.42001E, mp4a.40.2\"","video/x-flv","video/3gpp; codecs=\"mp4v.20.3, mp4a.40.2\"","video/3gpp; codecs=\"mp4v.20.3, mp4a.40.2\""],"itag":[{"itag":"43","url":"http://o-o.preferred.lax04s12.v22.lscache6.c.youtube.com/videoplayback?ip=70.190.251.26&upn=fTo3mp8v07c&sparams=algorithm%2Cburst%2Ccp%2Cfactor%2Cid%2Cip%2Cipbits%2Citag%2Csource%2Cupn%2Cexpire&fexp=903309%2C915102%2C910207%2C902519%2C907217%2C907335%2C921602%2C919306%2C922600%2C919316%2C919804%2C920704%2C924500%2C906831%2C924700%2C913542%2C919324%2C920706%2C924402%2C907344%2C912706%2C902518&mt=1342386917&ms=au&algorithm=throttle-factor&burst=40&ipbits=8&itag=34&sver=3&signature=AE74AA0A2C6F470D84ED9DC45C3F5C344A0296.22160D444FA0024DE526EDA56BC16826170EB714&source=youtube&expire=1342412032&key=yt1&factor=1.25&cp=U0hTR1RMUF9ITENOM19MRlRBOmZZcW1qZnJCc2U0&id=fc74b296a8156102"},{"itag":"34","url":"http://o-o.preferred.lax02s18.v17.lscache6.c.youtube.com/videoplayback?upn=fTo3mp8v07c&sparams=cp%2Cid%2Cip%2Cipbits%2Citag%2Cratebypass%2Csource%2Cupn%2Cexpire&fexp=903309%2C915102%2C910207%2C902519%2C907217%2C907335%2C921602%2C919306%2C922600%2C919316%2C919804%2C920704%2C924500%2C906831%2C924700%2C913542%2C919324%2C920706%2C924402%2C907344%2C912706%2C902518&ms=au&itag=18&ipbits=8&signature=807BD23D46FA1FB2C0D32C8EF4510592C69BC3D5.B2538A8B9A4A19D4AF7666E45174FD93BFEBE9CD&sver=3&mt=1342386917&ratebypass=yes&source=youtube&expire=1342412032&key=yt1&ip=70.190.251.26&cp=U0hTR1RMUF9ITENOM19MRlRBOmZZcW1qZnJCc2U0&id=fc74b296a8156102"},{"itag":"18","url":"http://o-o.preferred.lax04s17.v22.lscache8.c.youtube.com/videoplayback?ip=70.190.251.26&upn=fTo3mp8v07c&sparams=algorithm%2Cburst%2Ccp%2Cfactor%2Cid%2Cip%2Cipbits%2Citag%2Csource%2Cupn%2Cexpire&fexp=903309%2C915102%2C910207%2C902519%2C907217%2C907335%2C921602%2C919306%2C922600%2C919316%2C919804%2C920704%2C924500%2C906831%2C924700%2C913542%2C919324%2C920706%2C924402%2C907344%2C912706%2C902518&mt=1342386917&ms=au&algorithm=throttle-factor&burst=40&ipbits=8&itag=5&sver=3&signature=062CCEF50BCE91F2D280EF7D09826BFAF9085604.A675E8BDFABD211E8665F233F3626410C10D9431&source=youtube&expire=1342412032&key=yt1&factor=1.25&cp=U0hTR1RMUF9ITENOM19MRlRBOmZZcW1qZnJCc2U0&id=fc74b296a8156102"},{"itag":"5","url":"http://o-o.preferred.lax04s16.v15.lscache5.c.youtube.com/videoplayback?ip=70.190.251.26&upn=fTo3mp8v07c&sparams=algorithm%2Cburst%2Ccp%2Cfactor%2Cid%2Cip%2Cipbits%2Citag%2Csource%2Cupn%2Cexpire&fexp=903309%2C915102%2C910207%2C902519%2C907217%2C907335%2C921602%2C919306%2C922600%2C919316%2C919804%2C920704%2C924500%2C906831%2C924700%2C913542%2C919324%2C920706%2C924402%2C907344%2C912706%2C902518&mt=1342386917&ms=au&algorithm=throttle-factor&burst=40&ipbits=8&itag=36&sver=3&signature=7694186402A71347D1AB3341BD9C48AF163F0A69.AAC13D567426F1025E39FAFD31489A8B2442AB7B&source=youtube&expire=1342412032&key=yt1&factor=1.25&cp=U0hTR1RMUF9ITENOM19MRlRBOmZZcW1qZnJCc2U0&id=fc74b296a8156102"},{"itag":"36","url":"http://o-o.preferred.lax04s16.v7.lscache8.c.youtube.com/videoplayback?ip=70.190.251.26&upn=fTo3mp8v07c&sparams=algorithm%2Cburst%2Ccp%2Cfactor%2Cid%2Cip%2Cipbits%2Citag%2Csource%2Cupn%2Cexpire&fexp=903309%2C915102%2C910207%2C902519%2C907217%2C907335%2C921602%2C919306%2C922600%2C919316%2C919804%2C920704%2C924500%2C906831%2C924700%2C913542%2C919324%2C920706%2C924402%2C907344%2C912706%2C902518&mt=1342386917&ms=au&algorithm=throttle-factor&burst=40&ipbits=8&itag=17&sver=3&signature=48ABB342C22968486098A751F3B765BF402768AA.1CDB6ABB8C4A124DF340C0CA5B07FCE9D1D13D63&source=youtube&expire=1342412032&key=yt1&factor=1.25&cp=U0hTR1RMUF9ITENOM19MRlRBOmZZcW1qZnJCc2U0&id=fc74b296a8156102"},{"itag":"17","url":null}]},"tmi":1,"iv_logging_level":4,"st_module":"http://s.ytimg.com/yt/swfbin/st_module-vfl5ke6CD.swf","ad_flags":0,"endscreen_module":"http://s.ytimg.com/yt/swfbin/endscreen-vflsrWl4r.swf","iv_read_url":"http://www.youtube.com/annotations_iv/read2?video_id=_HSylqgVYQI&feat=CS","cid":907,"avg_rating":4.84751958225,"ad_module":"http://s.ytimg.com/yt/swfbin/ad-vflcz3iiY.swf","sendtmp":1,"token":"vjVQa1PpcFOtOHZKUWW6BntRSMj-SzwQadjKpPi8PkU=","ad_logging_flag":1,"excluded_ads":"3=1_1,1_3","fexp":["903309","915102","910207","902519","907217","907335","921602","919306","922600","919316","919804","920704","924500","906831","924700","913542","919324","920706","924402","907344","912706","902518"],"shortform":true,"allow_embed":1,"ad_host_tier":478479,"vq":"auto","pyv_in_related_cafe_experiment_id":"","allowed_ads":"[2]","gut_tag":"/4061/ytunknown/main","ptchn":"FlippyCat","author":"FlippyCat","muted":0,"length_seconds":11,"thumbnail_url":"http://i4.ytimg.com/vi/_HSylqgVYQI/default.jpg","plid":"AATE5NTWy_DW5eqS","iv_module":"http://s.ytimg.com/yt/swfbin/iv_module-vfln3w_Yn.swf","afv":true,"ftoken":"","ad_video_pub_id":"ca-pub-6219811747049371","status":"ok","watermark":["http://s.ytimg.com/yt/img/watermark/youtube_watermark-vflHX6b6E.png","http://s.ytimg.com/yt/img/watermark/youtube_hd_watermark-vflAzLcD6.png"],"oid":"uzYgx4cKij1Ia997SpUffA","video_verticals":[182,3,211,885],"view_count":501845,"ad_channel_code_overlay":["invideo_overlay_480x70_cat15","afv_overlay","Vertical_Overlay_65","Vertical_Overlay_66","Vertical_Overlay_182","Vertical_Overlay_211","Vertical_Overlay_563","Vertical_Overlay_613","Vertical_Overlay_885","VidVert65","VidVert66","VidVert182","VidVert211","VidVert563","VidVert613","VidVert885","Vertical_65","Vertical_66","Vertical_182","Vertical_211","Vertical_563","Vertical_613","Vertical_885","afv_user_flippycat","afv_user_id_uO06egvl5yE__rR_02WUZw","yt_mpvid_AATE5NTWyGbB_mPR","yt_cid_907","ytexp_903309.915102.910207.902519.907217.907335.921602.919306.922600.919316.919804.920704.924500.906831.924700.913542.919324.920706.924402.907344.912706.902518","ytps_default","ytel_embedded"],"iv_load_policy":1,"iv_queue_log_level":0,"as_launched_in_country":1,"video_id":"_HSylqgVYQI","iv_allow_in_place_switch":1,"pltype":"content","formats":[{"container":"webm","resolution":"360p","encoding":"VP8","profile":null,"bitrate":"0.5","audioEncoding":"vorbis","audioBitrate":128,"quality":"medium","fallback_host":"tc.v6.cache6.c.youtube.com","type":"video/webm; codecs=\"vp8.0, vorbis\"","url":"http://o-o.preferred.lax04s12.v22.lscache6.c.youtube.com/videoplayback?ip=70.190.251.26&upn=fTo3mp8v07c&sparams=algorithm%2Cburst%2Ccp%2Cfactor%2Cid%2Cip%2Cipbits%2Citag%2Csource%2Cupn%2Cexpire&fexp=903309%2C915102%2C910207%2C902519%2C907217%2C907335%2C921602%2C919306%2C922600%2C919316%2C919804%2C920704%2C924500%2C906831%2C924700%2C913542%2C919324%2C920706%2C924402%2C907344%2C912706%2C902518&mt=1342386917&ms=au&algorithm=throttle-factor&burst=40&ipbits=8&itag=34&sver=3&signature=AE74AA0A2C6F470D84ED9DC45C3F5C344A0296.22160D444FA0024DE526EDA56BC16826170EB714&source=youtube&expire=1342412032&key=yt1&factor=1.25&cp=U0hTR1RMUF9ITENOM19MRlRBOmZZcW1qZnJCc2U0&id=fc74b296a8156102"},{"container":"flv","resolution":"360p","encoding":"H.264","profile":"main","bitrate":"0.5","audioEncoding":"aac","audioBitrate":128,"quality":"medium","fallback_host":"tc.v22.cache6.c.youtube.com","type":"video/x-flv","url":"http://o-o.preferred.lax02s18.v17.lscache6.c.youtube.com/videoplayback?upn=fTo3mp8v07c&sparams=cp%2Cid%2Cip%2Cipbits%2Citag%2Cratebypass%2Csource%2Cupn%2Cexpire&fexp=903309%2C915102%2C910207%2C902519%2C907217%2C907335%2C921602%2C919306%2C922600%2C919316%2C919804%2C920704%2C924500%2C906831%2C924700%2C913542%2C919324%2C920706%2C924402%2C907344%2C912706%2C902518&ms=au&itag=18&ipbits=8&signature=807BD23D46FA1FB2C0D32C8EF4510592C69BC3D5.B2538A8B9A4A19D4AF7666E45174FD93BFEBE9CD&sver=3&mt=1342386917&ratebypass=yes&source=youtube&expire=1342412032&key=yt1&ip=70.190.251.26&cp=U0hTR1RMUF9ITENOM19MRlRBOmZZcW1qZnJCc2U0&id=fc74b296a8156102"},{"container":"mp4","resolution":"360p","encoding":"H.264","profile":"baseline","bitrate":"0.5","audioEncoding":"aac","audioBitrate":96,"quality":"medium","fallback_host":"tc.v17.cache6.c.youtube.com","type":"video/mp4; codecs=\"avc1.42001E, mp4a.40.2\"","url":"http://o-o.preferred.lax04s17.v22.lscache8.c.youtube.com/videoplayback?ip=70.190.251.26&upn=fTo3mp8v07c&sparams=algorithm%2Cburst%2Ccp%2Cfactor%2Cid%2Cip%2Cipbits%2Citag%2Csource%2Cupn%2Cexpire&fexp=903309%2C915102%2C910207%2C902519%2C907217%2C907335%2C921602%2C919306%2C922600%2C919316%2C919804%2C920704%2C924500%2C906831%2C924700%2C913542%2C919324%2C920706%2C924402%2C907344%2C912706%2C902518&mt=1342386917&ms=au&algorithm=throttle-factor&burst=40&ipbits=8&itag=5&sver=3&signature=062CCEF50BCE91F2D280EF7D09826BFAF9085604.A675E8BDFABD211E8665F233F3626410C10D9431&source=youtube&expire=1342412032&key=yt1&factor=1.25&cp=U0hTR1RMUF9ITENOM19MRlRBOmZZcW1qZnJCc2U0&id=fc74b296a8156102"},{"container":"flv","resolution":"240p","encoding":"Sorenson H.283","profile":null,"bitrate":"0.25","audioEncoding":"mp3","audioBitrate":64,"quality":"small","fallback_host":"tc.v22.cache8.c.youtube.com","type":"video/x-flv","url":"http://o-o.preferred.lax04s16.v15.lscache5.c.youtube.com/videoplayback?ip=70.190.251.26&upn=fTo3mp8v07c&sparams=algorithm%2Cburst%2Ccp%2Cfactor%2Cid%2Cip%2Cipbits%2Citag%2Csource%2Cupn%2Cexpire&fexp=903309%2C915102%2C910207%2C902519%2C907217%2C907335%2C921602%2C919306%2C922600%2C919316%2C919804%2C920704%2C924500%2C906831%2C924700%2C913542%2C919324%2C920706%2C924402%2C907344%2C912706%2C902518&mt=1342386917&ms=au&algorithm=throttle-factor&burst=40&ipbits=8&itag=36&sver=3&signature=7694186402A71347D1AB3341BD9C48AF163F0A69.AAC13D567426F1025E39FAFD31489A8B2442AB7B&source=youtube&expire=1342412032&key=yt1&factor=1.25&cp=U0hTR1RMUF9ITENOM19MRlRBOmZZcW1qZnJCc2U0&id=fc74b296a8156102"},{"container":"3gp","resolution":"240p","encoding":"MPEG-4 Visual","profile":"simple","bitrate":"0.17","audioEncoding":"aac","audioBitrate":38,"quality":"small","fallback_host":"tc.v15.cache5.c.youtube.com","type":"video/3gpp; codecs=\"mp4v.20.3, mp4a.40.2\"","url":"http://o-o.preferred.lax04s16.v7.lscache8.c.youtube.com/videoplayback?ip=70.190.251.26&upn=fTo3mp8v07c&sparams=algorithm%2Cburst%2Ccp%2Cfactor%2Cid%2Cip%2Cipbits%2Citag%2Csource%2Cupn%2Cexpire&fexp=903309%2C915102%2C910207%2C902519%2C907217%2C907335%2C921602%2C919306%2C922600%2C919316%2C919804%2C920704%2C924500%2C906831%2C924700%2C913542%2C919324%2C920706%2C924402%2C907344%2C912706%2C902518&mt=1342386917&ms=au&algorithm=throttle-factor&burst=40&ipbits=8&itag=17&sver=3&signature=48ABB342C22968486098A751F3B765BF402768AA.1CDB6ABB8C4A124DF340C0CA5B07FCE9D1D13D63&source=youtube&expire=1342412032&key=yt1&factor=1.25&cp=U0hTR1RMUF9ITENOM19MRlRBOmZZcW1qZnJCc2U0&id=fc74b296a8156102"}]}
1  test/files/video1.html
@@ -0,0 +1 @@
+account_playback_token=&ptk=FlippyCat%252Buser&iv_close_button=0&mpvid=AATE5NTWyGbB_mPR&allow_ratings=1&keywords=cat%2Cmeow%2Cscream%2Cloud%2Cfunny%2Cscary%2Cwtf&track_embed=1&host_language=en&iv3_module=http%3A%2F%2Fs.ytimg.com%2Fyt%2Fswfbin%2Fiv3_module-vflGPy5-i.swf&no_get_video_log=1&fmt_list=43%2F320x240%2F99%2F0%2F0%2C34%2F320x240%2F9%2F0%2F115%2C18%2F320x240%2F9%2F0%2F115%2C5%2F320x240%2F7%2F0%2F0%2C36%2F320x240%2F99%2F0%2F0%2C17%2F176x144%2F99%2F0%2F0&title=Meor+2+-+Screaming+cat+goes+nuts%21&targeting_video_doc_id=&sffb=True&content_owner_name=FlippyCat%2Buser&cafe_experiment_id=&timestamp=1342387009&has_cc=False&ad_host=ca-host-pub-0749790722879797&ad_eurl=http%3A%2F%2Fwww.youtube.com%2Fvideo%2F_HSylqgVYQI&url_encoded_fmt_stream_map=url%3Dhttp%253A%252F%252Fo-o.preferred.lax02s14.v6.lscache6.c.youtube.com%252Fvideoplayback%253Fupn%253DfTo3mp8v07c%2526sparams%253Dcp%25252Cid%25252Cip%25252Cipbits%25252Citag%25252Cratebypass%25252Csource%25252Cupn%25252Cexpire%2526fexp%253D903309%25252C915102%25252C910207%25252C902519%25252C907217%25252C907335%25252C921602%25252C919306%25252C922600%25252C919316%25252C919804%25252C920704%25252C924500%25252C906831%25252C924700%25252C913542%25252C919324%25252C920706%25252C924402%25252C907344%25252C912706%25252C902518%2526ms%253Dau%2526itag%253D43%2526ipbits%253D8%2526signature%253D0D8132A9D938B326416BDE94CD18D1DBEAB6FE75.4AE867D3A04FE7EED9ABF27BF607D12F5E8BCF96%2526sver%253D3%2526mt%253D1342386917%2526ratebypass%253Dyes%2526source%253Dyoutube%2526expire%253D1342412032%2526key%253Dyt1%2526ip%253D70.190.251.26%2526cp%253DU0hTR1RMUF9ITENOM19MRlRBOmZZcW1qZnJCc2U0%2526id%253Dfc74b296a8156102%26quality%3Dmedium%26fallback_host%3Dtc.v6.cache6.c.youtube.com%26type%3Dvideo%252Fwebm%253B%2Bcodecs%253D%2522vp8.0%252C%2Bvorbis%2522%26itag%3D43%2Curl%3Dhttp%253A%252F%252Fo-o.preferred.lax04s12.v22.lscache6.c.youtube.com%252Fvideoplayback%253Fip%253D70.190.251.26%2526upn%253DfTo3mp8v07c%2526sparams%253Dalgorithm%25252Cburst%25252Ccp%25252Cfactor%25252Cid%25252Cip%25252Cipbits%25252Citag%25252Csource%25252Cupn%25252Cexpire%2526fexp%253D903309%25252C915102%25252C910207%25252C902519%25252C907217%25252C907335%25252C921602%25252C919306%25252C922600%25252C919316%25252C919804%25252C920704%25252C924500%25252C906831%25252C924700%25252C913542%25252C919324%25252C920706%25252C924402%25252C907344%25252C912706%25252C902518%2526mt%253D1342386917%2526ms%253Dau%2526algorithm%253Dthrottle-factor%2526burst%253D40%2526ipbits%253D8%2526itag%253D34%2526sver%253D3%2526signature%253DAE74AA0A2C6F470D84ED9DC45C3F5C344A0296.22160D444FA0024DE526EDA56BC16826170EB714%2526source%253Dyoutube%2526expire%253D1342412032%2526key%253Dyt1%2526factor%253D1.25%2526cp%253DU0hTR1RMUF9ITENOM19MRlRBOmZZcW1qZnJCc2U0%2526id%253Dfc74b296a8156102%26quality%3Dmedium%26fallback_host%3Dtc.v22.cache6.c.youtube.com%26type%3Dvideo%252Fx-flv%26itag%3D34%2Curl%3Dhttp%253A%252F%252Fo-o.preferred.lax02s18.v17.lscache6.c.youtube.com%252Fvideoplayback%253Fupn%253DfTo3mp8v07c%2526sparams%253Dcp%25252Cid%25252Cip%25252Cipbits%25252Citag%25252Cratebypass%25252Csource%25252Cupn%25252Cexpire%2526fexp%253D903309%25252C915102%25252C910207%25252C902519%25252C907217%25252C907335%25252C921602%25252C919306%25252C922600%25252C919316%25252C919804%25252C920704%25252C924500%25252C906831%25252C924700%25252C913542%25252C919324%25252C920706%25252C924402%25252C907344%25252C912706%25252C902518%2526ms%253Dau%2526itag%253D18%2526ipbits%253D8%2526signature%253D807BD23D46FA1FB2C0D32C8EF4510592C69BC3D5.B2538A8B9A4A19D4AF7666E45174FD93BFEBE9CD%2526sver%253D3%2526mt%253D1342386917%2526ratebypass%253Dyes%2526source%253Dyoutube%2526expire%253D1342412032%2526key%253Dyt1%2526ip%253D70.190.251.26%2526cp%253DU0hTR1RMUF9ITENOM19MRlRBOmZZcW1qZnJCc2U0%2526id%253Dfc74b296a8156102%26quality%3Dmedium%26fallback_host%3Dtc.v17.cache6.c.youtube.com%26type%3Dvideo%252Fmp4%253B%2Bcodecs%253D%2522avc1.42001E%252C%2Bmp4a.40.2%2522%26itag%3D18%2Curl%3Dhttp%253A%252F%252Fo-o.preferred.lax04s17.v22.lscache8.c.youtube.com%252Fvideoplayback%253Fip%253D70.190.251.26%2526upn%253DfTo3mp8v07c%2526sparams%253Dalgorithm%25252Cburst%25252Ccp%25252Cfactor%25252Cid%25252Cip%25252Cipbits%25252Citag%25252Csource%25252Cupn%25252Cexpire%2526fexp%253D903309%25252C915102%25252C910207%25252C902519%25252C907217%25252C907335%25252C921602%25252C919306%25252C922600%25252C919316%25252C919804%25252C920704%25252C924500%25252C906831%25252C924700%25252C913542%25252C919324%25252C920706%25252C924402%25252C907344%25252C912706%25252C902518%2526mt%253D1342386917%2526ms%253Dau%2526algorithm%253Dthrottle-factor%2526burst%253D40%2526ipbits%253D8%2526itag%253D5%2526sver%253D3%2526signature%253D062CCEF50BCE91F2D280EF7D09826BFAF9085604.A675E8BDFABD211E8665F233F3626410C10D9431%2526source%253Dyoutube%2526expire%253D1342412032%2526key%253Dyt1%2526factor%253D1.25%2526cp%253DU0hTR1RMUF9ITENOM19MRlRBOmZZcW1qZnJCc2U0%2526id%253Dfc74b296a8156102%26quality%3Dsmall%26fallback_host%3Dtc.v22.cache8.c.youtube.com%26type%3Dvideo%252Fx-flv%26itag%3D5%2Curl%3Dhttp%253A%252F%252Fo-o.preferred.lax04s16.v15.lscache5.c.youtube.com%252Fvideoplayback%253Fip%253D70.190.251.26%2526upn%253DfTo3mp8v07c%2526sparams%253Dalgorithm%25252Cburst%25252Ccp%25252Cfactor%25252Cid%25252Cip%25252Cipbits%25252Citag%25252Csource%25252Cupn%25252Cexpire%2526fexp%253D903309%25252C915102%25252C910207%25252C902519%25252C907217%25252C907335%25252C921602%25252C919306%25252C922600%25252C919316%25252C919804%25252C920704%25252C924500%25252C906831%25252C924700%25252C913542%25252C919324%25252C920706%25252C924402%25252C907344%25252C912706%25252C902518%2526mt%253D1342386917%2526ms%253Dau%2526algorithm%253Dthrottle-factor%2526burst%253D40%2526ipbits%253D8%2526itag%253D36%2526sver%253D3%2526signature%253D7694186402A71347D1AB3341BD9C48AF163F0A69.AAC13D567426F1025E39FAFD31489A8B2442AB7B%2526source%253Dyoutube%2526expire%253D1342412032%2526key%253Dyt1%2526factor%253D1.25%2526cp%253DU0hTR1RMUF9ITENOM19MRlRBOmZZcW1qZnJCc2U0%2526id%253Dfc74b296a8156102%26quality%3Dsmall%26fallback_host%3Dtc.v15.cache5.c.youtube.com%26type%3Dvideo%252F3gpp%253B%2Bcodecs%253D%2522mp4v.20.3%252C%2Bmp4a.40.2%2522%26itag%3D36%2Curl%3Dhttp%253A%252F%252Fo-o.preferred.lax04s16.v7.lscache8.c.youtube.com%252Fvideoplayback%253Fip%253D70.190.251.26%2526upn%253DfTo3mp8v07c%2526sparams%253Dalgorithm%25252Cburst%25252Ccp%25252Cfactor%25252Cid%25252Cip%25252Cipbits%25252Citag%25252Csource%25252Cupn%25252Cexpire%2526fexp%253D903309%25252C915102%25252C910207%25252C902519%25252C907217%25252C907335%25252C921602%25252C919306%25252C922600%25252C919316%25252C919804%25252C920704%25252C924500%25252C906831%25252C924700%25252C913542%25252C919324%25252C920706%25252C924402%25252C907344%25252C912706%25252C902518%2526mt%253D1342386917%2526ms%253Dau%2526algorithm%253Dthrottle-factor%2526burst%253D40%2526ipbits%253D8%2526itag%253D17%2526sver%253D3%2526signature%253D48ABB342C22968486098A751F3B765BF402768AA.1CDB6ABB8C4A124DF340C0CA5B07FCE9D1D13D63%2526source%253Dyoutube%2526expire%253D1342412032%2526key%253Dyt1%2526factor%253D1.25%2526cp%253DU0hTR1RMUF9ITENOM19MRlRBOmZZcW1qZnJCc2U0%2526id%253Dfc74b296a8156102%26quality%3Dsmall%26fallback_host%3Dtc.v7.cache8.c.youtube.com%26type%3Dvideo%252F3gpp%253B%2Bcodecs%253D%2522mp4v.20.3%252C%2Bmp4a.40.2%2522%26itag%3D17&tmi=1&iv_logging_level=4&st_module=http%3A%2F%2Fs.ytimg.com%2Fyt%2Fswfbin%2Fst_module-vfl5ke6CD.swf&ad_flags=0&endscreen_module=http%3A%2F%2Fs.ytimg.com%2Fyt%2Fswfbin%2Fendscreen-vflsrWl4r.swf&iv_read_url=http%3A%2F%2Fwww.youtube.com%2Fannotations_iv%2Fread2%3Fvideo_id%3D_HSylqgVYQI%26feat%3DCS&cid=907&avg_rating=4.84751958225&ad_module=http%3A%2F%2Fs.ytimg.com%2Fyt%2Fswfbin%2Fad-vflcz3iiY.swf&sendtmp=1&token=vjVQa1PpcFOtOHZKUWW6BntRSMj-SzwQadjKpPi8PkU%3D&ad_logging_flag=1&excluded_ads=3%3D1_1%2C1_3&fexp=903309%2C915102%2C910207%2C902519%2C907217%2C907335%2C921602%2C919306%2C922600%2C919316%2C919804%2C920704%2C924500%2C906831%2C924700%2C913542%2C919324%2C920706%2C924402%2C907344%2C912706%2C902518&shortform=True&allow_embed=1&ad_host_tier=478479&vq=auto&pyv_in_related_cafe_experiment_id=&allowed_ads=%5B2%5D&gut_tag=%2F4061%2Fytunknown%2Fmain&ptchn=FlippyCat&author=FlippyCat&muted=0&length_seconds=11&thumbnail_url=http%3A%2F%2Fi4.ytimg.com%2Fvi%2F_HSylqgVYQI%2Fdefault.jpg&plid=AATE5NTWy_DW5eqS&iv_module=http%3A%2F%2Fs.ytimg.com%2Fyt%2Fswfbin%2Fiv_module-vfln3w_Yn.swf&afv=True&ftoken=&ad_video_pub_id=ca-pub-6219811747049371&status=ok&watermark=%2Chttp%3A%2F%2Fs.ytimg.com%2Fyt%2Fimg%2Fwatermark%2Fyoutube_watermark-vflHX6b6E.png%2Chttp%3A%2F%2Fs.ytimg.com%2Fyt%2Fimg%2Fwatermark%2Fyoutube_hd_watermark-vflAzLcD6.png&oid=uzYgx4cKij1Ia997SpUffA&video_verticals=%5B182%2C+3%2C+211%2C+885%5D&view_count=501845&ad_channel_code_overlay=invideo_overlay_480x70_cat15%2Cafv_overlay%2CVertical_Overlay_65%2CVertical_Overlay_66%2CVertical_Overlay_182%2CVertical_Overlay_211%2CVertical_Overlay_563%2CVertical_Overlay_613%2CVertical_Overlay_885%2CVidVert65%2CVidVert66%2CVidVert182%2CVidVert211%2CVidVert563%2CVidVert613%2CVidVert885%2CVertical_65%2CVertical_66%2CVertical_182%2CVertical_211%2CVertical_563%2CVertical_613%2CVertical_885%2Cafv_user_flippycat%2Cafv_user_id_uO06egvl5yE__rR_02WUZw%2Cyt_mpvid_AATE5NTWyGbB_mPR%2Cyt_cid_907%2Cytexp_903309.915102.910207.902519.907217.907335.921602.919306.922600.919316.919804.920704.924500.906831.924700.913542.919324.920706.924402.907344.912706.902518%2Cytps_default%2Cytel_embedded&iv_load_policy=1&iv_queue_log_level=0&as_launched_in_country=1&video_id=_HSylqgVYQI&iv_allow_in_place_switch=1&pltype=content
1  test/files/video2.html
@@ -0,0 +1 @@
+status=fail&errorcode=100&reason=The+video+you+have+requested+is+not+available.+If+you+have+recently+uploaded+this+video%2C+you+may+need+to+wait+a+few+minutes+for+the+video+to+process.
BIN  test/files/video3.flv
Binary file not shown
BIN  test/files/video4.flv
Binary file not shown
126 test/main-test.js
@@ -0,0 +1,126 @@
+var ytdl = require('..')
+ , assert = require('assert')
+ , path = require('path')
+ , fs = require('fs')
+ , url = require('url')
+ , nock = require('nock')
+ ;
+
+
+var HOST = 'http://www.youtube.com'
+ , INFO_GET = '/get_video_info?hl=en_US&video_id='
+ , URL = 'http://www.youtube.com/watch?v='
+
+ , id1 = '_HSylqgVYQI'
+ , url1 = URL + id1
+ , mockget1 = INFO_GET + id1
+ , page1 = path.join(__dirname, 'files', 'video1.html')
+ , info1 = JSON.parse(fs.readFileSync(
+ path.join(__dirname, 'files', 'info1.json'), 'utf8'))
+
+ , id2 = '_HSylqgyyyy'
+ , url2 = URL + id2
+ , mockget2 = INFO_GET + id2
+ , page2 = path.join(__dirname, 'files', 'video2.html')
+
+ , id3 = '_HSylqgVYQI'
+ , url3 = URL + id3
+ , mockget3 = INFO_GET + id3
+ , video3 = path.join(__dirname, 'files', 'video3.flv')
+ , output3 = path.join(__dirname, 'files', 'output3.flv')
+
+ , id4 = '_HSylqgVYQI'
+ , url4 = URL + id4
+ , mockget4 = INFO_GET + id4
+ , video4 = path.join(__dirname, 'files', 'video4.flv')
+ , output4 = path.join(__dirname, 'files', 'output4.flv')
+ ;
+
+
+describe('ytdl.getInfo()', function() {
+ it('Returns correct video metainfo', function(done) {
+ nock(HOST)
+ .get(mockget1)
+ .replyWithFile(200, page1)
+ ;
+
+ ytdl.getInfo(url1, function(err, info) {
+ if (err) return done(err);
+
+ assert.deepEqual(info, info1);
+ done();
+ });
+
+ });
+});
+
+
+describe('ytdl.getInfo() from a non-existant video', function() {
+ it('Should give an error', function(done) {
+ nock(HOST)
+ .get(mockget2)
+ .replyWithFile(200, page2)
+ ;
+
+ ytdl.getInfo(url2, function(err, info) {
+ assert.ok(err);
+ assert.equal(err.message, 'Error 100: The video you have requested is not available. If you have recently uploaded this video, you may need to wait a few minutes for the video to process.');
+ done();
+ });
+ });
+});
+
+
+describe('download', function() {
+ it('Should be pipeable and data equal to stored file', function(done) {
+ var stream = ytdl(url3, {
+ filter: function(format) { return format.container === 'mp4' }
+ });
+ stream.pipe(fs.createWriteStream(output3));
+
+ var infoEmitted = false;
+ stream.on('info', function(info, format) {
+ infoEmitted = true;
+ var uri = url.parse(format.url);
+ nock(uri.protocol + '//' + uri.host)
+ .get(uri.path)
+ .replyWithFile(200, video3)
+ ;
+ });
+
+ stream.on('error', done);
+ stream.on('end', function() {
+ assert.ok(infoEmitted);
+ var oldData = fs.readFileSync(video3);
+ var newData = fs.readFileSync(output3);
+ fs.unlink(output3);
+ assert.deepEqual(oldData.length, newData.length);
+ done();
+ });
+ });
+});
+
+
+describe('download with `start`', function() {
+ it('Should be pipeable and data equal to stored file', function(done) {
+ var stream = ytdl(url3, { start: '5s' });
+ stream.pipe(fs.createWriteStream(output4));
+
+ stream.on('info', function(info, format) {
+ var uri = url.parse(format.url);
+ nock(uri.protocol + '//' + uri.host)
+ .get(uri.path)
+ .replyWithFile(200, video4)
+ ;
+ });
+
+ stream.on('error', done);
+ stream.on('end', function() {
+ var oldData = fs.readFileSync(video4);
+ var newData = fs.readFileSync(output4);
+ fs.unlink(output4);
+ assert.deepEqual(oldData.length, newData.length);
+ done();
+ });
+ });
+});
20 test/util-test.js
@@ -0,0 +1,20 @@
+var parseTime = require('../lib/util').parseTime
+ , assert = require('assert')
+ ;
+
+
+describe('util.parseTime()', function() {
+ it('Returns milliseconds if given numbers', function() {
+ assert.equal(1234, parseTime(1234));
+ });
+
+ it('Works with minutes and seconds', function() {
+ assert.equal(2 * 60000 + 36 * 1000, parseTime('2m36s'));
+ });
+
+ it('And even only hours and milliseconds', function() {
+ assert.equal(3 * 3600000 + 4200, parseTime('3h4200ms'));
+ });
+});
+
+
Please sign in to comment.
Something went wrong with that request. Please try again.