TypeError: player.b is undefined. How do I fix that?

This is coming up in the web console.

Code:

    function onPlayerStateChange(event) {
    const player = event.target;
    const playerVars = player.b.b.playerVars;
    if (playerVars.loop && event.data === YT.PlayerState.ENDED) {
      player.seekTo(playerVars.start);
    }
  }

What is the exact wording of the error?

This is coming up in the web console.

What documentation tells you that the player has a b property?

Question.

Should it be:

player.g.g instead?

const playerVars = player.g.g.playerVars;

After I did that the error is no longer coming up.

Please answer my question. What documentation are you following in regard to accessing player properties?

I didn’t figure that part of it out, you may have.

What documentation would say to use:

player.g.g instead

I am trying to figure that out.

Sorry if this isn’t helpful.

Looking though some of the code, it just seems overly complicated to me. It takes a bit of deciphering to figure out what is going on.

For instance the load object

const load = (function() {
  "use strict";

  function _load(tag) {
    return function(url) {
      return new Promise(function(resolve) {
        const element = document.createElement(tag);
        const parent = "body";
        const attr = "src";
        element.onload = function() {
          resolve(url);
        };
        element[attr] = url;
        document[parent].appendChild(element);
      });
    };
  }
  return {
    js: _load("script")
  };
}());

Breaking it down we are making use of a module reveal pattern and the private function _load is using currying to return an inner function with a tag property held in a closure. I don’t know, but is this worthy of currying?

Furthermore do parent and attr need to be defined as constants

Just experimenting — not tested!!

First with partial application — Could js just be a function that calls _load instead?

const load = (function(doc){

    const _load = (tag, url) => {

        return new Promise( resolve => {
            const element = doc.createElement(tag);
            element.onload = () => resolve(url)
            element.src = url;
            doc.body.appendChild(element);
        });
    }

    return {
        js: _load.bind(null, 'script')
    }

}(window.document))

or would it be so bad to just make load a function

const load = (tag, url) => {
    return new Promise( resolve => {
        const element = document.createElement(tag);

        element.onload = () => resolve(url)
        element.src = url;
        document.body.appendChild(element);
    });
}

Maybe I am missing the point — re-usability perhaps — but it does seem to be unnecessarily complicated/obfusicated.

Lastly would it be helpful to stick a console.dir(player) in under ‘const player = event.target’

p.s. b.b tells you a lot :neutral_face:

I was told this:

I debugged your code and found playerVars inside a g object which was in turn inside a ‘g’ object.

If that’s the case, what’s the definition of a g object?

or, a better question would be:

What determines whether it is b.b, or g.g, or c.c, or r.r?

That would better help me to understand how that works.

What am I supposed to be looking at that would give me that answer?

I was just given this instruction, but I don’t understand.

In your onPlayerStateChange . Insert console.log(JSON.stringify(event)) as the first line. Copy the JSON string into an JSON formatter online and see the structure of event object. This might help in getting an idea about the event object

I’ve never used a JSON formatter before and so I would not know how to use it.

I did that here, then what?

    function onPlayerStateChange(event) {
    console.log(JSON.stringify(event))
    const player = event.target;
    const playerVars = player.b.b.playerVars;
    if (playerVars.loop && event.data === YT.PlayerState.ENDED) {
      player.seekTo(playerVars.start);
    }
  }

Are those that same playerVars that you’ve defined elsewhere in the code as this?

      playerVars: {
        autoplay: 1,
        controls: 1,
        showinfo: 1,
        rel: 0,
        iv_load_policy: 3,
        cc_load_policy: 0,
        fs: 0,
        disablekb: 1,
        loop: true,
        start: 200,
        end: 204
      },

It’s always better to avoid using undocumented features, because there’s no assurance that those undocumented features will remain, and your code becomes brittle, and breaks as soon as those undocumented features change.

A much more reliable way is to place those playerVars in a local variable, at the top of the videoPlayer object, and refer to that instead.

const videoPlayer = (function makeVideoPlayer() {
  "use strict";

  const playerVars = {
    autoplay: 1,
    controls: 1,
    showinfo: 1,
    rel: 0,
    iv_load_policy: 3,
    cc_load_policy: 0,
    fs: 0,
    disablekb: 1,
    loop: true,
    start: 200,
    end: 204
  };
...
    // const playerVars = player.b.b.playerVars;
    if (playerVars.loop && event.data === YT.PlayerState.ENDED) {
      player.seekTo(playerVars.start);
...
    new YT.Player(video, {
      width: 606,
      height: 344,
      videoId: video.dataset.id,
      host: "https://www.youtube-nocookie.com",
      // playerVars: {
      //   ...
      // },
      playerVars,

That way, when the internal workings of the player API changes from g.g.playerVars to h.h.playerVars or anything else, your code won’t break and will keep on working with no troubles.

Keep to the documented usage of the player API, don’t use undocumented features, and your code will be so much the better for it.

2 Likes

This is the working code.

What did I do wrong here?

const videoPlayer = (function makeVideoPlayer() {
  "use strict";

  function onPlayerReady(event) {
    const player = event.target;
    player.setVolume(50); // percent
  }
  
   const playerVars = {
    autoplay: 1,
    controls: 1,
    showinfo: 1,
    rel: 0,
    iv_load_policy: 3,
    cc_load_policy: 0,
    fs: 0,
    disablekb: 1,
    loop: true,
    start: 200,
    end: 204
  };
  
      if (playerVars.loop && event.data === YT.PlayerState.ENDED) {
      player.seekTo(playerVars.start);
  
     new YT.Player(video, {
      width: 606,
      height: 344,
      videoId: video.dataset.id,
      host: "https://www.youtube-nocookie.com",
      // playerVars: {
      //   ...
      // },
      playerVars,

Running the code through beautifier.io I find that the new YT.Player hasn’t been closed. You have also accidentally deleted the events section that was at the end of the YT.Player code too.

What needs to be fixed here?

const videoPlayer = (function makeVideoPlayer() {
   "use strict";

   function onPlayerReady(event) {
      const player = event.target;
      player.setVolume(50); // percent
   }

   const playerVars = {
         autoplay: 1,
         controls: 1,
         showinfo: 1,
         rel: 0,
         iv_load_policy: 3,
         cc_load_policy: 0,
         fs: 0,
         disablekb: 1,
         loop: true,
         start: 200,
         end: 204
      },
      events: {
         "onReady": onPlayerReady
      }
});

if (playerVars.loop && event.data === YT.PlayerState.ENDED) {
   player.seekTo(playerVars.start);

   new YT.Player(video, {
         width: 606,
         height: 344,
         videoId: video.dataset.id,
         host: "https://www.youtube-nocookie.com",
         // playerVars: {
         //   ...
         // },
         playerVars,
      };

You are still missing the events section.

Like that?

const videoPlayer = (function makeVideoPlayer() {
   "use strict";

   function onPlayerReady(event) {
      const player = event.target;
      player.setVolume(50); // percent
   }

   const playerVars = {
         autoplay: 1,
         controls: 1,
         showinfo: 1,
         rel: 0,
         iv_load_policy: 3,
         cc_load_policy: 0,
         fs: 0,
         disablekb: 1,
         loop: true,
         start: 200,
         end: 204
      },
      events: {
        "onReady": onPlayerReady,
        "onStateChange": onPlayerStateChange
      }
    });
  
  
function onPlayerStateChange(event) {
if (playerVars.loop && event.data === YT.PlayerState.ENDED) {
   player.seekTo(playerVars.start);
  }
  }

No not like that. Where it used to be at the end of the YT.Player code.

Like this?

   new YT.Player(video, {
         width: 606,
         height: 344,
         videoId: video.dataset.id,
         host: "https://www.youtube-nocookie.com",
         // playerVars: {
         //   ...
         // },
         playerVars,
      };     
         events: {
        "onReady": onPlayerReady,
        "onStateChange": onPlayerStateChange
      }

I’m starting to think unkind things about your abilities.

That }; needs to be below the events object.

1 Like

I did something wrong here.

TypeError: event is undefined

I think I almost got it.

const videoPlayer = (function makeVideoPlayer() {
   "use strict";

   function onPlayerReady(event) {
      const player = event.target;
      player.setVolume(50); // percent
   }


   const playerVars = {
      autoplay: 1,
      controls: 1,
      showinfo: 1,
      rel: 0,
      iv_load_policy: 3,
      cc_load_policy: 0,
      fs: 0,
      disablekb: 1,
      loop: true,
      start: 200,
      end: 204
   };

   if (playerVars.loop && event.data === YT.PlayerState.ENDED) {
      player.seekTo(playerVars.start);


      new YT.Player(video, {
         width: 606,
         height: 344,
         videoId: video.dataset.id,
         host: "https://www.youtube-nocookie.com",
         // playerVars: {
         //   ...
         // },
         playerVars,

         events: {
            "onReady": onPlayerReady
         }
      });
   }

I remember that you used to have two different types of events in the events object. I’m only seeing that you have one there currently.