Control YouTube player from a Chrome extension
Asked Answered
B

4

8

I am developing a Google Chrome extension that needs to take over all the controls of a YouTube video (play, pause, next, previous, change the volume, start video at X seconds, etc). I tried the following approaches:

First approach: with content_scripts

My manifest.json contains the following:

"content_scripts": [
    {
        "matches": ["https://www.youtube.com/*"],
        "js": ["scripts/jquery.js", "scripts/in_page.js"]
    }
],

I'm loading the scripts for every YouTube link, and not only /watch* because now YouTube navigation is fully using AJAX, therefore the page is not entirely refreshed, and if I was on a search page and then clicked on a video, the extension wouldn't have loaded my content_scripts.

How I took over the controls

I successfully managed to trigger simple click events on the YouTube player, such as Play, Pause, Previous video, Next video.

For instance, this works to pause the video:

$("#player-api .html5-video-controls .ytp-button-pause").trigger("click");

But it seems that I can't trigger events such as clicking on the progress bar to play from a specific moment of the video or restart the video.

I tried this piece of code without success (using jQuery's $.Event):

var click = $.Event("click");
click.clientX = 0; // Beginning of the video
click.clientY = 0; // I also tried several other coordinates
$("#player-api .html5-video-controls .html5-progress-bar").trigger(click);

(And I also tried clicking on every child of the .html5-progress-bar, but nothing worked.)

Second approach: with an injected script

As I seemed to have encountered a dead-end with my first approach, I then tried something else: injecting a script directly inside the page.

My manifest.json contains the following:

"content_scripts": [
    {
        "matches": ["https://www.youtube.com/*"],
        "js": ["scripts/jquery.js", "scripts/isolated.js"]
    }
],

"web_accessible_resources": [
    "scripts/injected.js"
],

Contents of isolated.js

var s = document.createElement("script");
s.src = chrome.extension.getURL("scripts/injected.js");
s.onload = function() {
    this.parentNode.removeChild(this);
};
(document.head || document.documentElement).appendChild(s);

Contents of injected.js

Well, this is where I encountered my second dead-end. I may have overlooked some things, but I searched in every object inside the page, I found yt, ytplayer, player, and others. But none of them seem to have proper functions to trigger changes on the player such as play/pause/next, etc.

I then downloaded the html5player.js from YouTube (available here) and beautified it to take a peek at what was under the hood. I found some interesting function names such as playVideoAt, playVideo, pauseVideo around the lines 20235-20315 (once beautified).

That would be exactly the functions I would want to call/invoke, but I don't know where they are, and how to call them.

I also know that YouTube has a JavaScript API to control the player, but it's only for embedded players (iframes), so it's not useful in my case.

Biforked answered 29/6, 2015 at 12:51 Comment(0)
E
13

It sounds like you're using YouTube's HTML5 video player. In that case you can just interface with the <video> DOM element. It happens to be the only <video> element on the page, so just do:

$('video').play()
$('video').pause()
$('video').currentTime = 100

The interface is here.

As for previous and next videos, you're almost certainly right that the easiest thing to do is:

$('.ytp-button-next').click()
$('.ytp-button-prev').click()

Since that obviously isn't a published API you have no guarantee those class names will stay the same in future.

Excurrent answered 29/6, 2015 at 13:5 Comment(2)
Well, needless to say, it clearly was quite simpler than I would have thought. Thanks a lot! Oh and by the way, this only appears to work inside an injected script (not a content script).Biforked
I was mistaken, it works in the content_scripts aswell, but only by using document.getElementsByName("video")[0], as the $("video").play() raises that play is not a function.Biforked
D
0

I'm late to the party, but you can use the API's Youtube provides. Take a look here: https://github.com/kurtextrem/StopAutoplay/blob/master/src/main.js

However, there is another way: The element '#movie_player' provides all these functions as well. So getting movie_player by its ID and then executing "playVideoAt" works.

Dinner answered 7/8, 2015 at 9:51 Comment(0)
T
0

It's too late but I recently created a chrome extension for same: https://chrome.google.com/webstore/detail/youtube-playback-control/okbcoijdeebocmahlanbfemnckjonfnh

Hope it helps.

Tentacle answered 22/1, 2016 at 9:56 Comment(2)
This helps. I have one question - How have you handled message passing between tabId and popup.js? To be specific who is listening "toggle_video_state" message and what are you doing when you receive this message?Varro
@ShubhamGupta You can specify in content_scripts section a js file which gets added to every page that matches provided urls. In that js file you can specify onMesasgeListener which is a chrom api. Also check github repo here: github.com/shahankit/youtube-playback-controlTentacle
S
0

I'm too late, but I think it can still help someone. If you wish to control HTML5 video player using jQuery within content_scripts, then you can get the video element using $("video")[0] and then use that to trigger a valid event, e.g.

$("video")[0].pause()
$("video")[0].play()
..
Snood answered 25/1, 2022 at 23:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.