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:
- Ability to select Video/Audio Qualities
- 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();
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 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>
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 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 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)