9

I have been unable to figure out how to do a video seek (automatically advance to a certain point in the video) in the Netflix video player running in Chrome. The currentTime property can be read but not set in the Netflix player, and when set, immediately triggers the error "Whoops! Something went wrong.". Other actions such as Play and Pause work quite well. For example, you can try the following:

  1. Log into Netflix (from Google Chrome) and go to the movie Armageddon.
  2. After the movie loads, pause it if it starts playing.
  3. Open the Chrome Developer Tools panel. Go to the Console tab.
  4. Paste the following snippet into the console and hit <ENTER>:

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

Note: The id value is specific to Armageddon. If you choose a different movie, which is fine, change the id as per the id in the URL of that movie.

  1. Enter the following and then press <Enter>: video.play(). Observe that the video resumes playing.

Simple enough, but how to make the video auto-advance to a specific point in the video? You may want to refer to this doc. Obviously you can manually seek by dragging the video player slider from left to right and release it someplace. You may wish to discover which method or event is called when you do this, and simulate that. I haven't had luck thus far.

Any ideas?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
HerrimanCoder
  • 6,835
  • 24
  • 78
  • 158

5 Answers5

29

Looks like netflix changed player api. Its what I found:

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

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

const player = videoPlayer
  .getVideoPlayerBySessionId(playerSessionId)

Now you can use full player API. For example player.seek or player.getCurrentTime or player.pause, etc...

DimaOverflow
  • 1,513
  • 1
  • 16
  • 18
  • 2
    This is exactly what I was looking for. The `cadmium` object is no longer availabled through the `netflix` api. Thanks. – Fethi Dilmi Oct 30 '17 at 17:59
  • 2
    Dimitry, do you mind explaining your deductive process for figuring this out? I gave up on this months ago because I just couldn't crack it. How did you go about figuring this out? What tools, what process, what train of thought. Thanks! – Stephen Cagle Nov 12 '17 at 06:37
  • 4
    @StephenCagle Nothing supernatural :) Just chrome dev-tools and its autocomplete. The first I looked for any global variable (so I found `window.netflix`). Then I just attentively reading all properties, methods names, etc and took a lot of time to experiments with it :) – DimaOverflow Nov 13 '17 at 07:43
  • 1
    Ah too bad, I was hoping for some magic. :) Yeah, you're gonna laugh, but I actually experimented with jiggling the mouse, finding the slider, calculating the seek position, and then generating a mouse event. It was awful. ;) – Stephen Cagle Nov 15 '17 at 03:22
  • You will laugh too, because I did the same actions at first time, lol :) – DimaOverflow Nov 20 '17 at 13:44
  • 1
    This is brilliant! Works like a charm! – katspaugh Aug 03 '18 at 14:17
7

It will only work in console log of netflix if you want it to work in chrome extension then you need to inject this code in a script tag to make this work.

const videoPlayer = netflix.appContext.state.playerApp.getAPI().videoPlayer;
const player = videoPlayer.getVideoPlayerBySessionId(videoPlayer.getAllPlayerSessionIds()[0]);

player.seek(1091243) //seek to roughly 18mins

player.getCurrentTime() // will give you the current video time.

enter image description here

Danish
  • 325
  • 1
  • 4
  • 12
3

Finally found a simple solution:

netflix.cadmium.UiEvents.events.resize[1].scope.events.dragend[1].handler(null, {value: 999, pointerEventData: {playing: false}});

You can set:

  • position using value property
  • playing state using pointerEventData.playing property

It's not complete solution, but can be useful.

Previous version of netflix player was with global object window.netflix.cadmium.objects.videoPlayer. In the recent version it's empty, but you can access this object within events listeners:

  1. Open Chrome Developer Tools
  2. Select "body" element
  3. Select "Event Listeners"
  4. Turn on "Framework listeners"
  5. Select "keydown" event, "body" - handler p(e), "[[Scopes]]", 1 [[Closure]]
  6. You can save this object reference with context menu - "Store as a global variable"
  7. Then in new global variable you can get access to temp1.cadmium.objects.videoPlayer

    temp1.cadmium.objects.videoPlayer().getDuration() temp1.cadmium.objects.videoPlayer().seek(2283839); temp1.cadmium.objects.videoPlayer().seek(4283839);

[![enter image description here][1]][1]

I am not sure is it possible to do fully automatic. You can get access to this listeners by

getEventListeners(document.getElementsByTagName("body")[0]).keydown[0].listener

But I don't know how to get access to scopes variables

kb0
  • 1,143
  • 1
  • 8
  • 13
  • Seems promising but please give code samples for all this. Thanks! – HerrimanCoder Feb 13 '17 at 12:19
  • 1
    kb0, this works great inside Chrome Dev Tools (console), but when I execute the same line of code inside a Chrome Extension, I always get the same error: `netflix is not defined`. I have stepped into the code with a breakpoint and confirmed that I'm executing the exact same line as I'm doing in the console, and same as you pasted above. Why would it work in the console but not the Chrome extension? (In my extension I'm already interacting with other DOM elements. For example, this works fine, and gets the video object: `document.evaluate('//*[@id="60003082"]/video',document).iterateNext();`) – HerrimanCoder Feb 14 '17 at 14:14
  • 2
    @SweatCoder, It doesn't work in chrome extension due to restrictions for content scripts (https://developer.chrome.com/extensions/content_scripts): "However, content scripts have some limitations. They cannot ... Use variables or functions defined by web pages or by other content scripts ...". You can try to use workarounds in http://stackoverflow.com/questions/12395722/can-the-window-object-be-modified-from-a-chrome-extension – kb0 Feb 14 '17 at 14:31
  • could you point me in a direction that might enable me to do this in an extension? Since I can get a handle to DOM elements it seems like there should be some other way to do this. – HerrimanCoder Feb 14 '17 at 16:57
  • Inject js as script object. var elt = document.createElement("script"); elt.innerHTML = "netflix.cadmium.UiEvents..." document.head.appendChild(elt); – kb0 Feb 14 '17 at 18:06
  • 4
    As of September 2017, this is no longer working: cadmium is not referenced in the netflix object. – Fethi Dilmi Oct 10 '17 at 14:23
3

Building on Dmitry's response jump ahead 10 seconds by

player.seek(player.getCurrentTime() - 10000)

MarkedPath
  • 41
  • 3
0

For those trying to control Netflix's video player from their extensions: I have found a simple solution.

Current time and pause status: can be easily obtained using

document.querySelector('video').currentTime
document.querySelector('video').paused

(Simply accessing this data doesn't redirect to the error page)

Resume/Pause: Here we have two options:

  1. Simply execute

     document.querySelector('video').click();
    

this pauses/unpaused the video. You can use paused status fetching described above to make sure that you don't unpause video that's already paused.

  1. Add script, which itself adds elements to the document with click event listeners:

         const actualCode = `
         const pauseElem = document.createElement('div');
         pauseElem.id = 'my_pause_elem';
         pauseElem.addEventListener('click', () => {
             const videoPlayer = window.netflix.appContext.state.playerApp.getAPI().videoPlayer;
             const player = videoPlayer.getVideoPlayerBySessionId(videoPlayer.getAllPlayerSessionIds()[0]);
             player.pause();
         });
    
         const resumeElem = document.createElement('div');
         resumeElem.id = 'my_resume_elem';
         resumeElem.addEventListener('click', () => {
             const videoPlayer = window.netflix.appContext.state.playerApp.getAPI().videoPlayer;
             const player = videoPlayer.getVideoPlayerBySessionId(videoPlayer.getAllPlayerSessionIds()[0]);
             player.play();
         });
    
    
         document.body.appendChild(pauseElem);
         document.body.appendChild(resumeElem);
         `;
    
         const script = document.createElement('script');
         script.textContent = actualCode;
         (document.head||document.documentElement).appendChild(script);
         script.remove();    
    

Now all you need to do is

document.getElementById('my_pause_elem').click();
document.getElementById('my_resume_elem').click();

to pause/unpause.

Seek: This has the same solution as above: add an element with an event listener, which uses spotify API to seek. Special thing here is that we need to pass a timestamp. This can be done by setting an attribute value to our element:

    const actualCode = `
    const seekElem = document.createElement('div');
    seekElem.id = "my_seek_elem";
    seekElem.addEventListener('click', () => {
        const newTime = Number(document.getElementById('my_seek_elem').getAttribute('timestamp')) * 1000;
        const videoPlayer = window.netflix.appContext.state.playerApp.getAPI().videoPlayer;
        const player = videoPlayer.getVideoPlayerBySessionId(videoPlayer.getAllPlayerSessionIds()[0]);
        player.seek(newTime);
    });
    (document.body).appendChild(seekElem);
    `;

 const script = document.createElement('script');
 script.textContent = actualCode;
 (document.head||document.documentElement).appendChild(script);
 script.remove();   

Now all we need to do is trigger this event after setting the timestamp:

document.getElementById('my_seek_elem').setAttribute('timestamp', '' + timestamp);
document.getElementById('my_seek_elem').click();

That's all!

lkiti
  • 11
  • 2