Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

commit

  • Loading branch information...
commit 059d81b513daaf1cb848387300e43f3caf6068ce 0 parents
@fent authored
1  .gitignore
@@ -0,0 +1 @@
+bin
1  .npmignore
@@ -0,0 +1 @@
+bin
19 LICENSE
@@ -0,0 +1,19 @@
+Copyright (C) 2011 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.
101 README.md
@@ -0,0 +1,101 @@
+A [youtube-dl][] driver for node.
+
+Usage
+------------------
+
+ youtubedl = require('youtube-dl');
+
+###Downloading videos
+
+ youtubedl.download('http://www.youtube.com/watch?v=90AiXO1pAiA',
+ './videos',
+
+ // will be called when a state changes
+ function(state, data) {
+ console.log(state);
+ if (state == 'Downloading video') {
+ console.log('Video size: ' + data.size);
+ }
+ },
+
+ // will be called during download progress of a video
+ function(data) {
+ console.log(data.eta + ' ' + data.percent + '% at ' + data.speed);
+ },
+
+ // called when youtube-dl finishes
+ function(err) {
+ if (err)
+ throw err;
+ console.log('Download finished!')
+ },
+
+ // optional arguments passed to youtube-dl
+ ['--max-quality=18']);
+
+This example can be found in the *example* folder, and will produce an output that looks like the following when ran.
+
+ Setting language
+ Downloading video webpage
+ Downloading video info webpage
+ Extracting video information
+ Downloading video
+ Video size: 918.31k
+ --:-- 0.1% at ---b/s
+ --:-- 0.3% at ---b/s
+ 00:12 0.8% at 75.62k/s
+ 00:06 1.6% at 135.69k/s
+ 00:03 3.4% at 223.15k/s
+ 00:04 6.9% at 171.27k/s
+ 00:05 13.8% at 135.40k/s
+ 00:03 26.1% at 194.62k/s
+ 00:03 50.5% at 140.90k/s
+ 00:02 62.8% at 169.02k/s
+ 00:00 87.3% at 210.18k/s
+ 00:00 100.0% at 181.41k/s
+ Download finished!
+
+###Getting video information
+
+ youtube.info('http://www.youtube.com/watch?v=WKsjaOqDXgg',
+
+ // called when video page is downloaded and info extracted
+ function(err, info) {
+ if (err)
+ throw err;
+ console.log('title: ' + info.title);
+ console.log('url: ' + info.url);
+ console.log('thumbnail: ' + info.thumbnail);
+ console.log('description: ' + info.description);
+ }
+
+ // optional arguments passed to youtube-dl
+ // ['--username=user', '--password=hunter2']
+ );
+
+Running that will produce something like
+
+ title: Ace Rimmer to the Rescue
+ url: http://v2.lscache2.c.youtube.com/videoplayback?sparams=id%2Cexpire%2Cip%2Cipbits%2Citag%2Calgorithm%2Cburst%2Cfactor%2Coc%3AU0hPSFFQVF9FSkNOOV9JSlhJ&fexp=904410%2C907048%2C910100&algorithm=throttle-factor&itag=34&ipbits=0&burst=40&sver=3&signature=4093330AC1A5B0CAF8709A0416A4B593A75BB892.21F2F12C418003492D9877E1570DC7AEE6DBEEBA&expire=1303156800&key=yt1&ip=0.0.0.0&factor=1.25&id=58ab2368ea835e08
+ thumbnail: http://i4.ytimg.com/vi/WKsjaOqDXgg/default.jpg
+ description: An old Red Dwarf eposide where Ace Rimmer saves the Princess Bonjella.
+
+For more usage info on youtube-dl and the arguments you can pass to it, do `youtube-dl -h` or go to the [youtube-dl documentation][].
+
+
+Installation
+------------
+Using npm:
+
+ $ npm install youtube-dl
+
+This will install this node module along with the latest version of [youtube-dl][] into your module folder. It will also create a symlink to youtube-dl so you run it from the command line.
+
+Issues and the Future
+---------------------
+
+I haven't tested this with playlists yet because I haven't needed to use them. But my guess is they probably work with the download function but not the info function.
+
+
+[youtube-dl]: http://rg3.github.com/youtube-dl/
+[youtube-dl documentation]: http://rg3.github.com/youtube-dl/documentation.html
27 example/download.js
@@ -0,0 +1,27 @@
+youtubedl = require('youtube-dl');
+
+// will be called when a state changes
+stateChange = function(state, data) {
+ console.log(state);
+ if (state == 'Downloading video') {
+ console.log('Video size: ' + data.size);
+ }
+}
+
+// will be called during download progress of a video
+progress = function(data) {
+ console.log(data.eta + ' ' + data.percent + '% at ' + data.speed);
+}
+
+// called when youtube-dl finishes
+finished = function(err) {
+ if (err)
+ throw err;
+ console.log('Download finished!')
+},
+
+youtubedl.download('http://www.youtube.com/watch?v=90AiXO1pAiA', './',
+ stateChange, progress, finished,
+
+ // optional arguments passed to youtube-dl
+ ['--max-quality=18']);
17 example/info.js
@@ -0,0 +1,17 @@
+youtube = require('youtube-dl');
+
+youtube.info('http://www.youtube.com/watch?v=WKsjaOqDXgg',
+
+ // called when video page is downloaded and info extracted
+ function(err, info) {
+ if (err)
+ throw err;
+ console.log('title: ' + info.title);
+ console.log('url: ' + info.url);
+ console.log('thumbnail: ' + info.thumbnail);
+ console.log('description: ' + info.description);
+ }
+
+ // optional arguments passed to youtube-dl
+ // ['--username=user', '--password=hunter2']
+ );
133 lib/youtube-dl.coffee
@@ -0,0 +1,133 @@
+# module dependencies
+spawn = require('child_process').spawn
+fs = require 'fs'
+path = require 'path'
+
+
+# arguments we dont want users to use with youtube-dl
+# because they will break this module
+badArgs = [
+ '-h', '--help'
+ '-v', '--version'
+ '-U', '--update'
+ '-q', '--quiet'
+ '-s', '--simulate'
+ '-g', '--get-url'
+ '-e', '--get-title'
+ '--get-thumbnail'
+ '--get-description'
+ '--get-filename'
+ '--no-progress'
+ '--console-title'
+]
+
+# helps parse options used in youtube-dl command
+parseOpts = (args = []) ->
+ for arg in badArgs
+ if (pos = hasArg args, arg) isnt -1
+ args.splice pos, 1
+ args
+
+# returns position if argument is found in array
+hasArg = (arr, arg) ->
+ for a, i in arr
+ if (a.indexOf arg) is 0
+ return i
+ return -1
+
+
+# check that youtube-dl file exists
+file = path.normalize __dirname + '/../bin/youtube-dl'
+fs.stat file, (err, stats) ->
+ if err
+ require __dirname + '/../scripts/download'
+ fs.stat file, (err, stat) ->
+ if err
+ throw new Error 'youtube-dl file does not exist. tried to download it but failed.'
+
+
+# command to be called
+cmd = file
+
+
+# main download function
+module.exports.download = (url, dest, stateChange, download, callback, args) ->
+ # setup settings
+ args = parseOpts args
+ args.push url
+
+ # call youtube-dl
+ youtubedl = spawn cmd, args, { cwd: dest }
+
+ err = video = size = state = false
+ regex = /(\d+\.\d)% of (\d+\.\d+\w) at\s+([^\s]+) ETA ((\d|-)+:(\d|-)+)/
+
+ youtubedl.stdout.on 'data', (data) ->
+ data = data.toString()
+
+ # check if video is uploading so script can start
+ # calling the download progress function
+ if state is 'Downloading video'
+ if result = regex.exec data
+ if size is false
+ stateChange state,
+ video: video
+ size: size = result[2]
+ download
+ percent: result[1]
+ speed: result[3]
+ eta: result[4]
+
+ # about to start downloading video
+ else if (pos = data.indexOf '[download] ') is 0
+ state = 'Downloading video'
+
+ # check if this is any other state
+ else if (pos = data.indexOf ']') isnt -1
+ state = data.substring pos + 2, data.length - 1
+
+ # get video name
+ if (pos = state.indexOf ':') isnt -1
+ video = state.substring 0, pos
+ state = state.substring pos + 2
+ stateChange state, video
+
+ youtubedl.stderr.on 'data', (data) ->
+ data = data.toString()
+ err = data.substring 7, data.length - 1
+
+ youtubedl.on 'exit', (code) ->
+ callback err
+
+
+# gets info from a video
+module.exports.info = (url, callback, args) ->
+ # setup settings
+ args = parseOpts args
+ args = [
+ '--get-url'
+ '--get-title'
+ '--get-thumbnail'
+ '--get-description'
+ ].concat args
+ args.push url
+
+ # call youtube-dl
+ youtubedl = spawn cmd, args, spawnOptions
+
+ err = info = false
+
+ youtubedl.stdout.on 'data', (data) ->
+ data = data.toString().split "\n"
+ info =
+ title: data[0]
+ url: data[1]
+ thumbnail: data[2]
+ description: data[3]
+
+ youtubedl.stderr.on 'data', (data) ->
+ data = data.toString()
+ err = data.substring 7, data.length - 1
+
+ youtubedl.on 'exit', (code) ->
+ callback err, info
111 lib/youtube-dl.js
@@ -0,0 +1,111 @@
+(function() {
+ var badArgs, cmd, file, fs, hasArg, parseOpts, path, spawn;
+ spawn = require('child_process').spawn;
+ fs = require('fs');
+ path = require('path');
+ badArgs = ['-h', '--help', '-v', '--version', '-U', '--update', '-q', '--quiet', '-s', '--simulate', '-g', '--get-url', '-e', '--get-title', '--get-thumbnail', '--get-description', '--get-filename', '--no-progress', '--console-title'];
+ parseOpts = function(args) {
+ var arg, pos, _i, _len;
+ if (args == null) {
+ args = [];
+ }
+ for (_i = 0, _len = badArgs.length; _i < _len; _i++) {
+ arg = badArgs[_i];
+ if ((pos = hasArg(args, arg)) !== -1) {
+ args.splice(pos, 1);
+ }
+ }
+ return args;
+ };
+ hasArg = function(arr, arg) {
+ var a, i, _len;
+ for (i = 0, _len = arr.length; i < _len; i++) {
+ a = arr[i];
+ if ((a.indexOf(arg)) === 0) {
+ return i;
+ }
+ }
+ return -1;
+ };
+ file = path.normalize(__dirname + '/../bin/youtube-dl');
+ fs.stat(file, function(err, stats) {
+ if (err) {
+ require(__dirname + '/../scripts/download');
+ return fs.stat(file, function(err, stat) {
+ if (err) {
+ throw new Error('youtube-dl file does not exist. tried to download it but failed.');
+ }
+ });
+ }
+ });
+ cmd = file;
+ module.exports.download = function(url, dest, stateChange, download, callback, args) {
+ var err, regex, size, state, video, youtubedl;
+ args = parseOpts(args);
+ args.push(url);
+ youtubedl = spawn(cmd, args, {
+ cwd: dest
+ });
+ err = video = size = state = false;
+ regex = /(\d+\.\d)% of (\d+\.\d+\w) at\s+([^\s]+) ETA ((\d|-)+:(\d|-)+)/;
+ youtubedl.stdout.on('data', function(data) {
+ var pos, result;
+ data = data.toString();
+ if (state === 'Downloading video') {
+ if (result = regex.exec(data)) {
+ if (size === false) {
+ stateChange(state, {
+ video: video,
+ size: size = result[2]
+ });
+ }
+ return download({
+ percent: result[1],
+ speed: result[3],
+ eta: result[4]
+ });
+ }
+ } else if ((pos = data.indexOf('[download] ')) === 0) {
+ return state = 'Downloading video';
+ } else if ((pos = data.indexOf(']')) !== -1) {
+ state = data.substring(pos + 2, data.length - 1);
+ if ((pos = state.indexOf(':')) !== -1) {
+ video = state.substring(0, pos);
+ state = state.substring(pos + 2);
+ }
+ return stateChange(state, video);
+ }
+ });
+ youtubedl.stderr.on('data', function(data) {
+ data = data.toString();
+ return err = data.substring(7, data.length - 1);
+ });
+ return youtubedl.on('exit', function(code) {
+ return callback(err);
+ });
+ };
+ module.exports.info = function(url, callback, args) {
+ var err, info, youtubedl;
+ args = parseOpts(args);
+ args = ['--get-url', '--get-title', '--get-thumbnail', '--get-description'].concat(args);
+ args.push(url);
+ youtubedl = spawn(cmd, args, spawnOptions);
+ err = info = false;
+ youtubedl.stdout.on('data', function(data) {
+ data = data.toString().split("\n");
+ return info = {
+ title: data[0],
+ url: data[1],
+ thumbnail: data[2],
+ description: data[3]
+ };
+ });
+ youtubedl.stderr.on('data', function(data) {
+ data = data.toString();
+ return err = data.substring(7, data.length - 1);
+ });
+ return youtubedl.on('exit', function(code) {
+ return callback(err, info);
+ });
+ };
+}).call(this);
28 package.json
@@ -0,0 +1,28 @@
+{
+ "name": "youtube-dl",
+ "description": "youtube-dl driver for node",
+ "keywords": ["youtube", "download"],
+ "version": "1.0.1",
+ "homepage": "https://github.com/fent/node-youtube-dl",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/fent/node-youtube-dl.git"
+ },
+ "author": "Roly Fentanes (https://github.com/fent)",
+ "main": "./lib/youtube-dl.js",
+ "directories": {
+ "lib": "./lib"
+ },
+ "scripts": {
+ "preinstall": "node ./scripts/download.js",
+ "update": "node ./scripts/download.js",
+ "preuninstall": "node ./scripts/download.js"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "licenses": [ {
+ "type": "MIT",
+ "url" : "http://github.com/fent/node-youtube-dl/raw/master/LICENSE"
+ }]
+}
52 scripts/download.coffee
@@ -0,0 +1,52 @@
+# module dependencies
+fs = require 'fs'
+path = require 'path'
+https = require 'https'
+exec = require('child_process').exec
+
+
+# vars
+folder = path.normalize __dirname + '/../bin'
+filename = '/youtube-dl'
+filepath = folder + filename
+symlink = path.dirname(process.env._) + filename
+
+
+# download youtube-dl
+switch process.env.npm_lifecycle_event
+ when 'preinstall', 'update'
+ https.get({
+ host: 'raw.github.com',
+ path: '/rg3/youtube-dl/master/youtube-dl'
+ }, (res) ->
+ content = ''
+ res.on 'data', (data) ->
+ content += data
+
+ res.on 'end', () ->
+ # make bin folder if not exists
+ if not path.existsSync folder
+ fs.mkdirSync folder, 0744
+
+ # remove symlink if it exists
+
+ linkExists = (try
+ fs.readlinkSync symlink
+ true
+ catch err
+ false
+ )
+ if linkExists
+ fs.unlinkSync symlink
+
+ # write, chdmod, and symlink file when finished
+ fs.writeFileSync filepath, content
+ fs.chmodSync filepath, 0711
+ fs.symlinkSync filepath, symlink
+
+ ).on 'error', (err) ->
+ throw err
+
+ when 'preuninstall'
+ # remove symlink
+ fs.unlinkSync symlink
50 scripts/download.js
@@ -0,0 +1,50 @@
+(function() {
+ var exec, filename, filepath, folder, fs, https, path, symlink;
+ fs = require('fs');
+ path = require('path');
+ https = require('https');
+ exec = require('child_process').exec;
+ folder = path.normalize(__dirname + '/../bin');
+ filename = '/youtube-dl';
+ filepath = folder + filename;
+ symlink = path.dirname(process.env._) + filename;
+ switch (process.env.npm_lifecycle_event) {
+ case 'preinstall':
+ case 'update':
+ https.get({
+ host: 'raw.github.com',
+ path: '/rg3/youtube-dl/master/youtube-dl'
+ }, function(res) {
+ var content;
+ content = '';
+ res.on('data', function(data) {
+ return content += data;
+ });
+ return res.on('end', function() {
+ var linkExists;
+ if (!path.existsSync(folder)) {
+ fs.mkdirSync(folder, 0744);
+ }
+ linkExists = ((function() {
+ try {
+ fs.readlinkSync(symlink);
+ return true;
+ } catch (err) {
+ return false;
+ }
+ })());
+ if (linkExists) {
+ fs.unlinkSync(symlink);
+ }
+ fs.writeFileSync(filepath, content);
+ fs.chmodSync(filepath, 0711);
+ return fs.symlinkSync(filepath, symlink);
+ });
+ }).on('error', function(err) {
+ throw err;
+ });
+ break;
+ case 'preuninstall':
+ fs.unlinkSync(symlink);
+ }
+}).call(this);
14 test/download.coffee
@@ -0,0 +1,14 @@
+youtube = require '../lib/youtube-dl'
+
+
+# call youtube script
+youtube.download process.argv[2], './', ((state, data) ->
+ console.log 'state: ' + state
+ if data
+ console.log 'data: ' + data
+ ), ((data) ->
+ console.log "Downloading: #{data.percent}%, #{data.speed}, #{data.eta}"
+ ), ((err) ->
+ if err
+ console.log 'error: ' + err
+ )
16 test/download.js
@@ -0,0 +1,16 @@
+(function() {
+ var youtube;
+ youtube = require('../lib/youtube-dl');
+ youtube.download(process.argv[2], './', (function(state, data) {
+ console.log('state: ' + state);
+ if (data) {
+ return console.log('data: ' + data);
+ }
+ }), (function(data) {
+ return console.log("Downloading: " + data.percent + "%, " + data.speed + ", " + data.eta);
+ }), (function(err) {
+ if (err) {
+ return console.log('error: ' + err);
+ }
+ }));
+}).call(this);
9 test/info.coffee
@@ -0,0 +1,9 @@
+youtube = require '../lib/youtube-dl'
+
+# call youtube module
+youtube.info process.argv[2], (err, info) ->
+ throw err if err
+ console.log 'title: ' + info.title
+ console.log 'url: ' + info.url
+ console.log 'thumbnail: ' + info.thumbnail
+ console.log 'description: ' + info.description
13 test/info.js
@@ -0,0 +1,13 @@
+(function() {
+ var youtube;
+ youtube = require('../lib/youtube-dl');
+ youtube.info(process.argv[2], function(err, info) {
+ if (err) {
+ throw err;
+ }
+ console.log('title: ' + info.title);
+ console.log('url: ' + info.url);
+ console.log('thumbnail: ' + info.thumbnail);
+ return console.log('description: ' + info.description);
+ });
+}).call(this);
Please sign in to comment.
Something went wrong with that request. Please try again.