内容简介:Command Line Interfaces (CLIs)have been around for close to 50 years now, and still seem to be going strong. Even in the age of amazing libraries like React, VueJS, and Angular, text-based terminals remain the best way to accomplish a multitude of tasks.CL
Building a Simple CLI Youtube Video Downloader in NodeJS
Command Line Interfaces (CLIs)have been around for close to 50 years now, and still seem to be going strong. Even in the age of amazing libraries like React, VueJS, and Angular, text-based terminals remain the best way to accomplish a multitude of tasks.
CLIs are incredibly useful for developers seeking to automate workloads, e.g. when deploying new applications, running tests and migrating data. Besides, a lot of times, building a Graphical User Interface (GUI) is complete overkill for something that could be easily accomplished with just a few lines of code.
NodeJS is the perfect language for writing CLI apps thanks to the multitude of built-in libraries available for everything from network communication to reading and writing files. If you’re feeling lazy like every developer does every once in a while, the million of packages available on registries like NPM and YARN will definitely come in handy.
In this tutorial, we create a basic NodeJS CLI app for downloading music files from Youtube and converting them to MP3. This will serve as an introduction on how to interact with web interfaces through the CLI , style the output and accept arguments through the command line.
Setting Up the Project
First things first, we’ll set up our project by initializing NPM (which creates a ‘package.json’ file for us) and ‘git’ (because every project needs git).
Assuming you already created a project folder, simply run
npm init && git init
Then let’s get all the dependencies out of the way. Each of them will be covered in more specific detail later on in this guide.
npm install commander ora youtube-mp3-downloader -S
- Commander is a library that takes care of a lot of CLI boilerplate. It’s incredibly useful when you just want to get a project running in the shortest time possible.
- Ora adds styling to our command line output
- Youtube-mp3-downloader does what the package name suggests – it pulls videos from Youtube and converts them to mp3 files.
Downloading Files from Youtube
First, let’s create a ‘downloader.js’ file in ‘src/downloader.js.’ This will be responsible for handling downloads and storage locations.
It takes the following options:
- ffmpegPath : ffmpeg installation location. ffmpeg is a large open-source program used for handling audio and video files. In our case, it’s responsible for converting video files to mp3 format. If you don’t have it installed, run
sudo apt install ffmpeg
To check the installation location on your system, runwhich ffmpeg
- outputPath : where music files will be saved
- youtubeVideoQuality : download quality. “highest”, “lowest” or a number from 1-10
- queueParallelism : how many files can be converted consecutively
- progressTimeout : how often the progress indicator should be updated
const YoutubeMp3Downloader = require('youtube-mp3-downloader'); const downloader = new YoutubeMp3Downloader({ ffmpegPath: "/usr/bin/ffmpeg", outputPath: "/home/cray/mp3-downloads", youtubeVideoQuality: 'highest', queueParallelism: 3, progressTimeout: 5000 });
The ‘downloader’ instance accepts two arguments – the Youtube video id and the name of the file once downloaded. The second argument is optional.
Let’s export a function that will make the downloader
option accessible to other files.
let url = require('url'); module.exports = { download: function (videoIdOrLink, fileName) { return new Promise(((resolve, reject) => { if (!videoIdOrLink) { throw new Error("Please enter a valid video id or link") } let videoId = videoIdOrLink; if (isURL(videoIdOrLink)) { let urlQueryObj = url.parse(videoIdOrLink, true).query videoId = urlQueryObj.v; } downloader.download(videoId, fileName); downloader.on('finished', function (err, data) { resolve(data); }); downloader.on('error', function (err) { reject(err); }); })) }, downloader };
First, copy-pasting the video id from every link we want to download is a little cumbersome. Instead, we check if whatever has been input by the user is a URL, and if so, extract the ‘v’ query from the Youtube URL.
Recall that Youtube URLs typically look like this: https://www.youtube.com/watch?v=4l1gNOocZu8
. The id is the 4l1gNOocZu8
part.
The isURL
helper is borrowed from Stackoverflow and looks like this:
module.exports.isURL = function (str) { const urlRegex = '^(?!mailto:)(?:(?:http|https|ftp)://)(?:\\S+(?::\\S*)?@)?(?:(?:(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[0-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{2,})))|localhost)(?::\\d{2,5})?(?:(/|\\?|#)[^\\s]*)?$'; const url = new RegExp(urlRegex, 'i'); return str.length < 2083 && url.test(str); };
url.parse
is taken from the built-in NodeJS library and accepts two parameters – the url and a boolean indicating whether it should parse for query strings (which we want in this case). It then returns an object with all the queries.
Finally, we call the download function with the now-extracted video id.
Now comes the fun part.
Interacting With a NodeJS App Through the Command Line
While it’s entirely possible to write a NodeJS CLI app with Vanilla Javascript, Commander makes it much easier to do so.
const program = require("commander"); program.version('0.0.1').description("A simple node-cli youtube downloader"); program.command('ytd') .requiredOption('-l, --link <link>', 'A youtube video link or id') .option('-n, --name [name]', 'Name of the downloaded file') .action((cmObj) => { let {link, name} = cmObj; console.log(link, name) }); program.parse(process.argv);
Let’s go over the above code:
version
indicates what version of our CLI app we are running.
description
provides a description of our app.
command
adds a new command that we can run different programs through. For instance, adding ytd
, in this case, allows us to run
node src/index.js ytd --version
If we wanted a different app that would say, download videos from Instagram, we might also add
program.command(‘instd’) //...
And run the code as
node src/index.js instd --version
option
is used to add different options to our app. The above code might be refactored to read
program.command('ytd <link>')
So that downloading would just be as simple as
ytd https://www.youtube.com/watch?v=zDul1dkWxFk
However, using arguments makes the code a lot clearer.
ytd -l https://www.youtube.com/watch?v=zDul1dkWxFk -n “Emptions.mp3”
Optional arguments can also be indicated using square brackets []
and required arguments using ‘<>’
action
is where all the magic happens. Our callback function might look like
//... .action((cmObj) => { let {link, name} = cmObj; Downloader.download(link, name) .then(finishedObj => { console.log("Succeeded.") }).catch(err => { console.log("Failed.") }); }); //..
Note that the commander makes all details about the argument that’s been called available through the callback function.
The final line parses all arguments we’ve passed for us.
While this actually works, it’s all a bit boring. Besides, we have no way of knowing whether the program has stalled or is still working, so let’s add some spinners.
Adding Loading Spinners to Our Node CLI App
To create a simple CLI spinner, we’re going to use ora .
Initializing it needs just three lines:
const ora = require('ora'); //The initial loading text const spinner = ora('Downloading...').start(); spinner.color = 'yellow';
And our ‘action’ callback can be changed to
//... .action((cmObj) => { let {link, name} = cmObj; Downloader.download(link, name) .then(finishedObj => { //tell the spinner everything is done loading spinner.succeed(`Finished downloading...${finishedObj.videoTitle} in ${finishedObj.states.runtime} seconds`); }).catch(err => { //tell the spinner something went wrong. spinner.fail("Could not download that file. An Error occurred.") console.error(err); }); //recall that we exported the ‘downloader’ object alongside the promise function Downloader.downloader.on('progress', function (progressObj) { //every time the progress is updated, round off the percentage done to the nearest 2 d.p and set it to the spinner spinner.text = `${Number(progressObj.progress.percentage).toFixed(2)}% done`; }) }); //...
Now, running our Node CLI app:
Running Node CLI app
Conclusion
Command line apps have an almost unlimited number of uses, and this app just scratches the surface of all the amazing things you can build using NodeJS and a few related libraries.
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
创新者的窘境(全新修订版)
克莱顿•克里斯坦森 / 胡建桥 / 中信出版社 / 2014-1-1 / 48.00元
全球商业领域中,许多企业曾叱咤风云,但面对市场变化及新技术的挑战,最终惨遭淘汰。究其原因,竟然是因为它们精于管理,信奉客户至上等传统商业观念。这就是所有企业如今都正面临的“创新者的窘境”。 在《创新者的窘境》中,管理大师克里斯坦森指出,一些看似很完美的商业动作——对主流客户所需、赢利能力最强的产品进行精准投资和技术研发——最终却很可能毁掉一家优秀的企业。他分析了计算机、汽车、钢铁等多个行业的......一起来看看 《创新者的窘境(全新修订版)》 这本书的介绍吧!