loading...
Cover image for Chrome Extension That Skips YouTube Ads (+Steps How To Create It)

Chrome Extension That Skips YouTube Ads (+Steps How To Create It)

penge profile image Pavel Bucka ・6 min read

Recently I have created chrome extension called My Notes and as it received nice reviews, I have decided to create another one – Skip Ad.

I am also adding a tutorial this time, on how to create the extension.


Skip Ad

Skip Ad is a simple chrome extension that will skip YouTube ads for you, at https://www.youtube.com, so you can enjoy the content without interruptions.

I chose to create Skip Ad as it is something I would like to use personally, and is also simple to create, which makes it suitable to showcase how to create chrome extension.

After this article, you should not only have YouTube "without" ads but also be able to create chrome extension like this one.


Chapters


1 How to install Skip Ad

The extension can be installed from the Web Store, a place where extensions are published.

Link:

https://chrome.google.com/webstore/detail/skip-ad/bimlffinhbdhgpomhngmnhidjgnfcnoc


2 How Skip Ad looks

Once the extension is installed, the following icon appears in the extensions list and the toolbar.

icon

toolbar

If you'd like to hide the icon from the toolbar, right-click the icon and select Hide in Chrome menu.


3 How Skip Ad works

After you open YouTube page, the extension will periodically check whether new ads are present and close them for you. If you already had some YouTube page open before the extension was installed, a simple page refresh will do.

The kind of ads that will be closed for you, and normally you would have to close manually by yourself, are shown below.

video-ad

^^^ Video Ad can be closed with the Skip Ad button (single ad is shown), or Skip Ads button (multiple ads in the playlist).

banner-ad

^^^ Banner Ad can be closed with the X button.

Video Ad and Banner Ad can appear at the beginning of the video, or during the playback.

Buttons that are used to close the ad, are loaded with the ad, but in case of Video Ad, many times displayed after a delay. Skip Ad can skip the ad even before the button that closes the ad is shown. As a result, the ad may appear for a quick moment only.


4 How to create Skip Ad


4.1 Start by creating a new folder

We start by creating a new folder, to hold the extension files.

The name of the folder can be any. Usually, is the same as the name of the extension itself, just in kebab case (also called hyphen case).

Example: extension name Skip Ad => folder name skip-ad

Can two extensions have the same name, you might ask? The answer is yes.
Two extensions can have the same name, and the same code, still, they both can be installed because their unique ID will be different.

For learning purposes, let's call the extension differently – No More Ad.

Create a folder no-more-ad.


4.2 Create manifest.json

Every file we create from now on will go inside the folder no-more-ad.

Every extension has to have manifest.json file. How you name and organize the other files, that's up to you.

Create manifest.json with the following content:

{
  "manifest_version": 2,
  "name": "No More Ad",
  "description": "Skips YouTube Ads.",
  "version": "1.0"
}

Required keys: manifest_version, name and version. Anything else is optional. More at: https://developer.chrome.com/extensions/manifest.

You might be surprised, but at this point, we have an extension that can be installed locally.
It won't do anything though, just appear in the list of extensions.


4.3 Add icon

Download the icon from the repository and put it inside the no-more-ad folder.

Update manifest.json to assign the icon.

{
  "manifest_version": 2,
  "name": "No More Ad",
  "description": "Skips YouTube Ads.",
  "version": "1.0",
  "icons": { "128": "icon128.png" }
}

Now, as the extension has the icon, it will appear in the extensions list and the toolbar. You can try a different icon later.


4.4 Install extension locally

Although we have only manifest.json file, the extension can be installed already and as we update the code, it can be manually reloaded, which is also a way to test if nothing got broken.

To install the extension, open the Extensions list first, either via the menu or by entering chrome://extensions into the tab.

Now, click the Load unpacked button:

load-unpacked

Navigate to no-more-ad folder and click OPEN.

After the extension is loaded, you should see the following card:

card

The orange icon indicates, the extension is installed locally.

If you have installed Skip Ad, disable it temporarily so it doesn't interfere with No More Ad.


4.5 Time to skip the ads

Buttons that are used to skip the ads (3 How Skip Ad works) have CSS classes that make them easy to find.

CSS classes:

// Video Ad; "Skip Ad" or "Skip Ads" button
"ytp-ad-skip-button-text"

// Banner Ad; "X" button
"ytp-ad-overlay-close-button"

The code to skip the ads will be fairly simple. It should look for the buttons that can close the ad, and if it finds them, trigger the click().

Create youtube.js now:

const click = (clazz) => {
  const buttons = document.getElementsByClassName(clazz);
  for (const button of buttons) {
    button.click();
    console.log("No More Ad");
  }
}

setInterval(() => {
  click("ytp-ad-skip-button-text");
  click("ytp-ad-overlay-close-button");
}, 300);
console.log("No More Ad - Init");

You can try the code by opening YouTube and paste it to the console. Then, check some videos. If the ad gets closed, you should see in the console:

"No More Ad"

The last step of the puzzle is to get this code to the YouTube page without manually pasting it, that is, via the extension.


4.6 Setting content script

Content script can be either css, or js, or both. It is a term that, in other words, adds or modifies (as it can access the DOM) the content of the page, where it is inserted.

In our case, we intend to insert earlier created youtube.js to the YouTube page.

This step is very easy, as it requires only to update manifest.json:

{
  "manifest_version": 2,
  "name": "No More Ad",
  "description": "Skips YouTube Ads.",
  "version": "1.0",
  "icons": { "128": "icon128.png" },
  "content_scripts": [
    {
      "matches": ["https://*.youtube.com/*"],
      "js": ["youtube.js"]
    }
  ]
}

We have defined, that any page that matches a YouTube page, should include youtube.js.

The content script defined via manifest.json, is injected declaratively, which means, automatically. It is the easiest way of inserting a script to the other page.

Another way how to inject the content script, is programmatically. It is more complicated, but also more customizable.

Content scripts run in an isolated environment, therefore, they cannot influence other scripts. What they can do, as mentioned above, is access the DOM. And that's exactly what we needed.

More about content scripts is here: https://developer.chrome.com/extensions/content_scripts


4.7 Reload the extension

The extension we installed in the step 4.4 Install extension locally requires to be reloaded, to apply the changes.

This is easy to do by clicking the reload button.

card

If everything went well, no Errors should be shown.


4.8 Test the extension

Now, it's time to test the extension. Open a new YouTube page, or reload the existing.

To see that the content script was inserted, visit Sources:

sources

In the console you can see:

"No More Ad - Init"

Check some YouTube videos. What you should experience is, the ads are getting closed automatically.

Check the console again. For every ad that gets closed, you should see:

"No More Ad"

You have now successfully created your extension.


5 Repository

You can check the extension here:

https://github.com/penge/skip-ad


I hope you enjoyed the article and learned new.

If you have any questions, I will gladly answer them.

If you'd like to see more tips or tricks, or series on this topic, let me know.

Now, let's enjoy this extension and start 2020 with fewer ads!

Cheers!

Posted on by:

Discussion

markdown guide
 

For those who would like to try the solution using Observer out of curiosity, although it seems not to be more performant nor work on pages where #movie_player is not present, here's the code:

const target = document.getElementById('movie_player');
const config = { childList: true, subtree: true };

const callback = function(mutationsList, observer) {
  for(const mutation of mutationsList) {
    if (mutation.addedNodes.length === 0) {
      return;
    }

    if (mutation.target.className !== "video-ads ytp-ad-module") {
      return;
    }

    // Look for Skip Button here :)
  }
};


const observer = new MutationObserver(callback);
observer.observe(target, config);

The solution needs some finishing where the comment is now. Take it as an exercise :)

 

performance would be better using a document observer instead of calling the function every 300 milliseconds

 

Hi!

  1. You need to attach the Observer to the node. The "good" node would be #movie_player but it is not always present on the page, like in home page for example. You would then have to move the observer up the chain to the node that exists on each page. As content script is inserted on the first page load that might not have #movie_player.

  2. If you let's say ignore point 1. (which you can't lol) and test it from #movie_player, then you still find yourself getting HUNDREDS of mutations (no attribute mutations included). You loop the mutations and filter them to only those that have addedNodes. Then, you finally get to the container that encloses the ad, which is having className equal to "video-ads ytp-ad-module".

  3. The ad container that contains the enclosed ad, from point 2., is not the final destination. You still have to look for button inside that node as you don't get mutation for that button. So you end up using getElementsByClassName anyway.

  4. The execution time for the script using setInterval 300ms, is around 0.00024ms every 300ms. That's literally nothing. 500k executions take about 120-130ms and that's tested on really cheap ARM CPU. This execution time, from the top of the tree, is same (no real difference), when compared to starting from #movie_player.

And so for these reasons, I doubt performance using the Observer would be any better.

PS: I'd like to see btw how this could be precisely measured. If you have the time to test it and support your statement with better result, that would be really cool.

 

Thanks for the response, I just checked how many times the Mutationobserver actually triggers while watching a video, and it's so much more than every 300ms! So yes, your method seems more efficient.
One thing I did, in order to hide the ads shown to the right oft the video, is a tiny bit of css code:

#player-ads {display:none !important}

The ads are still loaded, so the publisher should be paid (unlike with adblock, which blocks the request).

That's a good idea! Thanks!

I will add it to the next version.

Video overlay banners can also be handled the same way, as I found out they have a different container than Video ads. Hiding it via CSS would make it longer "visible" and publisher would get paid better as closing the banner is handled by youtube's base.js so it's probably tracked.

Video ads would continue to be handled the same way, with a click.

The final code would be like this:

JS

setInterval(() => {
  for (const button of document.getElementsByClassName("ytp-ad-skip-button")) {
    button.click(); // "Skip Ad" or "Skip Ads" buttons
  }
}, 300);

CSS

.ytp-ad-overlay-container, #player-ads {
  display: none !important;
}

Much better!

 

Thank you for this. I have created an extension for myself now.

 

Thanks very cool, didn't know it was this easy to make a simple extension

 
 

I thought this would be something closer to AdBlock Plus!

 

How about AdSkip Plus? 😀