Playing YouTube videos from an array

I don’t seem to get that error. You will need to supply detailed instructions on what I must do so that I can see that same error too.

1 Like

I agree.

Let’s fix that issue on here first:
https://jsitor.com/SEWXsfQOf7

I’ve noticed this quite frequently but never thought of it as anything.

Uncaught TypeError: player.playVideo is not a function at line 72 col 12

Keep tapping run then play until you see the error.

That happens because it takes time for the youtube player to load and be ready.

Do you agree that we should disable clicking the play button, until the player is loaded and ready?

1 Like

It was never added to a single player before, you can try adding it to it.

There’s a lot to be done here because we are returning some fundamentals back to the code, such as init methods.

To help ensure that timing works right, we’ll use a videoPlayer init method to load the scripts and setup the onYouTubeIframeAPIReady handler.

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

    function onYouTubeIframeAPIReady() {
        const cover = document.querySelector(".jacket");
        const wrapper = cover.parentElement;
        const frameContainer = wrapper.querySelector(".video");
        videoPlayer.addPlayer(frameContainer);
    }
...
    function init() {
        loadIframeScript();
        window.onYouTubeIframeAPIReady = onYouTubeIframeAPIReady;
    }

    return {
        addPlayer,
        init,
        play
    };
...
videoPlayer.init();

The manageCover code at the top needs an init method, so that it can be delayed until after the player is ready.

const manageCover = (function makeManageCover() {
    const events = {};
    ...
    function coverClickHandler(evt) {
        const cover = evt.currentTarget;
        ...
        cover.dispatchEvent(events.afterClickCover);
    }

    function init(callback) {
        const cover = document.querySelector(".jacket");
        cover.addEventListener("click", coverClickHandler);
        events.afterClickCover = new Event("afterClickCover");
        cover.addEventListener("afterClickCover", callback);
    }

    return {
        init
    };
}());

In fact, that init method can accept a callback function, so that we can run that callback function when initialising the code.

(function manageCover() {
    ...
    function coverClickHandler(evt) {
        ...
        callback(cover);
    }
    ...
    function init(callback) {
        const cover = document.querySelector(".jacket");
        cover.addEventListener("click", coverClickHandler);
    }
...
videoPlayer.init();
manageCover.init(function playVideo() {
    videoPlayer.play();
});

And, the videoPlayer function can take an eventHandlers object, so that we can delay that manageCover code until after the player is ready.

const videoPlayer = (function makeVideoPlayer() {
    const events = {};
    const eventHandlers = {};
...
    function addEvents(handlers) {
        eventHandlers.afterPlayerReady = handlers.afterPlayerReady;
        events.afterPlayerReady = new Event("afterPlayerReady");
    }
...
    function init(initEventHandlers) {
        addEvents(initEventHandlers);

Then we just need to add that afterPlayerReady event to an element, such as the iframe, and dispatch the event when after the onPlayerReady function.

    function onPlayerReady(event) {
        ...
        const iframe = player.h;
        iframe.dispatchEvent("afterPlayerReady");
    }
...
        player = new YT.Player(video, config);
        const iframe = player.h;
        const eventHandler = eventHandlers.afterPlayerReady;
        iframe.addEventListener("afterPlayerReady", eventHandler);

The code at https://jsitor.com/SEWXsfQOf7 has been updated, and clicking on the cover now doesn’t trigger it until after the player is ready to go.

1 Like

Thank you for that simple example where the code is easily movable.

I agree with you now, Math.random() should not be used where there is a much better alternative Playlist shuffle.

Playlist shuffle does the same thing, and uses way less code, and it is able to use setInterval which I did not know could be done.

Math here, where it picks a single video and plays only one, that’s really the only thing it is useful for.

const videoID = ["0dgNc5S8cLI", "2VwsvrPFr9w"];
const index = Math.floor(Math.random() * videoID.length);

function addPlayer(videos) {

    const config = {
            height: 360,
            host: "https://www.youtube-nocookie.com",
            videoId: videoID[index],

The array that plays only one video, it wouldn’t be able to work in this, would it?

function onYouTubeIframeAPIReady() {
    ...
    const videos = [
        "0dgNc5S8cLI",
        "mnfmQe8Mv1g",
        "CHahce95B1g",
        "2VwsvrPFr9w"
    ];
    videoPlayer.addPlayer(frameContainer, videos.join());
}

You have a habit of removing code that isn’t used in a certain circumstance, but is still used in the wider picture by other things you are are actively working on. That kind of chaos is not conducive to much progress.

1 Like

What would be helpful here is if the addPlayer function was smart enough to handle both a single video id, as well as an array of them.

Carrying on with the code from https://jsitor.com/SEWXsfQOf7 we do not use join() when passing the list to addPlayer. Using join() there was a bad choice. It should be done inside of addPlayer so that it’s only the videos (or single video) being given to addPlayer.

We can do that by checking if the videoIds are an array or not. If they aren’t an array then we want the value to be given to videoId. If they are an array though then we want them joined together and given to playlist.

    function addPlayer(video, videoIds) {
        const videoId = !Array.isArray(videoIds) && videoIds;
        const playlist = Array.isArray(videoIds) && videoIds.join();
        const playerOptions = {
            ...
            videoId,
            ...
        };
        playerOptions.playerVars = {
            ...
            playlist: playlist || undefined,
            ...
        };

That’s how easy it is to ensure that addPlayer can handle both a single video, or a list of videos.

I’ve put both a single video and multiple videos in the example code:

        const video = "mnfmQe8Mv1g";
        const videos = [
            "0dgNc5S8cLI",
            "mnfmQe8Mv1g",
            "-Xgi_way56U",
            "CHahce95B1g"
        ];
        videoPlayer.addPlayer(frameContainer, videos);

That way you can use it as is shown above for multiple videos, or as follows for a single video.

        videoPlayer.addPlayer(frameContainer, video);

The code has been updated at https://jsitor.com/SEWXsfQOf7

1 Like

How can we have it pick 1 video out of the array and play it without it being a playlist?

Can that be done?

Pick one out beforehand and give only that 1 to addPlayer.

The YouTube player will pick one on its own and play it.

That can be done?

I don’t want to tell it what to play.

The YouTube player starts and plays 1 video from the given array, and after that one song, that is it.

The youtube player doesn’t know how to play one at random. It can take a playlist and shuffle it, but that’s all. If you want only a single one to be played at random, that’s on us to pick out that random song and tell youtube to play it.

This piece allows 1 video at random, and that’s all.

The only issue here is that it may not be compatible with playlist.

  const videoID = ["0dgNc5S8cLI", "2VwsvrPFr9w"];
    const index = Math.floor(Math.random() * videoID.length);

    function addPlayer (videos){

        const config = {
            height: 360,
            host: "https://www.youtube-nocookie.com",
            videoId:  videoID[index],
            width: 640
        };

Let me know when you want to know more about my solution to all of this.

I want to know your solution.