I am trying to use YouTube Javascript but I figured you're using the Iframe not the object/embed method for Javascript enabled browsers. Is there a way to embed the video with the option enablejsapi=1 to enable Javascript API?

Comments

RobW’s picture

I've added it to the patches in #1555276: Clean up theming function, Use recommended iframe player by default, without js. That patch does a lot more though, so let me know if you'd like it broken out.

shadysamir’s picture

Can't it be optional based on settings? The same way I choose AS3 or AS2 player, etc.?

I know there are valid reasons behind resorting to iframe and the whole device compatibility issues. But not all use cases are the same. Hence suggesting a settings option.

RobW’s picture

I don't think there's any reason not to use the iframe -- if you have reservations or use cases I'd be happy to discuss them on the other thread.

RobW’s picture

If you want the js API be optional on the settings page, it is in the other patch. The way I see it, if you're using the js API you're counting on JavaScript being turned on, and if js is on Media YouTube already uses the iframe player, just in a roundabout way. I could write a patch against current dev that only adds the js option, but I'd rather see people drop the cruft and use my patch in the other issue.

shadysamir’s picture

Status: Active » Postponed

My use case requires controlling YT player. There are 4 players in a cycle slideshow and I need to stop currently playing one when its slide gets out of display.

I will be testing with Iframe API and how it will work with media YouTube. Let's postpone this for now

RobW’s picture

Yeah, give the patch a try, should work out well. Please post any module-related problems you run into when using the js API, too, I'd be happy to (try to) help fix.

RobW’s picture

Status: Postponed » Closed (fixed)

Patch in #1555276: Clean up theming function, Use recommended iframe player by default, without js has been committed to 2.x, so this should be working for you now.

remaye’s picture

I'm pretty confused about how to make it work.
I'm using last 7.x-2.x-dev version (downloaded yesterday) and I checked "enable the javascript API"
The code generated around my video is :

<iframe class="media-youtube-player" id="media-youtube-t5xl0qry-ha" width="575" height="350" src="https://www.youtube.com/embed/T5Xl0Qry-hA?wmode=opaque&amp;enablejsapi=1&amp;playerapiid=media-youtube-t5xl0qry-ha&amp;rel=0&amp;showinfo=0&amp;autohide=0" frameborder="0" allowfullscreen="">.../...</iframe>

that looks pretty good as I have the "enablejsapi" and "playerapiid" set to the correct values except that "origin" parameter is not present (is it critical ?)

But I don't know how to just play or stop one of the videos present on my page.
I would be supposed to have a "onYouTubePlayerReady" called when players are ready, I created this function on document.ready() but it is not called at all.
And anyway I don't know how to get the "player" object to just apply "player.play()" function to.

Of course I red the Google API documentation but it was no use for me.
Please give me a clue...

Thanks for your time.

RobW’s picture

Looks like the origin query got lost in some patch revisions. Re-inserted in dev, should be working now.

The js API options help add markup for js API integration, but that's where it ends -- it doesn't do anything to help write functional code to use the API. Adding javascript to use the js API probably forever outside the scope of this module.

You'll need to dig into Drupal's js handling and the YouTube docs to get something working. Stack Overflow tends to have a lot of good answers on this type of thing.

remaye’s picture

Hi RobW, thank you for your feedback.

"origin" parameter is now ok (in last dev version)

And indeed I found in Stack Overflow what helped me to make YT API working with "media:youtube" : this is a piece of code that .... you wrote !
Although this is maybe not the focus of this module "to help write functional code to use the API", I think it's pretty useful to share it here because it seems YT API won't work with Drupal media:youtube unless we use this JS code.
And Youtube support page won't be useful because they assume the player is scriptly written in the page, while in our case, Drupal already wrote the players in the page and then, Youtube regular way of controlling player will not work.
Please correct me if I'm wrong...

So, here is the (very very) useful link : Youtube iframe API: how do I control a iframe player that's already in the HTML?
Thanks for it !

RobW’s picture

Haha, that's not me! I have next to no experience working with the js API. Ridiculous coincidence.

Thanks for the link. It would probably be good to mention as a "further reading" in new documentation. Linking your comment in #1689302: Get the read me up to date.

joelcollinsdc’s picture

Re #10, if anyone like me is reading this looking for the same answer, you don't need to do that. Look at https://developers.google.com/youtube/iframe_api_reference#Loading_a_Vid... for an example of how to hook into an existing iframe.

David_Rothstein’s picture

Status: Closed (fixed) » Active

Yeah, that Stack Overflow link seems pretty weird - I wouldn't recommend continuing to link there in README.txt.

I was able to get it to work using the standard iframe API instructions (mentioned by @joelcollinsdc above), but there are some aspects of those instructions that aren't obvious coming from Drupal. Most important, not only can't you put the example code inside a standard Drupal behavior, it seems like you can't even put it inside a standard Drupal (function ($) { ... })(jQuery) code block either. Your custom JavaScript file that adds this code needs to just run it directly.

After doing that, I got it to work like this:

// Load the IFrame Player API code asynchronously.
var tag = document.createElement('script');
tag.src = "//www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

// Initalize the player once the API is ready.
var player;
function onYouTubeIframeAPIReady() {
  // NOTE: REPLACE THIS WITH SOMETHING THAT FINDS THE ACTUAL
  // IFRAME ID (which will, in the end, look something like
  // "media-youtube-[videoid]").  The line of code below might help you
  // find it inside a file field on the page, but will only work correctly
  // if you can guarantee that the field is displayed only once on the
  // page, so you might need to replace it with something else.
  var playerId = jQuery('.field-YOUR-VIDEO-FIELD').find('iframe').attr('id');
  // Make the player refer to the existing <iframe> on the page.
  player = new YT.Player(playerId, {
    events: {
      // Add whatever events you need. Here is one example.
      'onStateChange': onPlayerStateChange
    }
  });
}

// YouTube API callback function.
function onPlayerStateChange(event) {
  // Do stuff.
}
RobW’s picture

Component: Code » Documentation
Category: support » task

I would love for someone to summarize #12 and #13 for the documentation. Aside from that, is there any code change that needs to happen to close this issue?

joelcollinsdc’s picture

I am not sure. Its kind of a chicken / egg problem, does your rotator need to know about youtube players, or does youtube need to know about rotators? I think the solution is just documenting the right places to add customizations.

shadysamir’s picture

Status: Active » Closed (fixed)

The issue was about using IFrame API with the module, which is pretty much closed as far as I'm concerned. Anything else I guess is beyond the module maintainers scope.

joelcollinsdc’s picture

Status: Closed (fixed) » Active

updating documentation on the project page is outside of the module maintainer's scope? (see #13)

shadysamir’s picture

Oh! My bad. I didnt notice the category changes.

Microserve’s picture

I spent ages playing with this before I stumbled upon David Rothstein's comment (#13). Even a link to that comment from the homepage in reference to using the YouTube API would probably save everyone a great deal of time. Thank you!

rickdonohoe’s picture

I'm having an issue with the Controls checkbox and David_Rothstein's comment.

I have been using this code (without playerVars) and it works fine:

       var playerId = jQuery('.media-youtube-video').find('iframe').attr('id');
	player = new YT.Player(playerId, {
	playerVars : {
		'autoplay': 1,
		'controls': 0		
	},
	 events: {
	   'onStateChange': onPlayerStateChange
	 }
	});

I have then added playerVars and nothing changes. I'm only trying to hide controls so users are forced to watch the video without skipping, however I then added autoplay as a test and figured that nothing within playerVars is working.

Any suggestions why?

cameron prince’s picture

Issue summary: View changes

I tried the code in #13 and can't get it to work. I added console.log statements which do confirm that onYouTubeIframeAPIReady() is being called and var playerId is being properly populated. The issue is that onPlayerStateChange() is never called. When I dump the player var to console.log I see the player object.

I have tried with and without the origin parameter defined as I've read that others have had problems with that.

Anyone have any ideas?

loopy1492’s picture

This is as close as I've gotten to being able to wrap the code in the proper (function($){}(jQuery));

The code actually works. The video embeds itself and the loadVideoByID works as well. It sure would be nice if it used a class instead of an ID, though.

I have a thread here as well: https://www.drupal.org/node/2298983

/**
 * @file
 */
 
// ############ I am unable to wrap the first part of the javascript in the (function($){}) ############
// This code loads the IFrame Player API code asynchronously.
var tag = document.createElement('script');
tag.src = "http://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
// This function creates an <iframe> (and YouTube player) after the API code downloads.
var myvidid="";
//  initialize the player when the API becomes available to the page
var player;
function onYouTubeIframeAPIReady() {
  player = new YT.Player('main-player', {
      height: '315',
      width: '560',
      videoId: myvidid,
      playerVars: {
              'rel': 0,
  wmode: "opaque"
  },
      events: {
          'onReady': onPlayerReady,
          'onStateChange': onPlayerStateChange
      }
  });
}
// The API will call this function when the video player is ready.
function onPlayerReady(event) {
    //event.target.playVideo();
}
//  The API calls this function when the player's state changes.
//  The function indicates that when playing a video (state=1),
var done = false;
function onPlayerStateChange(event) {
    if (event.data == YT.PlayerState.PLAYING && !done) {
        setTimeout(stopVideo, 35000);
        done = true;
    }
}
// stop the video
function stopVideo() {
    player.stopVideo();
}
// #############################  end naughty drupal coding behavior ###################################

(function ($) {
  "use strict";
  // when the page is ready, get the ID of the initial video story
  // this must be wrapped in the jquery function for Drupal even in a module, it seems
  $(document).ready(function(){
    myvidid = $.trim($('#main-player').html());
    // click a video thumbnail to change videos
    // clicks in drupal must be wrapped in the .ready function... odd.
    $('.video-thumbnail-gallery, .video-title-gallery').click(function(){
      //alert('hello from click');
      var this_video_id = $.trim($(this).parents('.views-row').find('.video-id-gallery').html());
      player.loadVideoById(this_video_id);    
      $('.main-player .view-content .video-description').html($(this).parents('.views-row').find('.video-description-gallery').html());
      $('.main-player .view-content .video-title').html($(this).parents('.views-row').find('.video-title-gallery').html());
      $('.main-player .view-content .video-date').html($(this).parents('.views-row').find('.video-date-gallery').html());
      var theDownloadLink = $.trim($(this).parents('.views-row').find('.download-link-gallery a').attr('href'));
      if (theDownloadLink!=""){
          $('.main-player .view-content .download-link').html($(this).parents('.views-row').find('.download-link-gallery').html());
      }else{
          $('.main-player .view-content .download-link').html('<p class="download-link></p>"');
      }  
    });
    var theDownloadLink = $.trim($('.main-player .view-content .download-link a').attr('href'));
    if (theDownloadLink==""){
        $('.main-player .view-content .download-link').html('<p class="download-link></p>"');
    }
  });
}(jQuery));
jQuery.noConflict();

  • RobW committed 0b07a28 on 7.x-3.x
    Issue #1667660 (no patch) by RobW: Add missing origin handling.
    
alexverb’s picture

@loopy1492: It is indeed not possible to bind it in jQuery's document ready callback. As described here: http://stackoverflow.com/questions/17753525/onyoutubeiframeapiready-insi.... Using this approach I came up with the following usage of Drupal behaviors and functions. This javascript allows for me to stop a carousel when a video starts playing and to start the carousel again when it ends.

(function($) {

  // create deferred object
  var YTdeferred = $.Deferred();
  window.onYouTubeIframeAPIReady = function() {
    console.log('API ready');
    // resolve when youtube callback is called
    // passing YT as a parameter
    YTdeferred.resolve(window.YT);
  };

  // embedding youtube iframe api
  // https://developers.google.com/youtube/iframe_api_reference#Getting_Started
  var tag = document.createElement('script');
  tag.src = "https://www.youtube.com/iframe_api";
  var firstScriptTag = document.getElementsByTagName('script')[0];
  firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

  /**
   * Attach behaviors.
   */
  Drupal.behaviors.owlCarouselAutoAttach = {
    attach: function (context, settings) {
      $('#slider-blocks [id^=media-youtube-]', context).once('media-youtube-slider', function() {
        var player;
        var player_id = $(this).attr('id');
        var owl = $(this).closest('.owl-carousel');
        // whenever youtube callback was called = deferred resolved
        // your custom function will be executed with YT as an argument
        YTdeferred.done(function(YT) {
          console.log('Player ready');
          // creating a player
          // https://developers.google.com/youtube/iframe_api_reference#Getting_Started
          player = new YT.Player(player_id, {});
          player.addEventListener("onReady", function() {
            OwlCarousel.onPlayerReady();
          });
          player.addEventListener("onStateChange", function(event) {
            OwlCarousel.onPlayerStateChange(event, player, owl);
          });;
        });
      });
    }
  };
  OwlCarousel = {};

  OwlCarousel.onPlayerReady = function() {
    console.log('Player ready');
  }

  OwlCarousel.onPlayerStateChange = function(event, player, owl, pause_evt_stack) {
    // When video plays, stop the carousel.
    if ($.inArray(event.data, [1, 3]) != -1) {
      owl.trigger('stop.autoplay.owl');
    }
    // When video ends, start the carousel.
    else if ($.inArray(event.data, [0]) != -1) {
      owl.trigger('play.autoplay.owl', [8000]);
    }
  }

})(jQuery);