Skip to content

Instantly share code, notes, and snippets.

@yulioaj290
Last active June 19, 2019 17:42
Show Gist options
  • Save yulioaj290/075bf6c57d01b69a2991b0a8ed869f72 to your computer and use it in GitHub Desktop.
Save yulioaj290/075bf6c57d01b69a2991b0a8ed869f72 to your computer and use it in GitHub Desktop.

YouTube video downloader

Script Node.JS for downloading videos from YouTube. You can use some options to choose the features of the video, like format, size, etc. This script only produce a file video.sh_, with all commands to download a video from YouTube.

Requirements

  • NodeJS 8.4.0 or higher.
  • NPM 5.3.0 or higher.
  • Bzip2 1.0.6 or higher.

To prepare the environment for the script follow this steps (based on Ubuntu 14.04):

  1. (Optional) Update your linux repositories and upgrade all packages.
$ sudo apt-get update
$ sudo apt-get upgrade
  1. Install bzip2 on your system, it is neccesary to unzip node package modules.
$ sudo apt-get install bzip2
  1. Install PhantomJS dependencies on your Debian/Ubuntu (or Linux) system.
$ sudo apt-get install libfontconfig1 fontconfig libfontconfig1-dev libfreetype6-dev
  1. Install node dependencies included into the package.json.
$ npm install
  1. Verify the packages "phantomjs" and "casperjs" have been installed and linked on executable binaries system folder '/usr/bin/'.
$ phantomjs -v
$ casperjs -v

or

$ phantomjs --version
$ casperjs --version

Note: If some of this packages were trouble, you must create a symbolic link of their executables.

$ ln -s /absolute/path/to/project/node_modules/phantomjs/bin/phantomjs /usr/bin/phantomjs
$ ln -s /absolute/path/to/project/node_modules/casperjs/bin/casperjs /usr/bin/casperjs
  • Check again the step 4.
  1. Execute the script.
$ $ casperjs --ignore-ssl-errors=true youtube_downloader.js --helpme
{
"version": "0.0.1",
"private": true,
"name": "Laracasts.com video downloader",
"description": "Laracasts.com video downloader",
"repository": "https://gist.github.com/yulioaj290/20b4dccf19a262880497b66539f6bd87",
"license": "MIT",
"devDependencies": {
"casperjs": "^1.1.1",
"http-server": "^0.6.1",
"karma": "^0.12.16",
"karma-chrome-launcher": "^0.1.4",
"karma-jasmine": "^0.1.5",
"phantomjs": "^1.9.19",
"protractor": "~1.0.0",
"shelljs": "^0.2.6",
"tmp": "0.0.23"
},
"scripts": {
"prestart": "npm install",
"start": "http-server -a 0.0.0.0 -p 8000",
"pretest": "npm install",
"test": "node node_modules/karma/bin/karma start test/karma.conf.js",
"test-single-run": "node node_modules/karma/bin/karma start test/karma.conf.js --single-run",
"preupdate-webdriver": "npm install",
"update-webdriver": "webdriver-manager update",
"preprotractor": "npm run update-webdriver",
"protractor": "protractor test/protractor-conf.js",
"update-index-async": "node -e \"require('shelljs/global'); sed('-i', /\\/\\/@@NG_LOADER_START@@[\\s\\S]*\\/\\/@@NG_LOADER_END@@/, '//@@NG_LOADER_START@@\\n' + '\\n//@@NG_LOADER_END@@', 'app/index-async.html');\""
}
}
/**
* Copyright 2017 Yulio Aleman Jimenez
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
* NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
var fs = require('fs');
var childProcess = require("child_process");
var casper = require('casper').create({
verbose: true,
pageSettings: {
webSecurityEnabled: false, // Disable web security [SSL, TLS]
loadImages: false, // Disable image loading
loadPlugins: false,
}
});
// Base URL for downloading service
var baseURL = 'http://video.genyoutube.net/';
// Getting parameter's values
var IDOption = 'id' in casper.cli.options ? casper.cli.options.id : false; // Required
var maxWeightOption = 'maxw' in casper.cli.options ? parseFloat(casper.cli.options.maxw) : false; // Required
var formatOption = 'format' in casper.cli.options ? casper.cli.options.format.toUpperCase() : "MP4";
var novideoOption = 'novideo' in casper.cli.options ? true : false;
var nosoundOption = 'nosound' in casper.cli.options ? true : false;
var uploaderOption = 'uploader' in casper.cli.options ? casper.cli.options.uploader : false;
var rmAfterOption = 'rm-after-upload' in casper.cli.options ? true : false;
var helpmeOption = 'helpme' in casper.cli.options ? true : false;
// Checking parameters
if (!IDOption || !maxWeightOption || helpmeOption) {
casper.echo("YOUTUBE DOWNLOADER SCRIPT");
casper.echo("================================================== \n");
casper.echo("USAGE: casperjs --ignore-ssl-errors=true script.js --id=<YouTube_ID> --maxw=<Max_Weight> --format=<Format> --novideo --nosound --uploader=<Uploader_Script_Path> --rm-after-upload --helpme");
casper.echo("[--ignore-ssl-errors=true]: ---------- [REQUIRED] To say the script to ignore the SSL errors.");
casper.echo("[script.js]: ------------------------- [REQUIRED] Name of your YouTube Downloader JS script.");
casper.echo("[--id=<YouTube_ID>]: ----------------- [REQUIRED] ID fo the YouTube Video.");
casper.echo("[--maxw=<Max_Weight>]: --------------- [REQUIRED] Max weight of the file (in MB) to download.");
casper.echo("[--format=<Format>]: ----------------- [OPTIONAL] Format of the video or audio for downloading. Possible values are MP4, 3GP, WEBM, M4A. Default format is MP4.");
casper.echo("[--novideo]: ------------------------- [OPTIONAL] Filter download links to not contain Video.");
casper.echo("[--nosound]: ------------------------- [OPTIONAL] Filter download links to not contain Sound.");
casper.echo("[--uploader=<Uploader_Script_Path>]: - [OPTIONAL] Path to the JS script for uploading to personal storage.");
casper.echo("[--rm-after-upload]: ----------------- [OPTIONAL] Remove directory with downloaded file, after upload it to the personal storage. ");
casper.echo("[--helpme]: -------------------------- [OPTIONAL] Only print this help. This parameter avoid the execution of the script content. ");
casper.echo("================================================== \n");
casper.echo("EXAMPLES: \n");
casper.echo("1. casperjs --ignore-ssl-errors=true youtube_downloader.js --helpme");
casper.echo("2. casperjs --ignore-ssl-errors=true youtube_downloader.js --id=NduID48s-B --maxw=60");
casper.echo("3. casperjs --ignore-ssl-errors=true youtube_downloader.js --id=RsY30mqj-s --maxw=100 --format=WEBM --uploader=/home/jhon/uploader.js --rm-after-upload");
casper.echo("================================================== ");
casper.echo("Made by Yulio Aleman Jimenez [https://github.com/yulioaj290]");
casper.echo("================================================== ");
casper.exit(0) ;
}
// wait 7 seconds before giving up
casper.options.waitTimeout = 10000000
casper.onResourceTimeout = function(request) {
casper.echo('Response (#' + request.id + '): ' + JSON.stringify(request));
};
// Config the user agent
// casper.userAgent('Mozilla/5.0 (Macintosh; Intel Mac OS X); Safari');
casper.userAgent('Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1');
// Accept any kind of answer for the request, on the page Headers
casper.on('started', function () {
this.page.customHeaders = { 'Accept': '*/*' }
});
// casper.on('resource.error',function (request) {
// this.echo('RESOURCE: [' + request.id + '] IN: [' + request.url + '] SAID: [' + request.errorString + ']' );
// });
// Validating the format
if(formatOption != "MP4" && formatOption != "3GP" && formatOption != "WEBM"){
casper.echo("WARNING: Incorrect video format [" + formatOption + "].");
casper.echo("INFO: Setting video format to MP4.");
formatOption = "MP4";
}
// Initializing the output bash script
var output = "#!/bin/bash \n";
casper
.start()
.open(baseURL + IDOption)
.then(function () {
// Check if the download link list do exist
if(this.exists('div#downloadlist a[data-format="' + formatOption + '"]')){
casper.echo("Retrieving from download list... \n");
// Storing the list of all links that fit the requirements
var listOfLinks = this.evaluate(function(){
// Getting name, href & weight of each download link
var links = [].map.call($('div#downloadlist .downbuttonbox > a[href!="#"][data-format]'), function(linkItem) {
var name = linkItem.getAttribute("href").split('&title=GenYoutube.net_')[1];
var url = linkItem.getAttribute('href');
var weight = linkItem.querySelector("span.label.labelw").textContent ;
var format = linkItem.getAttribute('data-format');
var no_video = linkItem.querySelector(".glyphicon.glyphicon-music") != null ? true : false;
var no_sound = linkItem.querySelector(".glyphicon.glyphicon-volume-off") != null ? true : false;
// [name, url, weight, format, no_video, no_sound]
return [name, url, weight, format, no_video, no_sound];
});
return links;
});
// require('utils').dump(listOfLinks);
var bestChoice = -1;
var mayor = 0;
if(listOfLinks != null && listOfLinks.length > 0){
// Choosing the best link to download
for(var i = 0; i < listOfLinks.length; i++){
var videoWeight = listOfLinks[i][2];
var realWeight = parseFloat(videoWeight.split('MB')[0]);
if(realWeight < maxWeightOption &&
realWeight > mayor &&
formatOption == listOfLinks[i][3] &&
novideoOption == listOfLinks[i][4] &&
nosoundOption == listOfLinks[i][5]
){
bestChoice = i;
mayor = realWeight;
}
}
// Print download link list
casper.echo("DOWNLOAD LINKS FOR YOUTUBE VIDEO ");
casper.echo("[" + listOfLinks[0][0] + "]");
casper.echo("[" + IDOption + "]");
casper.echo("============================================================================== ");
for(var i = 0; i < listOfLinks.length; i++){
casper.echo("[" + (i < 9 ? " " : "") + (i + 1) + "] " + (bestChoice == i ? "[X]" : " ") + " [" + listOfLinks[i][2] + "][" + listOfLinks[i][3] + "]" + (listOfLinks[i][4] ? "[No Video]" : "") + (listOfLinks[i][5] ? "[No Sound]" : "") + (bestChoice == i ? "[SELECTED]" : ""));
casper.echo("--------------------------------------------------");
}
casper.echo("==============================================================================\n");
if(bestChoice != -1){
// Add download instruction to the output bash script
output += 'mkdir tmpvideo \n';
output += 'wget -O "' + listOfLinks[bestChoice][0] + '" "' + listOfLinks[bestChoice][1] + '" \n';
output += 'mv ' + listOfLinks[bestChoice][0] + ' tmpvideo \n';
if(!!uploaderOption){
if(!fs.isFile(uploaderOption)){
casper.echo("ERROR: The file [" + uploaderOption + "] don't exist.");
} else {
output += 'casperjs --ignore-ssl-errors=true ' + uploaderOption + ' tmpvideo/ \n';
output += 'echo "INFO: Uploaded file to personal storage." \n';
if(rmAfterOption){
output += 'rm -rf tmpvideo \n';
output += 'echo "INFO: Removed downloaded files." \n';
}
}
}
// Writing instructions on the final bash script
this.then(function(){
// output += 'mv ' + listOfLinks[bestChoice][0] + '* upload/ \n';
fs.write("video.sh", output, 'w');
// Execute the bash script
// casper.waitForExec('/bin/bash -c', ['{video.sh}']);
if (childProcess) {
// childProcess.execFile("/bin/bash", ["video.sh"]);
var myscript = childProcess.exec('sh video.sh', function (error, stdout, stderr) {
casper.echo('stdout: ' + stdout);
casper.echo('stderr: ' + stderr);
if (error !== null) {
casper.echo('exec error: ' + error);
}
});
} else {
this.log("Unable to require child process!", "warning");
}
if(rmAfterOption){
fs.remove("video.sh");
casper.echo("INFO: File video.sh removed successfully!");
}
});
} else {
casper.echo("WARNING: There is no links to satisfy your requirements!");
casper.echo("Change your requirements to get success.");
}
} else {
casper.echo("WARNING: There is no links to download video!");
}
}
casper.echo("INFO: Operation done!!!");
});
// Running the script
casper.run();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment