Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pause Youtube embed when playing Soundcloud embed

I have a music blog that contains various embedded soundcloud and youtube players.

What I want to do is prevent any audio from playing simultaneously. In other words while I am playing a youtube video, if I click to play the soundcloud embed I want the youtube player to pause and vice versa.

I have developed code that pauses the streaming youtube player if I click to play another youtube player (soundcloud already does this inherently). I just need to make it cross compatible. Really would appreciate some help, thanks.

var playerCurrentlyPlaying = null;
var players = {}
YT_ready(function() {
   $(".youtube_embed").each(function() {
       var identifier = this.id;
       var frameID = getFrameID(identifier);
       if (frameID) {
            players[frameID] = new YT.Player(frameID, {
                events: {
                    "onStateChange": function(event){
                        if (event.data == YT.PlayerState.PLAYING)
                        {
                            if(playerCurrentlyPlaying != null &&
                               playerCurrentlyPlaying != frameID)
                            callPlayer( playerCurrentlyPlaying , 'pauseVideo' );
                            playerCurrentlyPlaying = frameID;
                        }
                    }
                }
            });
        }
   });
});

I have used functions from these sources: Listening for Youtube Event in JavaScript or jQuery

YouTube iframe API: how do I control a iframe player that's already in the HTML?

YouTube API Target (multiple) existing iframe(s)

Rendered HTML using UniqueID():

<span class="soundcloud_embed" id="soundcloud_post_312">
  <iframe id="ui-id-1" width="100%" height="166" scrolling="no" frameborder="no" src="https://w.soundcloud.com/player/?url=http%3A%2F%2Fapi.soundcloud.com%2Ftracks%2F103518792&show_artwork=true&secret_token=s-LnOTK"></iframe>
</span>

<span class="youtube_embed" id="youtube_post_309">
  <iframe id="ui-id-2" width="528" height="190" src="//www.youtube.com/embed/Y3CYKXBEtf0" frameborder="0" allowfullscreen></iframe>
</span>
like image 723
Jaqx Avatar asked Aug 08 '13 03:08

Jaqx


1 Answers

First of all, things will be greatly simplified if you are able to get an ID onto the iframes themselves, like this:

<span class="soundcloud_embed">
  <iframe width="100%" height="166" scrolling="no" frameborder="no" src="https://w.soundcloud.com/player/url=http%3A%2F%2Fapi.soundcloud.com%2Ftracks%2F4997623" id="sound1"></iframe>
</span>
<span class="soundcloud_embed">
  <iframe width="100%" height="166" scrolling="no" frameborder="no" src="https://w.soundcloud.com/player/url=http%3A%2F%2Fapi.soundcloud.com%2Ftracks%2F105153133" id="sound2"></iframe>
</span>
<span class="youtube_embed">
  <iframe width="400" height="225" src="//www.youtube.com/embed/tv8WgLEIPBg" id="vid1" frameborder="0" allowfullscreen></iframe>
</span>
<span class="youtube_embed">
  <iframe width="400" height="225" src="//www.youtube.com/embed/FmICU1gMAAw" id="vid2" frameborder="0" allowfullscreen></iframe>
</span>

If you'd prefer not to hack the auto_html gem that you mention in order to get IDs, you can either use jquery's .uniqueId() function to generate them on all iframes or use the getFrameID() helper method from the post you referred to (and seem to already be using).

To make it as easy as possible to get the two APIs (Youtube and Soundcloud) to be able to respond to each other's events, you can use a couple of control objects that keep track of which players are on your page and which of them is currently playing (this strategy is similar to that employed by the links you referred to, but are expanded to be able to keep track of which player belongs to which API). With it, define a generic pause function that serves as a simplistic wrapper for both APIs:

var playerCurrentlyPlaying = {"api":null,"frameID":null};
var players = {"yt":{},"sc":{}};

pauseCurrentPlayer=function() {
  var api=playerCurrentlyPlaying["api"],
      frameid=playerCurrentlyPlaying["frameID"];

  switch(api) {
    case "yt":
      players[api][frameid].pauseVideo();
      break;
    case "sc":
      players[api][frameid]["widget"].pause();
      break;
  }
};

Next, you'll want to define two separate functions; the first will be called whenever a YouTube play event is captured, and the second whenever a Soundcloud play event is captured. Currently the Soundcloud HTML5 Widget has a few bugs that force the necessity to include some pretty ugly hacks--namely, the Play event sometimes isn't fired when you play a Soundcloud sound for the first time. Luckily, the Play_Progress event is, so we can leverage that, but must also include a workaround so it doesn't create a race condition when pausing a sound and starting a video:

onYTPlay =function(frameid) {
  if (playerCurrentlyPlaying["frameID"]!=frameid && playerCurrentlyPlaying["frameID"]!=null) {
     pauseCurrentPlayer();
  }
  playerCurrentlyPlaying["api"]="yt";
  playerCurrentlyPlaying["frameID"]=frameid;
};

onSCPlay=function(frameid,event) {
  if (event==SC.Widget.Events.PLAY||players["sc"][frameid]["firstplay"]==true) {
     if (playerCurrentlyPlaying["api"]=="yt") { // because any soundcloud player will be automatically paused by another soundcloud event, we only have to worry if the currently active player is Youtube
        pauseCurrentPlayer();
     }
     playerCurrentlyPlaying["api"]="sc";
     playerCurrentlyPlaying["frameID"]=frameid;
     players["sc"][frameid]["firstplay"]=false;
  }
};

Finally, you can then add your hooks to the Youtube embeds and the Soundcloud embeds, remembering the hack we have to put in for the Soundcloud Play event. Don't forget to embed the Soundcloud Widget API (<script src="w.soundcloud.com/player/api.js"; type="text/javascript"></script>), and then use this code to do your bindings:

var tag = document.createElement('script');
tag.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

function onYouTubeIframeAPIReady() {
  $(".youtube_embed iframe").each(function() {
    players["yt"][$(this).attr('id')] = new YT.Player($(this).attr('id'), {
      events: { 'onStateChange': onYTPlayerStateChange }
    });
  });
}

onYTPlayerStateChange = function(event) {
  if (event.data==YT.PlayerState.PLAYING) {
     onYTPlay(event.target.a.id);
  }
};

(function(){
    $(".soundcloud_embed iframe").each(function() {
      var frameid=$(this).attr('id');
      players["sc"][frameid]={};
      players["sc"][frameid]={"widget":SC.Widget(document.getElementById(frameid)),"firstplay":true};
      players["sc"][frameid]["widget"].bind(SC.Widget.Events.READY, function() {
       players["sc"][frameid]["widget"].bind(SC.Widget.Events.PLAY, function() {
        onSCPlay(frameid,SC.Widget.Events.PLAY);
       });
       players["sc"][frameid]["widget"].bind(SC.Widget.Events.PLAY_PROGRESS, function() {
        onSCPlay(frameid,SC.Widget.Events.PLAY_PROGRESS);
       });
     });
    });
   }());

This approach is set up to handle multiple players of both APIs, and should be decently extensible if you want to support other embeddable media widgets (provided they expose an event when they start to play and they have the ability to programmatically pause players).

like image 101
jlmcdonald Avatar answered Nov 26 '22 15:11

jlmcdonald