Chad Michael Chad Michael - 2 months ago 30
Javascript Question

Controlling Netflix (HTML5) playback with Tampermonkey/javascript

While watching a netflix video on a Netflix site, my goal is to have a userscript invoke the playback controls programmatically. Specifically, the volume, level, play/pause state, and time position of the video.

I've been able to manipulate the html5 video element itself, but controlling that directly does not provide the needed netflix control bar feedback to the user. (i.e. video is paused, but the control bar still shows it as playing).

My approach thus far has been to try and locate the elements that represent the "buttons" you click when using the normal controls, and trigger their click events through the userscript. But I can't seem to isolate the proper elements. Additionally netflix is using a javascript compressor/obfuscator which increases the difficulty of finding the proper elements that represent the buttons on the control bar.

On a site like this, how can one identify the element that is receiving the click event of an element and then create a userscript to invoke it through tampermonkey and/or greasemonkey?

In the sample code below, I've added a button on the view for testing purposes.

// ==UserScript==
// @name Jump a minute ahead in netflix
// @version 0.1
// @description A Test by trying to jump a minute or so ahead into a netflix movie
// @match *://www.netflix.com/*
// @grant GM_addStyle
// @require http://code.jquery.com/jquery-latest.js
// ==/UserScript==

var zNode = document.createElement ('div');
zNode.innerHTML = '<button id="myButton" type="button">Try It</button>';
zNode.setAttribute ('id', 'myContainer');
document.body.appendChild (zNode);

//--- Activate the newly added button.
document.getElementById ("myButton").addEventListener (
"click", ButtonClickAction, false
);

function ButtonClickAction (zEvent) {
/*--- For our dummy action, we'll just add a line of text to the top of the screen.*/
var zNode = document.createElement ('p');
var video = $("video:first-of-type");
var playerSlider = document.getElementsByClassName("player-slider")[0];

console.log(netflix);
console.log(playerSlider);
console.log(netflix.player);
console.log(netflix.cadmium);
console.log(netflix.cadmium.ui);
console.log(netflix.cadmium.ui.playback);

//video.get(0).pause();
//video.get(0).currentTime = 2000.0;
console.log(video.get(0).currentTime);
console.log(netflix.cadmium.ui.volume);
zNode.innerHTML = 'The button was clicked.';
document.getElementById ("myContainer").appendChild (zNode);
}

//--- Style our newly added elements using CSS.
GM_addStyle ( multilineStr ( function () {/*!
#myContainer {
position: absolute;
top: 0;
left: 0;
font-size: 20px;
background: orange;
border: 3px outset black;
margin: 5px;
opacity: 0.9;
z-index: 222;
padding: 5px 20px;
}
#myButton {
cursor: pointer;
}
#myContainer p {
color: red;
background: white;
}
*/} ) );

function multilineStr (dummyFunc) {
var str = dummyFunc.toString ();
str = str.replace (/^[^\/]+\/\*!?/, '') // Strip function () { /*!
.replace (/\s*\*\/\s*\}\s*$/, '') // Strip */ }
.replace (/\/\/.+$/gm, '') // Double-slash comments wreck CSS. Strip them.
;
return str;
}


The console.log statements are showing some of the things I've found so far. But I haven't figured out how to invoke functions off them, or which of them might have what I'm looking for (I think largely due to the javascript compressor making it difficult for me to follow the code).

Answer

I think this was a case of my thinking too hard about the problem. Once I stepped back, I realized that I had been looking in the right direction, but wasn't triggering the click event properly.

So, for example, to get the "button" that controls play and pause, you could use: document.getElementsByClassName("player-control-button player-play-pause")[0]. Then to click it programmatically in tampermonkey, you simply invoke the click event using:

document.getElementsByClassName("player-control-button player-play-pause")[0].click();

Volume and other controls in the bar are similar. The playback position is looking to be a bit trickier, but I'll do some more digging and add a comment to this answer once I figure it out.

Comments