DEV Community

Danities Ichaba
Danities Ichaba

Posted on

HOW TO CREATE VIDEO/AUDIO YOUTUBE DOWNLOADER EXTENSIONS USING JAVASCRIPT AND NODEJS

Follow up to my last week post on learningInPublic

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

What we are building:

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 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 page in order to learn deep and understand the functionalities of some of the API.

Requirements

  • Basic understanding of HTML, CSS and JavaScript
  • Node installed on your machine
  • Ytdl-core(YouTube download module created with Javascript for Nodejs)
  • Google developer account
  • expressjs(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

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 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();
Enter fullscreen mode Exit fullscreen mode

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"
    }
}
Enter fullscreen mode Exit fullscreen mode

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 and 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>
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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);
        })
    }
}
Enter fullscreen mode Exit fullscreen mode

Here, what we did is to create an onClick function anytime the scripts load, inside the onClick function we use the chrome.tabs API 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)
    })
})
Enter fullscreen mode Exit fullscreen mode

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 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 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

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

Top comments (0)