Preventing the browser from reading the YouTube code until the image was clicked

combinedPlayerVars,playerVars

This:

playerVars:combinedPlayerVars,

1 Like

Consider this key/value pair:

width: 200,

width is the key, and 200 is the value,

What you are needing is to use playerVars as the key, and combinedPlayerVars as the value.

Correct.

Here:

combinedPlayerVars,playerVars

This is the next step:

You can then refactor the code to improve it. When both the key and the variable name are the same, we can remove the key from the object, resulting in cleaner code. To achieve that we want combinedPlayerVars to instead be called playerVars, but that can’t occur while something else is called playerVars, which is the function parameter in this case.

I must now leave. We can carry on with this next time.

    const combinedPlayerVars = Object.assign(defaultPlayerVars, playerVars);
    new YT.Player(video, {
      playerVars:combinedPlayerVars,
      width: 200,
      height: 200,
      videoId: videoId,
      // defaultPlayerVars,
      combinedPlayerVars,
      events: {
        "onReady": onPlayerReady
      }
    });
  }

ok. Thank you for your help.

1 Like

I tried following your instructions, I think I was able to do it.
https://jsfiddle.net/d72Lp43v/124/

 function addVideo(video, desiredPlayerVars) {
    const videoId = video.getAttribute("data-id");
    const defaultPlayerVars = {
      autoplay: 1,
      controls: 1,
      showinfo: 1,
      rel: 0,
      iv_load_policy: 3,
      cc_load_policy: 0,
      fs: 0,
      disablekb: 1
    };
    const playerVars  = Object.assign(defaultPlayerVars, desiredPlayerVars );
    new YT.Player(video, {
      width: 200,
      height: 200,
      videoId: videoId,
      // defaultPlayerVars,
      playerVars ,
      events: {
        "onReady": onPlayerReady
      }
    });
  }
1 Like

Nicely done!

1 Like

Would I have been able to get start to seconds to work?
It doesn’t restart, it just shuts off after 2 seconds.

It should restart.

I tried to see if it would work.

This part works:

  const startSeconds = 5;
  const endSeconds = 7;

It’s this part that’s having trouble.

  function onPlayerStateChange(event) {
    const player = event.target;
    if (event.data === YT.PlayerState.ENDED) {
      player.seekTo(startSeconds);
    }
  }

(function iife() {
  "use strict";
  const show = (el) => el.classList.remove("hide");
  const startSeconds = 5;
  const endSeconds = 7;

  function onPlayerStateChange(event) {
    const player = event.target;
    if (event.data === YT.PlayerState.ENDED) {
      player.seekTo(startSeconds);
    }
  }

  function coverClickHandler(evt) {
    const wrapper = evt.currentTarget.nextElementSibling;
    show(wrapper);
    videoPlayer.init({
      video: wrapper.querySelector(".video"),
      playerVars: {
        start: startSeconds,
        end: endSeconds
      },
      events: {
        "onStateChange": onPlayerStateChange
      }
    });
  }
  const cover = document.querySelector(".playa");
  cover.addEventListener("click", coverClickHandler);
}());

What I’m trying to do is get (onPlayerStateChange) to work with a video.
I tried doing this but It’s not registering. Adding an event on doesn’t seem to be doing anything.

The only part that works is this:

    const startSeconds = 5;
    const endSeconds = 7;

This part isn’t working:

    function onPlayerStateChange(event) {
        const player = event.target;
        if (event.data === YT.PlayerState.ENDED) {
            player.seekTo(startSeconds);
        }
    }

Currently after 2 seconds it gets to the spot and just stops, it doesn’t restart.

Altogether

(function iife() {
    "use strict";
    const show = (el) => el.classList.remove("hide");
    const startSeconds = 5;
    const endSeconds = 7;

    function onPlayerStateChange(event) {
        const player = event.target;
        if (event.data === YT.PlayerState.ENDED) {
            player.seekTo(startSeconds);
        }
    }

    function coverClickHandler(evt) {
        const wrapper = evt.currentTarget.nextElementSibling;
        show(wrapper);
        videoPlayer.init({
            video: wrapper.querySelector(".video"),
            playerVars: {
                start: startSeconds,
                end: endSeconds
            },
            events: {
                "onStateChange": onPlayerStateChange
            }
        });
    }
    const cover = document.querySelector(".playa");
    cover.addEventListener("click", coverClickHandler);
}());

If this function is able to reach inside all the players.

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

Would this function be able to reach outside

(function iife() {
  "use strict";

  function onPlayerStateChange(event) {
    const player = event.target;
    if (event.data === YT.PlayerState.ENDED) {
      player.seekTo(startSeconds);
    }
  }
}());

And make contact with this?
new YT.Player(video, {

Which is what I think it’s trying to do.

But because it’s / (onPlayerStateChange) is inside a separate function, it’s not able to.

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

    function loadPlayer() {
        const tag = document.createElement("script");
        tag.src = "https://www.youtube.com/player_api";
        const firstScriptTag = document.getElementsByTagName("script")[0];
        firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
    }

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

    function addVideo(video, desiredPlayerVars) {
        const videoId = video.getAttribute("data-id");
        const defaultPlayerVars = {
            autoplay: 1,
            controls: 1,
            showinfo: 1,
            rel: 0,
            iv_load_policy: 3,
            cc_load_policy: 0,
            fs: 0,
            disablekb: 1
        };
        const playerVars = Object.assign(defaultPlayerVars, desiredPlayerVars);
        new YT.Player(video, {
            width: 200,
            height: 200,
            videoId: videoId,
            // defaultPlayerVars,
            playerVars,
            events: {
                "onReady": onPlayerReady
            }
        });
    }
    let apiIsReady = false;
    let timer;
    window.onYouTubePlayerAPIReady = function() {
        apiIsReady = true;
    };
    // function init(video) {
    function init(opts) {
        loadPlayer();
        timer = setInterval(function checkAPIReady() {
            if (apiIsReady) {
                timer = clearInterval(timer);
                addVideo(opts.video, opts.playerVars || {});
            }
        }, 100);
    }
    return {
        init
    };
}());
(function iife() {
    "use strict";
    const show = (el) => el.classList.remove("hide");
    const startSeconds = 5;
    const endSeconds = 7;

    function onPlayerStateChange(event) {
        const player = event.target;
        if (event.data === YT.PlayerState.ENDED) {
            player.seekTo(startSeconds);
        }
    }

    function coverClickHandler(evt) {
        const wrapper = evt.currentTarget.nextElementSibling;
        show(wrapper);
        videoPlayer.init({
            video: wrapper.querySelector(".video"),
            playerVars: {
                start: startSeconds,
                end: endSeconds
            },
            events: {
                "onStateChange": onPlayerStateChange
            }
        });
    }
    const cover = document.querySelector(".playa");
    cover.addEventListener("click", coverClickHandler);
}());

I was thinking maybe something like this:
But these are all just guesses on my part.

In the end, it might not be possible to set events individually for each player.

If I’m able to set playerVars individually for each player, wouldn’t it work the same way with events too, or no? Or if it would, it would just be done a different way.

  const defaultPlayerEvt = {
           events: {
                "onReady": onPlayerReady
            }

Then below in here it would be able to work somehow


    const startSeconds = 5;
    const endSeconds = 7;

    function onPlayerStateChange(event) {
        const player = event.target;
        if (event.data === YT.PlayerState.ENDED) {
            player.seekTo(startSeconds);
        }
    }

    function coverClickHandler(evt) {
        const wrapper = evt.currentTarget.nextElementSibling;
        show(wrapper);
        videoPlayer.init({
            video: wrapper.querySelector(".video"),
            playerVars: {
                start: startSeconds,
                end: endSeconds
            },
            events: {
                "onStateChange": onPlayerStateChange
            }
        });
    }

The video player code replaces existing default player vars with what you supply in the init function.

The events object had onPlayerReady in by default. When you put events into the init function, you destroy the old event information, replacing it with your newly supplied event information.

The player no longer has an onPlayerReady function because you destroyed it.

I’ll need to sleep on this problem for a while, but it most likely consists of supplying the onStateChange event as a separate init property, instead of being in the playerVars object.

1 Like

Then it would be set up something similar to this. But written differently.

(function iife() {
    "use strict";
    const show = (el) => el.classList.remove("hide");
    const startSeconds = 5;
    const endSeconds = 7;

    function onPlayerStateChange(event) {
        const player = event.target;
        if (event.data === YT.PlayerState.ENDED) {
            player.seekTo(startSeconds);
        }
    }

    function coverClickHandler(evt) {
        const wrapper = evt.currentTarget.nextElementSibling;
        show(wrapper);
        videoPlayer.init({
            video: wrapper.querySelector(".video"),
            playerVars: {
                start: startSeconds,
                end: endSeconds
            }
        });
    }

    function coverEvtClickHandler(evt) {
        const wrapper = evt.currentTarget.nextElementSibling;
        show(wrapper);
        videoPlayer.init({
            events: {
                "onStateChange": onPlayerStateChange
            }
        });
    }
    const cover = document.querySelector(".playa");
    cover.addEventListener("click", coverClickHandler);
}());

It’s important that you don’t force every video to loop when some of them aren’t supposed to loop.
There are some different options for how the code goes, depending on your intentions.

  • Do only a few of them need to loop? Then inject the loop code from outside of the init function.
  • Do many of them need to loop? Then have the init function setup the modification to loop.
  • Do all of them need to loop? Update onPlayerStateChange so that it’s incapable of doing anything else.
1 Like

I would want to pick which videos I would want to loop, but each one would have a different start and end time.

const startSeconds = 5;
const endSeconds = 7;

You could show me how each one of those scenarios would work.

Let’s do this so I can see how each would be written.

I don’t have the time to supply detailed working models of all possible variations.

Looping can wait for another time.

This is something I would like to be able to implement.

Can you show me how I can do this?

Will I be using something similar to this to loop all the players?
But all I’m doing is pausing.


    function pauseAllButtons() {
      const buttons = document.querySelectorAll(".playButton");
      buttons.forEach(function hidePause(button) {
        if (isPlaying(button)) {
          showPlayButton(button);
        }
      });
    }

    function showPauseButton(button) {
      const pause = getPause(button);
      pauseAllButtons();
      hideAllButtons(button);
      show(pause);
      button.classList.add("active");
    }

I just wanted to be able to pick which songs get the loop, and which wouldn’t. I hate when there are many options to pick from.

I think this would be the closest to that though:

Do many of them need to loop? Then have the init function setup the modification to loop.

I think what this would mean is setting up each init button individually to be able to use loop.
Whichever videos I would want loop to work with.

Well, onPlayerStateChange only receives an event object that doesn’t have information about looping or where to start and end, so that information needs to be supplied in some other way.

I recommend using a technique called closure where a maker function is called with the start/end information, which returns an onPlayerStageChange function. That way, the returned function retains knowledge of the start and end information even though it only has one normal function parameter called evt. That returned function can then be assigned as the onStateChange event, where it still retains knowledge about the start and end.

1 Like