HOW TO CREATE VIDEO/AUDIO YOUTUBE DOWNLOADER EXTENSIONS USING JAVASCRIPT AND NODEJS
Tags: videodownloade, audiodownloader, javascript, node
Published: 2022-11-03 10:27:30

Follow up to my last week post on [learningInPublic](shorturl.at/bsILW)

We are building a Video/Audio YouTube downloader google chrome extension using JavaScript and Nodejs. 

**Motivation**
I was looking for ways to improve my JavaScript and NodeJs skills and after some research I concluded to build something i use every day, and the best way to do this is to create extensions that can be installed on my browser. I download Video/Audio on YouTube frequently so I opt to build a Video/Audio Google Chrome extension - Although there are many video/audio extension mine should be one of them.

We are going to build this together.

Get the codes here on [Github](https://github.com/Danities316/Video-Audio-Chrome-Extensions/tree/newBranch)

What we are building: {% embed https://youtu.be/2skcfqL0gYQ %}

Our google Chrome extension would have two capabilities: 

1. Ability to select Video/Audio Qualities
2. Ability to select Video/Audio Fomart(mp3, mp4 etc)

For Google chrome starters, you would need to learn Goggle chrome extensions Features and API. This [article](https://developer.chrome.com/docs/extensions/mv3/getstarted/extensions-101/) was what get me started - They are extensive and very easy to understand, they provide examples for each API and functions/methods. I forked the Chrome extensions [github](https://github.com/GoogleChrome/chrome-extensions-samples) page in order to learn deep and understand the functionalities of some of the API.

**Requirements**
- Basic understanding of HTML, CSS and JavaScript
- [Node](https://nodejs.org/en/download/) installed on your machine
- [Ytdl-core](https://www.npmjs.com/package/ytdl-core)(YouTube download module created with Javascript for Nodejs)
- [Google developer account](https://developer.chrome.com/docs/extensions/mv3/getstarted/)
- [expressjs](https://expressjs.com/)(A simple and lightweight framework to make use of Nodejs easier and faster)

**What I learned**
- Google Chrome Extensions API and how they work
- Google Chrome extensions file structures
- Service worker API and how they worked in the browser
- WriteStream
- ReadStream
- ytdl-core functionalities
- How to deploy Google Chrome extensions

**STEPS**
We need to create a directory for our project. Our directory would contain two folders, one for the chrome extension and the second one for our server(express and ytdl-core)

[**Finding console logs and errors**](https://developer.chrome.com/docs/extensions/mv3/getstarted/development-basics/)

Let start with the local server setup

**Setup the local server with node to help in youtube Video/Audio download**

You would need to install [Nodejs](https://nodejs.org/en/download/) on your machine. After this create a directory called google-chrome-extensions then `cd` into the directory and type `npm init`(This would create `package.json` file in our directory.

Next we would install expressjs and ytdl-core. Run `npm install ytdl-core express` - If we check our directory you would notice a new folder called `node_module` is created and our `package.json` have two module(expressjs and ytdl-core) added into our dependencies objects. That is to say node_module is downloaded into our directory and expressjs and ytdl-core are loaded into our project.

We would create a folder called `server`

`cd` into the folder

create a file called `index.js`

Put the following code into `index.js` and type this code into it

```
const express = require("express");
const ytdl = require("ytdl-core");
const cors = require("cors");

const app = express();

app.use(cors());


app.get("/download", (req, res) => {
  const url = req.query.url;
  const format= req.query.format
  const quality = req.query.quality
  video = ytdl(url, {
    format: format,
    quality: quality,
  }).pipe(res)
  
});

const port = 4000;

const start = () => {
  app.listen(port, () => {
    console.log(`Server started on port ${port}....`);
  });
};
start();
```
What we did is to load the required module(expressjs and ytdl-core), create a GET request and listen to port 4000.

**Setup The Google Chrome extension features** 
Once we have created our local server it is now time to create files and logics for our google chrome extensions.

You need to `cd` out of the server folder into the root directory type `cd..`(I am using windows).

Create a folder called client and run `cd client` - this would contain our extension codes.

Inside the client folder, create a file called `manifest.json`

*What is Manifest.json in chrome extensions? 
This is a mandatory file that provides important information to the chrome extension engine.*

Type this into to manifest.json

```
{
    "manifest_version": 3,
    "name": "Video Downloader",
    "version": "1.0",
    "description": "Download Video and audio from YouTube",
    "action": {
        "default_title": "Download from YouTube",
        "default_popup": "popup.html"
    },
    "permissions": [
        "downloads",
        "activeTab",
        "declarativeContent"
    ], 
    "background" : {
        "service_worker" : "background.js"
    },
    "icons": {
        "16": "images/icon-16.png",
        "32": "images/icon-32.png",
        "48": "images/icon-48.png",
        "128": "images/icon-128.png"
    }
}
```
What we did is define the manifest version, declare some actions(Actions represent actions that can be taken on the current page, but that aren't applicable to all pages, we have [pageAction](https://developer.chrome.com/docs/extensions/reference/pageAction/) and [BrowserAction](https://developer.chrome.com/docs/extensions/reference/browserAction/)) - we would talk about them later, we equally tell our extension the default html(we would create this in a moment) it should use.

Next we need to create the html file for our extension.

while still in client folder create a file called `popup.html` and type in

```
<!DOCTYPE html>
<html>
<head>
	<title>Chrome Extension for Youtube Downloading</title>
	<link rel="stylesheet" type="text/css" href="style.css">
    <script src="/main.js" defer></script>
</head>
<body>
	<h1>Youtube Downloader</h1>
	<p>You must be on the youtube particular video/audio for the Extension to work</p>
	<!-- <input id="url" placeholder="Enter url"> -->
	<label for="quality">Quality</label>
	<select id="quality">
		<option value="highest">Select Quality</option>
		 <option value="highest">highest</option>
		 <option value="lowest">lowest</option>
		 <option value="highestaudio">highestaudio</option>
		 <option value="lowestaudio">lowestaudio</option>
		 <option value="highestvideo">highestvideo</option>
		 <option value="lowestvideo">lowestvideo</option>
	</select>
	<label for="filename">SaveAs</label>
	<input id="filename" placeholder="Enter filename">
	<label for="format">Format</label>
	<select id="format">
		<option value="format">Select Format</option>
		 <option value="mp4">mp4</option>
		 <option value="wmv">wmv</option>
		 <option value="flv">flv</option>
		 <option value="avi">avi</option>
		 <option value="mp3">mp3</option>
		 <option value="mpg">mpg</option>
	</select>
	<button id="download">Download</button>
</body>
</html>
```
What we did here is to create an html structure of our chrome extension. We created a drop-down list of Format and quality - we would style it in a moment.

Next create a CSS file called `style.css` inside the client folder and type this code

```
html {
	background-color: rgb(110, 2, 101);
    color: white;
    width: 625px;
}
h1{
    text-align: center;
}
p{
    text-align: center;
}

label {
	font-size: 15px;
}

#quality {
	padding: 10px;
	text-align: center;
}

#filename {
	padding: 10px;
	font-size: 10px;
}
#format {
	padding: 10px;
	text-align: center;
}
button{
    margin: 12px;
    padding: 10px;
    border-radius: 8px;
    background: white;
    color: rgb(24, 2, 44);
}
Footer
```
What we did here is to style our html.

Next create a JavaScript file called `main.js` inside the client folder

```
window.onload = function(){
    const quality = document.getElementById('quality')
    const filename = document.getElementById('filename')
    const format = document.getElementById('format')
    const downloadBtn = document.getElementById('download')
    
    downloadBtn.onclick = function(){
        console.log("button clicked")
        downloadBtn.innerText = "Downloading file..."
        chrome.tabs.query({
            'active':true, 
            'lastFocusedWindow': true
        },
        (tabs) => {
           var url = tabs[0].url
            var message = {
                'url': url,
                'quality':quality.value,
                'filename': filename.value,
                'format' : format.value
            };
            chrome.runtime.sendMessage(message);
        })
    }
}
```
Here, what we did is to create an `onClick` function anytime the scripts load, inside the `onClick` function we use the [chrome.tabs API](https://developer.chrome.com/docs/extensions/reference/tabs/) to fetch the current(YouTube Page) video url.

Next create a file called `background.js` inside the client folder. You remember we defined `background.js` in our `manifast.json`, essentially, what `background.js` is that it runs once our extension either gets installed or the user refreshes the extension manually.

Type in this code into the `background.js` file

```
chrome.runtime.onInstalled.addListener(() =>{
    //using declarativeContent.onPageChanged to remove previous rules and add new ones
    chrome.declarativeContent.onPageChanged.removeRules(undefined, () => {
        chrome.declarativeContent.onPageChanged.addRules([{
            //conditions to be meet for an action to be taken
            conditions: [
                //matching web pages if and only if all listed criteria are met.We are mathing the page url here
                new chrome.declarativeContent.PageStateMatcher({
                    pageUrl: {hostContains: 'youtube'}
                })
            ],
            // Action to be taken if conditions are met. 
            // we are using ShowPageAction() here - a Declarative event action that shows the extension's toolbar action
            actions: [new chrome.declarativeContent.ShowPageAction() ]
        }])
    })
});

chrome.runtime.onMessage.addListener((message) =>{
    var url = 'http://localhost:4000/download?'
    var queryString = Object.keys(message).map(key => key + '=' + message[key]).join('&')
    url += queryString
    chrome.downloads.download({
        url: url,
        filename: "YoutubeDownloader/" + message.filename + '.' + message.format}, function(downloadID){
            chrome.downloads.show(downloadID)
    })
})
```
What we did here is, we use chrome.runtime API(It retrieves the background page and return details about the manifest, and listen for and respond to events in the extension), the `OnInstalled` event is fired when the extension is first installed while the `addListener` listen to an event. Inside this function we define `chrome.declarativeContent.onPageChanged.removeRules` which enables us to take actions depending on the content of a page. You can learn more [here](https://developer.chrome.com/docs/extensions/reference/declarativeContent/) inside it we define our conditions and actions, we then create a query to send to our server running at localhost:4000. In the request query, we add all the required params. Then we use [chrome.downloads](https://developer.chrome.com/docs/extensions/reference/downloads/) API to send the `GET` request to the server.
Now, if we go back to `index.js` in the server folder, we have created a `GET` request of action ‘download’. When the server receives this `GET` request, we use ytdl-core module to generate a readable stream and then we write it to a writable stream, in our case, response. The response is then received back into the extension and a video file gets downloaded within a folder called “YoutubeDownloader” with the filename and format specified by the user in the defaults downloaded directory.

Finally deploy your codes to chrome extensions web store  - [Publish in the Chrome Web Store](https://developer.chrome.com/docs/webstore/publish/)


**Conclusion and Challenges**
In this post, we have successfully build a chrome extension that enables us download video/audio without hassle, but if you noticed on our downloaded format is always in webm instead of our selected, going forward, we would correct that and also add the ability to download video/audio from a specific part.

On Learning on Public we shall build an URL shortener.... follow me as we learned together.

Thank you