#youtube player - correct API load procedure?

11 messages · Page 1 of 1 (latest)

high hare
#

hi everyone, just jumped on the Astro wagon some veeks a go, and have some experience with vanilla javascript and vue...
I'm not able to correctly load the http://www.youtube.com/player_api to get control of the YT player ,
I'm able to embed as an IFRAME, but I'm not able to get control of the player.
API loading works on other apps, vanilla js, vue, and tampermokey plugins.
I just cannot get it to work in Astro, and I don't understand what I'm doing wrong or understood wrong...

<script src = "http://www.youtube.com/player_api"></script>
apparently runs, but doensn't create the YT object...

vapid gale
#

Hey! So I don't know exactly what your issue is, but I do know there have been several threads before about embedded yt videos. Are you able to create a basic reproduction on stackblitz of what you're trying?

high hare
#

I have a vanilla JS app that I'd like to rebuild as an Astro project.
Normally, following Google documentation, you include the API script http://www.youtube.com/player_api in the document and it declares the objects that will be injected in a div in the document.

The embedding methods I found on astro seem just a shortcut to the embedding an IFRAME method... and that works... but what I want is to take control of the player and send him commands and listen to events...

Then I tried to embed myself that script, but it doesn't work.
No success also inserting the API file in the SCRIPT part of the astro file.
I think that what I'm messing up is related to the server rendering part, (and since this is a feature of Astro, I wonder if it could be that Astro is not the right framework to do these things...).

cinder inlet
#

Create a YouTube Player Component:

---
// src/components/YoutubePlayer.astro
const videoId = "your-video-id"; // Replace with your video ID
---

<div id="player"></div>

<script>
  let player;
  
  function onYouTubeIframeAPIReady() {
    player = new YT.Player('player', {
      height: '390',
      width: '640',
      videoId: '{videoId}',
      events: {
        'onReady': onPlayerReady,
        'onStateChange': onPlayerStateChange
      }
    });
  }

  function onPlayerReady(event) {
    event.target.playVideo();
  }

  function onPlayerStateChange(event) {
    // Handle player state changes here
  }

  if (!window.YT) {
    const tag = document.createElement('script');
    tag.src = "https://www.youtube.com/iframe_api";
    const firstScriptTag = document.getElementsByTagName('script')[0];
    firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

    window.onYouTubeIframeAPIReady = onYouTubeIframeAPIReady;
  } else {
    onYouTubeIframeAPIReady();
  }
</script>

Use the Component in Your Layout or Page

---
// src/pages/index.astro
import YoutubePlayer from '../components/YoutubePlayer.astro';
---

<html lang="en">
  <head>
    <title>My Astro Site</title>
  </head>
  <body>
    <h1>My YouTube Video</h1>
    <YoutubePlayer />
  </body>
</html>
high hare
#

that's what is suggested, and exactly what i did...
(and worked perfectly in vanilla JS)
problem is that I don't find the YT object defined, so "player = new YT.Player" returns an error...
It gets created by including the API file
http://www.youtube.com/player_api
but doens't seem to work.

What does work (what I was able to do) is only the inclusion via IFRAME tag... but that way I have no player control...

vapid gale
#

So I've made a stackblitz which doesn't work 😅 But it should work in a real project. Basically I have the YT Api working and correctly picking up the iframe so you can use the yt api fine. Just the video player doesn't connect, but I think that's an issue with running it in stackblitz!

https://stackblitz.com/edit/github-bl6zow?file=src%2Fcomponents%2FVideoPlayer.astro

So there's 3 things here to check:

  1. Make the script is:inline, I don't have time to dig to the bottom of why exactly a regular bundled <script> doesn't work, but I think it has to do with the yt api setting things on window which you can't get to from a module.
  2. You can create an iframe element and then hook the yt api onto it, which I think may be better than trying to generate the whole tag just with the api. https://developers.google.com/youtube/iframe_api_reference#Examples
  3. I've used the credentialless and enablejsapi attributes on the iframe so it should work. Again, it doesn't worki in stackblitz, but it seems like it should work in a real project!

Run official live example code for Astro Basics, created by Withastro on StackBlitz

chrome glacier
#

Instead of using a CDN you can use a NPM package like youtube-player: ```html
<script>
import YouTubePlayer from 'youtube-player';

const player = YouTubePlayer('player', {
...
})
</script>

high hare
#

seems that is:inline is the key!
I've been able somewhat to have a player displaying a video in the page.
I had to hardcode some values, but now that I see that it works, it's a matter of better understanding how the variable passing work... shouldn't be a big problem.

But I like the youtube-player import. I'll try also that, it's more 'elegant' !

Thank you for the hints guys, hope I can walk on my own now 👍

vapid gale
#

Yeah youtube-player is a good shout! Never heard of it but hopefully should make it easier

high hare
#

ok, this seems to work:


const {videoId} = Astro.props;

<div id="player"></div>
<script>
import player from "youtube-player";
</script>

<script is:inline define:vars={{ videoId }}>
...
...

with is:inline only on the second script tag.
(my next step is to learn to post colored code here on Discord haha! )

chrome glacier
#

I recommend using a pattern like this: https://docs.astro.build/en/guides/client-side-scripts/#pass-frontmatter-variables-to-scripts for passing frontmatter to your script tags, define:vars is generally not recommended because it will inline the script

---
const { videoId } = Astro.props;
---

<div id="player" data-videoid={videoID}></div>

<script>
  import YouTubePlayer from 'youtube-player';
  
  const container = document.querySelector("#player")
  
  const player = YouTubePlayer('player', {
    videoId: container.dataset.videoid,
    ...
  })
</script>