Creating a Chrome Extension - A Hands-On Tutorial with YouTube Remaining

dikka

Dikka

Posted on July 15, 2023

Creating a Chrome Extension - A Hands-On Tutorial with YouTube Remaining

We all love the AI generated cover images, right?

.

This is my first article, so I choose an easy topic and tried to make it a nice read. Feedback is highly appreciated :)

Hey there, Internet Explorer! No, not the browser, you—the adventurous soul who's ventured into the world of Chrome extensions. You've in all likelihood got a bunch of these nifty little tools hanging out in your browser, making your life easier one click at a time. Ever wondered how they're made?

We're going to create a Chrome extension that's so useful, you'll wonder how you ever YouTube-d without it. It's called "YouTube Remaining", and it does exactly what it says on the tin—shows you the remaining time on a YouTube video. No more squinting at the tiny timestamp or doing mental math to figure out if you can squeeze in one more cat video before your next meeting.

And the best part? Making a Chrome extension is as straightforward as your favorite recipe. Think pancakes on a Sunday morning, not a five-course dinner. So, if you can flip a pancake, you're all set to create this extension.

Ready to get started? You can download the finished extension here. But if you're here for the journey, let's roll up our sleeves and dive in!

Getting Started: The Ingredients for Your Extension Recipe

Before we start cooking up our Chrome extension, let's make sure we have all the ingredients on hand. Don't worry, we're not talking about exotic spices or hard-to-find items. The recipe for a Chrome extension calls for a few basic things:

  1. A pinch of HTML and CSS knowledge: You don't need to be a Michelin-starred chef in the world of coding, but knowing your way around HTML and CSS (not really for this extension) is a must.

  2. A dash of JavaScript: This is the secret sauce that brings our extension to life. JavaScript is like that odd ingredient in a Michelin-starred dish — you're not sure what it is, and if you knew, you might be a little grossed out. But somehow, it works in the overall recipe. It's quirky, unpredictable, and, let's face it, a dumpster fire. But just like that peculiar ingredient, JavaScript is essential in a Chrome extension.

  3. Google Chrome: It might seem obvious, but you'll need the Chrome browser installed on your computer. After all, we're making a Chrome extension!

  4. A text editor: This is where you'll be writing your code. There are many options out there, vi, vim, neovim, etc.

Now that we've gathered our ingredients, let's take a look at the recipe. We'll start by setting up our project and creating the manifest.json file, which is like the instruction card for our extension. Then, we'll move on to the contentScript.js file, where we'll write the JavaScript code that powers our extension. Finally, we'll test our extension in Chrome and celebrate our culinary—err, coding—achievement.

The Recipe Card: manifest.json

First up in our Chrome extension cook-off is the manifest.json file. Think of this as the recipe card for your extension. It's where you list out all the key details about your extension, like its name, version, and the permissions it needs. It's also where you tell Chrome which scripts to run and when to run them.

In the case of our YouTube Remaining extension, the manifest.json file is pretty straightforward. It specifies that the contentScript.js file (our main course, which we'll get to in a bit) should be run on YouTube web pages.

{
  "manifest_version": 3,
  "name": "YouTube Remaining",
  "version": "1.0",
  "permissions": ["activeTab", "scripting"],
  "icons": {
    "16": "icon16.png",
    "48": "icon48.png",
    "128": "icon128.png"
  },
  "content_scripts": [
    {
      "matches": ["https://*.youtube.com/*"],
      "js": ["contentScript.js"]
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Let's break down this recipe:

  • "manifest_version": We're using version 3 of the manifest file format. This is the latest version and introduces some changes from version 2, including different ways to request permissions.

  • "name" and "version": Our extension is called "YouTube Remaining", and we're at version 1.0. Every time you update your extension, you'll increment this version number.

  • "permissions": We're asking for the "activeTab" and "scripting" permissions. The "activeTab" permission allows our extension to interact with the active tab when the user invokes the extension, while the "scripting" permission allows us to inject and remove scripts in tabs.

  • "icons": This specifies the icons for our extension at different sizes. These icons will be displayed in various places like the extension management page and the toolbar.

  • "content_scripts": This is where we tell Chrome to inject our contentScript.js file into any page that matches the pattern "https://.youtube.com/". This means our script will run on any YouTube page.

With our manifest.json file set up, we've laid the groundwork for our Chrome extension. Next, we'll move on to the main event: the contentScript.js file.

The Conversion Functions: Translating Time

Before we dive into the main course, let's whip up some side dishes that we'll need later on. In our contentScript.js file, we have two helper functions that deal with time conversion: durationToSeconds and secondsToDuration. These functions are like the sous chefs of our extension, working behind the scenes to ensure everything runs smoothly.

durationToSeconds

Think of this function as our trusty kitchen timer. It takes a duration string in the format "hh:mm:ss" or "mm:ss" and converts it into seconds. This is like breaking down a recipe into simpler steps, making it easier for us to follow along.

function durationToSeconds(duration) {
    const parts = duration.split(':').map(part => parseInt(part, 10));
    let seconds = 0;

    if (parts.length === 3) {
        // Format: hh:mm:ss
        seconds += parts[0] * 3600; // hours
        seconds += parts[1] * 60; // minutes
        seconds += parts[2]; // seconds
    } else if (parts.length === 2) {
        // Format: mm:ss
        seconds += parts[0] * 60; // minutes
        seconds += parts[1]; // seconds
    }

    return seconds;
}
Enter fullscreen mode Exit fullscreen mode

secondsToDuration

Now, let's flip the timer. This function is the reverse of durationToSeconds. It takes a number of seconds and converts it back into a duration string in the format "hh:mm:ss" or "mm:ss". It's like reading the cooking time on a recipe, showing us the time in a format we can easily understand.

function secondsToDuration(seconds) {
    const hrs = Math.floor(seconds / 3600);
    const mins = Math.floor((seconds % 3600) / 60);
    const secs = seconds % 60;

    let duration = "";

    if (hrs > 0) {
        // Pad to 2 digits
        duration += String(hrs).padStart(2, '0') + ":";
    }

    // Pad to 2 digits
    duration += String(mins).padStart(2, '0') + ":";
    duration += String(secs).padStart(2, '0');

    return duration;
}
Enter fullscreen mode Exit fullscreen mode

With these two functions, we've got our timers set, and we're ready to handle time conversion in our extension.

The Sous Chef: waitForElement Function

Next up on our menu is the waitForElement function. This function is like the sous chef who ensures that all ingredients are ready before the chef starts cooking. In the context of our extension, it makes sure that the elements we need are loaded on the page before we start manipulating them.

function waitForElement(selector, callback) {
    if (document.querySelector(selector)) {
        callback();
    } else {
        setTimeout(() => {
            waitForElement(selector, callback);
        }, 500);
    }
}
Enter fullscreen mode Exit fullscreen mode

In a bustling kitchen, timing is everything. You wouldn't want to start sautéing your onions before your garlic is finely chopped, right? Similarly, in a web page, we can't start manipulating elements before they are fully loaded.

The waitForElement function takes a CSS selector and a callback function as arguments. It checks if an element matching the selector exists on the page. If it does, it immediately calls the callback function. If not, it waits for half a second (500 milliseconds) and then checks again, repeating this process until the element is found.

This function is crucial for our extension because YouTube is a dynamic website, which means that elements on the page can load at different times. We use waitForElement to ensure that the video time elements are present on the page before we try to add the remaining time display.

With our sous chef in place, we're ready to start cooking up the main course of our extension: creating and updating the remaining time display.

The Main Course: Serving Up the Remaining Time Display

Now that we've got our sous chef keeping an eye on the ingredients, it's time to start cooking up the main course of our extension. This is where we create the remaining time display and serve it up on the YouTube video player.

First, we wait for the current time element to load on the page. Once it's ready, we select the current time, time separator, and duration elements from the page.

waitForElement('.ytp-time-current', () => {
    const currentTimeElement = document.querySelector('.ytp-time-current');
    const timeSeparatorElement = document.querySelector('.ytp-time-separator');
    const durationElement = document.querySelector('.ytp-time-duration');
Enter fullscreen mode Exit fullscreen mode

Think of these elements as the main ingredients of our dish. We also get the parent element of these elements, which is like our plate where we'll be arranging our ingredients.

    const parentElement = currentTimeElement.parentElement;
Enter fullscreen mode Exit fullscreen mode

Next, we create a new separator and a new span for the remaining time. We add the appropriate classes to these elements and set their text content. For the remaining time element, we initialize it with the full duration of the video, prefixed with a '-' sign to indicate that it's the remaining time.

    const newSeparator = document.createElement('span');
    newSeparator.classList.add('ytp-time-separator');
    newSeparator.textContent = ' / ';

    const remainingTimeElement = document.createElement('span');
    remainingTimeElement.classList.add('ytp-time-remaining');
    remainingTimeElement.textContent = '-' + durationElement.textContent;
Enter fullscreen mode Exit fullscreen mode

Finally, we add our new elements to the parent element, effectively serving up our remaining time display on the YouTube video player.

    parentElement.appendChild(newSeparator);
    parentElement.appendChild(remainingTimeElement);
})
Enter fullscreen mode Exit fullscreen mode

But we're not done yet! Our main course needs a little more seasoning.

The Seasoning: Keeping the Display Updated with MutationObserver

Just like a good chef keeps an eye on their dish while it's cooking, adjusting the seasoning and stirring as needed, our extension needs to keep an eye on the YouTube video player and update the remaining time display as the video progresses. This is where the MutationObserver comes in.

    const observer = new MutationObserver(() => {
        const totalDuration = durationToSeconds(durationElement.textContent);
        const currentTime = durationToSeconds(currentTimeElement.textContent);
        const remainingTime = totalDuration - currentTime;

        remainingTimeElement.textContent = '-' + secondsToDuration(remainingTime);
    });

    observer.observe(currentTimeElement, { childList: true, characterData: true, subtree: true });
Enter fullscreen mode Exit fullscreen mode

Think of the MutationObserver as the seasoning in our dish. It's what gives our extension its dynamic flavor, keeping the remaining time display fresh and accurate as the video plays.

The MutationObserver watches for changes in the textContent of the currentTimeElement. Whenever the current time changes, it recalculates the remaining time and updates the remainingTimeElement.

The Cherry on Top: README.md

Just like a good meal isn't complete without a dessert, a good open-source project isn't complete without a README file. It's the cherry on top that gives people an overview of your project, how to use it, and how to contribute.

Here's what you should include in your README:

  1. Project Title: Give your project a name that clearly describes what it does.

  2. Description: Provide a brief overview of your project. What does it do? Why is it useful?

  3. Installation Instructions: Explain how to get your project up and running. If your project can be installed in multiple ways (like from the Chrome Web Store or manually), be sure to cover all the options.

  4. Usage Instructions: Describe how to use your project. In the case of a Chrome extension, this might include how to enable the extension and what to expect when it's running.

  5. How it Works: Give a high-level overview of how your project works. You don't need to go into the nitty-gritty details (that's what the code is for), but do give readers a sense of the overall architecture and key components.

  6. Contributing Guidelines: If you're open to contributions, provide some guidelines on how others can help. This might include how to submit bug reports, propose new features, or set up a development environment.

  7. License Information: If your project is open source, include information about the license it's released under.

With a well-crafted README, your project is not just a piece of software, but a complete open-source project that others can learn from, use, and contribute to. It's been a pleasure cooking up this extension with you, and I hope you're now feeling ready to create your own Chrome extensions.

💖 💪 🙅 🚩
dikka
Dikka

Posted on July 15, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related