Send data from a Firefox Web Extension to a Python script and create a simple “playlisting” app for tube videos and songs (Windows). Joao Guedes Follow Feb 6, 2018 · 10 min read

I always surf the web looking for cool videos and songs. Given my “creative” kind of operational mode, sometimes i even download several different videos and songs to make some mix or compilation and re-up it to Youtube. Other times i just wish there was some sort of script or mechanism that allowed me to maybe build up playlists while i surf the web and then hit a button and download everything at once.

So, we alread have youtube-dl out there to provide downloading functionality on tube videos and you can even pass entire YouTube standard playlists pages and it will download everything — except of course those playlists must be built on YouTube.com itself — Maybe it would be better if we had some other way to build our playlists, a simple add-on that all it does is grab that juicy link for the video you want and save it somewhere along with all the other links and then yeah, we download it all at once later. Sounds cool for me. Let’s do that.

Firefox Web Extensions

First, let’s understand how we can create an add-on for Firefox. We are going to need;

Firefox Nightly (other versions do not support unsigned add-ons)

Gulp (to build our extension — pip install gulp)

And that’s it. The next step is to create a folder to put our extension’s files in there. Not necessary but i’d recommend it since we’re are goind to need 3 different files to be zipped together in a .xpi format and that will be the “thing” we will move into Nighly’s window in order to “install” our extension.

The files are;

manifest.json containing the initial declarations and permissions.

background.js which is the actual javascript code for our extension.

gulpfile.js which is the reference for gulp to build our app.

So, let’s begin by writing our manifest.json first.

playlister is the name of our extension

in the applications property, notice there is a playlister_downloader@example.org under ‘gecko’ and ‘id’ keys, that is the name for our Python script that will receive our links and proccess it. You can pick any name you want, just make sure to name your Python script the same later.

“scripts” key holds an array with a single item “background.js”, as said earlier, that .js file will be our main code.

“permissions” declares two things, contextMenus and nativeMessaging, those are the two functionalities from the Firefox Web Extension API we are going to need permission for. Our whole app is basically a sum of these two modules.

Before we get to the actual extension’s code in background.js, let’s quicly make our gulpfile.js file.

So, create a file named gulpfile.js with a .js extension and code in the following;

var gulp = require('gulp');

var zip = require('gulp-zip'); var files = ['manifest.json', 'background.js'];

var xpiName = 'playlister.xpi'; gulp.task('default', function() {

gulp.src(files)

.pipe(zip(xpiName))

.pipe(gulp.dest('.'));

});

So, variable files contains an array with our manifest.json & background.js names. The variable xpiName holds a string with the name for our built/zipped extension — Notice the .xpi format.

You can zip these files together using other means, gulp is just a suggestion that works pretty fine here. Just make sure the file format is .xpi.

Assuming you’re using gulp, all you have to do to actually build everything into our extension package is to cd throught the terminal/shell into the directory containing the files and type the gulp command. Everything should be built fine. However, in order to do that we need our final file, so, let’s code in the background.js.

Background.js is pretty simple, it basically creates a contextMenu with a listener and sends a message to our script everytime a button is pressed.

chrome.contextMenus.create({

id:"add_link",

type:"normal",

title:"Add Link",

contexts:["all"]

}); chrome.contextMenus.create({

id:"save_playlist",

type:"normal",

title:"Save & Create New",

contexts:["all"]

}); chrome.contextMenus.onClicked.addListener((info, tab) => {

var port=chrome.runtime.connectNative("playlist_downloader");



if (info.menuItemId == 'add_link') {

port.postMessage(info.pageUrl);

} else if (info.menuItemId == 'save_playlist') {

port.postMessage("save");

} });

So far, so good.

chrome.contextMenus.create(object) takes an object with different options and create a context menu based on that. Id It will be our internal reference name for the button, title is the text that will appear to the front-end, and contexts are internal options that specify under which circumstances the button will be available. For instance, contexts:[‘videos’] would cause our button to appear only if the mouse-pointer is over a video object. I use context:[‘all’] so we can always see our button in there and know if it’s appearing or not without having to access a video website or any specified context.

Let’s analise the second part better before we move on:

chrome.contextMenus.onClicked.addListener((info, tab) => {

var port=chrome.runtime.connectNative("playlist_downloader");



if (info.menuItemId == 'add_link') {

port.postMessage(info.pageUrl);

} else if (info.menuItemId == 'save_playlist') {

port.postMessage("save");

} });

The first line adds a listener to the context buttons we just created. Inside that listener’s scope, we declare var port to be our connection to our python script named ‘playlist_downloader’.

The first if statement checks info (which is our listener’s argument object containing button properties) too see if it’s menuItemId property is currently ‘add_link’. If it is, means the user clicked ‘add_link’. Then, we use port.postMessage() method and pass it info.pageUrl object property as the argument. What the code basically does so far is to check when the button is clicked and returns whatever info.pageUrl has currently in it’s context.

Right now, every kind of link will be sent, but if we change button’s options from contexts:[‘all’] to contexts:[‘videos’], the button will only appear when hovering over a video object and we’ll always get the video link we intend.

Lastly, the else if statement simply sends the string “save” when the button id’ed “save_playlist” is clicked.

Ok, once we’ve done that. We can already build our .xpi using gulp (or other zipper) and just move that .xpi file to Nightly.

If you are getting “This extension can’t be installed because isn’t signed”, which you should be getting by the way, type in about:config in the browser and change the following settings;

Set the bool values from true to false in this options. Remember only Nightly will accept unsigned extensions even with these options set to false.

If you’re getting “extension is corrupt error” there is probably a typo or some logical flaw in your code. Just double check it.

if you succeeded in “installing” our extension, clicking with the mouse’s right-button anywhere will bring the context-menus and our “playlister” menu should be in there, along with a “Add Link” and “Save & Create New” button.

These buttons don’t do much yet, we need our native Python App. Let’s get to it.