17

I have a Netflix account and I have peeked under the hood at its video player running inside Google Chrome. Netflix calls its video player "Cadmium" and the javascript exposes all the functions and event handlers you might expect, such as play, stop, pause, mute, etc. I'm building a little Chrome extension that would enable me to call these Cadmium player function, but the hard part for me is figuring out how to create an instance of the player so I can start calling. The javascript is large, complex, and somewhat obscure. Once I can create an instance of that player, I'm thinking that making calls into the functions will be easy.

Here is a relevant chunk of js:

muteOn: function() {
          this.savedVolume = this.getVolume(),
          this.updateVolumeDisplay(0),
          this.scrubber.updatePercent(0),
          this.muted = !0,
          this.videoPlayer.setMuted(this.muted)
}

In Chrome dev tools I can set a breakpoint inside that block, and execution hits the breakpoint when I click the Mute button on the netflix video player. The Netflix js is (unsurprisingly) heavily obfuscated via method renaming. I tried stepping through the code in the debugger and ended down a hundred rabbit holes, never able to find my way to the top of the stack, so that I could make that same call (at top of stack) to simulate the user clicking the mute button. I also tried the approach of programmatically clicking the mute button on the UI player, which would meet my needs equally well, but they have serious defensive mechanisms in there, spinning me like a top.

Since there are over 100K lines of javascript, and I'm uncertain which chunks exactly would be relevant for this post, I would like to suggest that you load Netflix in Chrome, open dev tools, play a movie, and inspect the pause or mute button. Interacting with those video player controls takes you into the maze of javascript which I'm trying to see how I can tap into to control aspects of the player programmatically (just from dev tools is fine for now). Another important thing I need to figure out is how to query the video player to determine the current elapsed time of the playing video.

Any ideas how I can crack this nut? (Thanks in advance!)

HerrimanCoder
  • 5,858
  • 20
  • 65
  • 111

1 Answers1

14

Using Chrome, I get playback using HTML 5 video.

Once you get a hold on the <video> tag element, you can use the HTML 5 video API:

Get the <video> element

var video = document.evaluate('//*[@id="70143639"]/video',document).iterateNext()

70143639 is the id of the video, as in https://www.netflix.com/watch/70143639

Remaining time (HH:mm)

document.evaluate('//*[@id="netflix-player"]/div[4]/section[1]/label', document).iterateNext().innerHTML

Elapsed time (seconds)

video.currentTime

Elapsed time updates

video.addEventListener("timeupdate",
    function(e) {
        console.debug("Seconds elapsed: ", e.timeStamp/1000/60);
    }
);

Note that I don't get the same results as with video.currentTime, you may need to use an offset based on the difference. Also it may be something explained in the spec: https://www.w3.org/TR/html5/embedded-content-0.html

Play

video.play();

Pause

video.pause();

Go back and forth in time

Courtesy of rebelliard: netflix.cadmium.UiEvents.events.resize[1].scope.events.drage‌​nd[1].handler(null, {value: 600, pointerEventData: {playing: false}}); where 600 is the number of seconds to seek.

Note that I ran into "Whoops, something went wrong..." using this:

video.currentTime += 60;

Even with pause and play calls. This is what this demo page does, you nay need to read the full spec on seeking.

Mute and get muted status

video.muted = true

Like video.currentTime, this is a writeable property.

pyb
  • 3,448
  • 1
  • 22
  • 35
  • pyb, this is quite promising, but I'm having a few problems. Although getting elapsed and remaining time work great, I'm unable to successfully call `video.play()` or `video.pause()`. I get `video.play is not a function` and `video.pause is not a function`, respectively. Also the event listener does nothing -- I was expecting to see console outputs after executing that and clicking the Play button on netflix, but there is nothing. I am calling all these from the Console window of Chrome dev tools. Any thoughts? – HerrimanCoder Feb 05 '17 at 03:23
  • One more note: the `video` variable seems to evaluate just to a label showing elapsed time -- not to the player itself. – HerrimanCoder Feb 05 '17 at 14:21
  • 1
    @SweatCoder Apologies, I mixed the code to get `video` and the label of the remaining time. I edited my answer, this should fix all the issues you mentioned above. – pyb Feb 06 '17 at 01:36
  • Thanks pyb, that was very helpful. Any new thoughts on getting video.currentTime (set/seek) to work? I am running into brick walls on that. – HerrimanCoder Feb 06 '17 at 13:23
  • I played a bit yesterday, trying small increments to no avail. Even if the requested range has been buffered, I get the same. It may be a DRM protection from Netflix to prevent automated playback (screen recording would become possible). I'm thinking they may be using a JS event listener to detect these kind of manipulations. Best of luck and thanks for accepting my answer :) – pyb Feb 06 '17 at 13:31
  • Please see http://stackoverflow.com/questions/42105028/netflix-video-player-in-chrome-how-to-seek – HerrimanCoder Feb 08 '17 at 04:57
  • Doing `video.currentTime += 10` causes Netflix to throw "Whoops, something went wrong". :/ – rebelliard Jul 23 '17 at 16:33
  • @rebelliard yes, that's what I said in the answer. I tried several things but none worked. Let us know if you're more successful! – pyb Jul 24 '17 at 13:33
  • 1
    @pyb seeking worked for me this way: `netflix.cadmium.UiEvents.events.resize[1].scope.events.dragend[1].handler(null, {value: 600, pointerEventData: {playing: false}});`, where `value` is the playback seconds to seek. – rebelliard Jul 24 '17 at 17:06
  • Great, looks like you're simulating a mouse drag event. Adding it to the answer. Thank you. – pyb Jul 24 '17 at 19:26
  • The cadmium object no longer exists, at least not within the netflix object. – HerrimanCoder Sep 30 '17 at 19:30