Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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).

like image 222
Chad Michael Avatar asked Jan 13 '15 17:01

Chad Michael


2 Answers

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.

like image 128
Chad Michael Avatar answered Oct 06 '22 05:10

Chad Michael


I know this an older question, but I wanted to post this answer for the benefit of future users.

You can now control the playback of Netflix by accessing their player API, by executing the following Javascript (originally contributed by Dmitry Paloskin, on this topic):

const videoPlayer = netflix.appContext.state.playerApp.getAPI().videoPlayer;

// Getting player id
const playerSessionId = videoPlayer.getAllPlayerSessionIds()[0];

const player = videoPlayer.getVideoPlayerBySessionId(playerSessionId);

You can then use the API to perform various commands on the Netflix player, such as seeking (by using player.seek() with the number of milliseconds in the parentheses), playing or pausing the video (by using player.play() or player.pause() respectively), or controlling the volume (by using player.setVolume() with the value in the parentheses, with 1 being 100 percent and 0 being 0 percent).

This method avoids the issue with Netflix crashing when trying to change the video element's currentTime property, as mentioned in the reply above from rogerdpack.

like image 22
The-Coder-Who-Knew-Too-Little Avatar answered Oct 06 '22 04:10

The-Coder-Who-Knew-Too-Little