Making a Custom YouTube Video Player With YouTube’s APIs

Video presentations are a great addition to any product page. With a presentation you can showcase your product’s features without making the visitor read through long paragraphs of text. But apart from producing the video, you still need to manually convert it and find (or code) some sort of flash player that will display it on your site.

The other possible path is that you upload it to a video sharing site such as youtube, but you are going to have a rough time trying to incorporate the player into your design.

Luckily for us, YouTube does provide a solution to this problem – their chromeless player (a stripped down version of the regular embeddable player), which allow you to build and style your own custom controls. This way you have both a quick and secure way to include videos in your pages, and the freedom to customize any way you might want to.

The Idea

Today we are going to make a jQuery plugin which uses YouTube’s chromeless player, and creates our own set of minimalistic controls, which allows for perfect integration with your designs. The supported controls include a Play/Pause/Replay button, and a clickable progress bar.

The plugin is going to use YouTube’s gdata api to determine whether embedding has been allowed for the video, and fetch extensive information about it, such as title, description, tags, screenshots & more, which you can use to improve the plugin.

Using the plugin to embed videos is extremely easy:

// Embed a video into the #player div:
$('#player').youTubeEmbed('http://www.youtube.com/watch?v=u1zgFlCw8Aw');

// Chaining is also supported:
$('#player').youTubeEmbed('http://www.youtube.com/watch?v=u1zgFlCw8Aw');
		.youTubeEmbed('http://www.youtube.com/watch?v=AsdfFdwlzdAw');

You can also specify a width for the embedded video (the height will be calculated automatically depending on the aspect ratio), and choose to disable the progress bar:

$('#player').youTubeEmbed({
	video			: 'http://www.youtube.com/watch?v=u1zgFlCw8Aw',
	width			: 600, 		// Height is calculated automatically
	progressBar	: false		// Hide the progress bar
});

You can grab the plugin from the download button above, and start with the first step.

Step 1 – XHTML

Our plugin depends on jQuery SWFObject to embed the SWF files in the page. Below you can see the combined markup that is generated by both of the plugins.

youtube-player.html

<div class="flashContainer" style="width: 640px; height: 360px;">

	<object height="360" width="640" id="video_26ELpS3Wc4Q" type="application/x-shockwave-flash"
	data="http://www.youtube.com/apiplayer?enablejsapi=1&version=3">

		<param value="always" name="allowScriptAccess">
		<param value="transparent" name="wmode">
		<param value="video_id=26ELpS3Wc4Q&playerapiid=26ELpS3Wc4Q"
		name="flashvars">
		<param value="http://www.youtube.com/apiplayer?enablejsapi=1&version=3"
		name="movie">

	</object>

	<div class="controlDiv play"></div>

	<div class="progressBar">
		<div class="elapsed"></div>
	</div>
</div>

The .flashContainerDiv is dynamically created by the plugin for each video on the page. It is populated with the embed code generated by SWFObject, the .controlDiv (which acts as a play/pause button) and the progress bar.

As mentioned above, the insertion of the player itself is handled by the SWFObject plugin. Depending on the browser, it can output either an object element, or a non-standard embed element for IE. This lifts the burden from us and allows us to concentrate on tasks such as querying YouTube’s APIs and building the player controls.

Custom YouTube Player

Custom YouTube Player

Step 2 – jQuery

The plugin’s code is located in the youTubeEmbed-jquery-1.0.js file. However, before being able to use it, you need to include the latest version of the jQuery library in the page, along with the jQuery SWFObject plugin and lastly script.js, which inserts two videos in the demonstration page and handles the submissions of the preview form.

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script src="jquery.swfobject.1-1-1.min.js"></script>
<script src="youTubeEmbed/youTubeEmbed-jquery-1.0.js"></script>
<script src="script.js"></script>

Before we start digging into the player plugin’s code, lets take a look at a sample response from YouTube’s gdata api. It can give you a lot of useful information about a video, including duration, access control (both of which used by the plugin) and all sorts of additional data such as title, description, tags, screenshots and more.

Sample JSON response

{
    "id": "u1zgFlCw8Aw",
    "uploaded": "2008-03-05T01:22:17.000Z",
    "updated": "2010-07-23T01:02:42.000Z",
    "uploader": "GoogleDevelopers",
    "category": "People",
    "title": "The YouTube API: Upload, Player APIs and more!",
    "description": "Listen to the YouTube APIs and Tools team talk about...",
    "tags": ["youtube", "launch", "api", "engineering"],
    "thumbnail": {
        "sqDefault": "http://i.ytimg.com/vi/u1zgFlCw8Aw/default.jpg",
        "hqDefault": "http://i.ytimg.com/vi/u1zgFlCw8Aw/hqdefault.jpg"
    },
    "player": {
        "default": "http://www.youtube.com/watch?v=u1zgFlCw8Aw",
        "mobile": "http://m.youtube.com/details?v=u1zgFlCw8Aw"
    },
    "content": {
        "1": "rtsp://v4.cache5.c.youtube.com/CiILE..",
        "5": "http://www.youtube.com/v/u1zgFlCw8Aw?f..",
        "6": "rtsp://v3.cache4.c.youtube.com/CiILENy73.."
    },
    "duration": 259,
    "location": "san bruno, ca",
    "rating": 4.3,
    "likeCount": "119",
    "ratingCount": 144,
    "viewCount": 251024,
    "favoriteCount": 164,
    "commentCount": 118,
    "accessControl": {
        "syndicate": "allowed",
        "commentVote": "allowed",
        "rate": "allowed",
        "list": "allowed",
        "comment": "allowed",
        "embed": "allowed",
        "videoRespond": "allowed"
    }
}

All the fields of this response objects are available as properties in the data variable (data.fieldname). You could potentially modify the plugin to show the title with a link to the video page on youtube, or show the rating of the video.

Now lets dive directly into the script’s source code.

youTubeEmbed-jquery-1.0.js – Part 1

(function($){

	$.fn.youTubeEmbed = function(settings){

		// Settings can be either a URL string,
		// or an object

		if(typeof settings == 'string'){
			settings = {'video' : settings}
		}

		// Default values

		var def = {
			width		: 640,
			progressBar	: true
		};

		settings = $.extend(def,settings);

		var elements = {
			originalDIV	: this,	// The "this" of the plugin
			container	: null,	// A container div, inserted by the plugin
			control		: null,	// The control play/pause button
			player		: null,	// The flash player
			progress	: null,	// Progress bar
			elapsed		: null	// The light blue elapsed bar
		};

		try{	

			settings.videoID = settings.video.match(/v=(\w+)/)[1];

			// safeID is a stripped version of videoID,
			// ready for use as a JavaScript function name

			settings.safeID = settings.videoID.replace(/[^a-z0-9]/ig,'');

		} catch (e){
			// If the url was invalid, just return the "this"
			return elements.originalDIV;
		}

		// Fetch data about the video from YouTube's API

		var youtubeAPI = 'http://gdata.youtube.com/feeds/api/videos?v=2&alt=jsonc';

		$.get(youtubeAPI,{'q':settings.videoID},function(response){

			var data = response.data;

			if(!data.totalItems || data.items[0].accessControl.embed!="allowed"){

				// If the video was not found, or embedding is not allowed;

				return elements.originalDIV;
			}

			// data holds API info about the video:

			data = data.items[0];

			settings.ratio = 3/4;
			if(data.aspectRatio == "widescreen"){
				settings.ratio = 9/16;
			}

			settings.height = Math.round(settings.width*settings.ratio);

We start by defining our script as a jQuery plugin by adding it as a function to the $.fn object. To make the code easier to follow and read, I put all the elements of the page, such as the control and the progressBar divs in a structure called elements.

After extracting the id of the video (a unique 11 character sequence after the ?v= parameter), we send a JSONP request to youtube’s gdata API. Depending on whether such a video exists, and on whether embedding is allowed on it, we proceed with calculating the aspect ratio. The height of the video is calculated by using this ratio and multiplying it to the width.

youTubeEmbed-jquery-1.0.js – Part 2

			// Creating a container inside the original div, which will
			// hold the object/embed code of the video

			elements.container = $('<div>',{className:'flashContainer',css:{
				width	: settings.width,
				height	: settings.height
			}}).appendTo(elements.originalDIV);

			// Embedding the YouTube chromeless player
			// and loading the video inside it:

			elements.container.flash({
				swf			: 'http://www.youtube.com/apiplayer?enablejsapi=1&version=3',
				id			: 'video_'+settings.safeID,
				height		: settings.height,
				width		: settings.width,
				allowScriptAccess:'always',
				wmode		: 'transparent',
				flashvars	: {
					"video_id"		: settings.videoID,
					"playerapiid"	: settings.safeID
				}
			});

			// We use get, because we need the DOM element
			// itself, and not a jquery object:

			elements.player = elements.container.flash().get(0);

			// Creating the control Div. It will act as a ply/pause button

			elements.control = $('<div>',{className:'controlDiv play'})
							   .appendTo(elements.container);

			// If the user wants to show the progress bar:

			if(settings.progressBar){
				elements.progress =	$('<div>',{className:'progressBar'})
									.appendTo(elements.container);

				elements.elapsed =	$('<div>',{className:'elapsed'})
									.appendTo(elements.progress);

				elements.progress.click(function(e){

					// When a click occurs on the progress bar, seek to the
					// appropriate moment of the video.

					var ratio = (e.pageX-elements.progress.offset().left)/elements.progress.outerWidth();

					elements.elapsed.width(ratio*100+'%');
					elements.player.seekTo(Math.round(data.duration*ratio), true);
					return false;
				});

			}

In the second part of the code, we use the SWFObject plugin to generate the embed code of  the youtube chromeless player. Notice that the id of the video is passed as a flashvar so the player can load it directly. The safeID variable (a JavaScript safe version of the videoid) becomes the value of the id parameter of the to-be generated object element. This way we can later fetch the DOM element by running document.getElementById(‘video_’+settings.safeID) and get access to the methods which control the youtube player (play, pause etc).

youTubeEmbed-jquery-1.0.js – Part 3

var initialized = false;

// Creating a global event listening function for the video
// (required by YouTube's player API):

window['eventListener_'+settings.safeID] = function(status){

	var interval;

	if(status==-1)	// video is loaded
	{
		if(!initialized)
		{
			// Listen for a click on the control button:

			elements.control.click(function(){
				if(!elements.container.hasClass('playing')){

					// If the video is not currently playing, start it:

					elements.control.removeClass('play replay').addClass('pause');
					elements.container.addClass('playing');
					elements.player.playVideo();

					if(settings.progressBar){
						interval = window.setInterval(function(){
							elements.elapsed.width(
					((elements.player.getCurrentTime()/data.duration)*100)+'%'
							);
						},1000);
					}

				} else {

					// If the video is currently playing, pause it:

					elements.control.removeClass('pause').addClass('play');
					elements.container.removeClass('playing');
					elements.player.pauseVideo();

					if(settings.progressBar){
						window.clearInterval(interval);
					}
				}
			});

			initialized = true;
		}
		else{
			// This will happen if the user has clicked on the
			// YouTube logo and has been redirected to youtube.com

			if(elements.container.hasClass('playing'))
			{
				elements.control.click();
			}
		}
	}

In order to be able to control the video player, we need to be notified when certain events (like playback stopped, video ready etc) occur. Normally, this would mean that we need to pass a callback function, which is executed by the player every time such an event happens.

Unfortunately, flash can only execute functions if they are defined in the global scope and cannot see the functions which are defined inside the plugin. However, by creating functions with unique names (with the safeID) and explicitly adding them to the window object we can make this happen. If it weren’t for this little trick, it would be impossible for the plugin to work.

youTubeEmbed-jquery-1.0.js – Part 4

				if(status==0){ // video has ended
					elements.control.removeClass('pause').addClass('replay');
					elements.container.removeClass('playing');
				}
			}

			// This global function is called when the player is loaded.
			// It is shared by all the videos on the page:

			if(!window.onYouTubePlayerReady)
			{
				window.onYouTubePlayerReady = function(playerID){
					document.getElementById('video_'+playerID).addEventListener('onStateChange','eventListener_'+playerID);
				}
			}
		},'jsonp');

		return elements.originalDIV;
	}

})(jQuery);

The event listening function we created in the previous section of the code, is attached to the player with the addEventListener method. It is called every time when a “stateChange” occurs (playback start, playback pause, end of playback etc). A numeric code is passed to the event listening function as a parameter, corresponding to the event.

Now lets take a look at how our plugin is used.

script.js

$(document).ready(function(){

	$('#player').youTubeEmbed('http://www.youtube.com/watch?v=u1zgFlCw8Aw');

	/*
		//You can alternatively pass an object:

		$('#player').youTubeEmbed({
			video			: 'http://www.youtube.com/watch?v=u1zgFlCw8Aw',
			width			: 600, 		// Height is calculated automatically
			progressBar	: false		// Hide the progress bar
		});

	*/

});

You just need to call the youTubeEmbed() method and pass it either a string or a configuration object. If a string is passed, it is assumed to be the URL of a YouTube video. If you are passing an object make sure that you are passing the video property with a correct video URL.

Video Playing with a Progress Bar

Video Playing with a Progress Bar

Step 3 – CSS

Finally we are left with applying a few CSS styles to the player. They will change the design of the player controls and define the way they are positioned in the player window.

youTubeEmbed-jquery-1.0.css

.flashContainer{

	/*	Setting the container to relative positioning
		so we can center the control div */

	position:relative;
	overflow:hidden;
}

.progressBar{
	display:none;
	position:absolute;
	width:auto;
	height:8px;
	left:20px;
	right:105px;
	bottom:20px;
	background-color:#141414;
	overflow:hidden;
	cursor:pointer;

	/* A light CSS3 bottom highlight */

	-moz-box-shadow:0 1px 0 rgba(255, 255, 255, 0.3);
	-webkit-box-shadow:0 1px 0 rgba(255, 255, 255, 0.3);
	box-shadow:0 1px 0 rgba(255, 255, 255, 0.3);
}

.progressBar .elapsed{
	position:absolute;
	width:0;
	height:100%;
	background-color:#1fa2f6;
	border-right:1px solid #49AFF0;
}

.controlDiv{
	/* Centering the control div */
	position:absolute;
	width:120px;
	height:120px;
	cursor:pointer;
	top:50%;
	left:50%;
	margin:-60px 0 0 -60px;
}

.controlDiv.play{
	background:url('img/play.png') no-repeat center center;
}

.controlDiv.replay{
	background:url('img/replay.png') no-repeat center center;
}

.controlDiv.pause{
	background:url('img/pause.png') no-repeat -99999px;
}

.flashContainer:hover .controlDiv.pause{
	background-position:center center;
}

/* Only show the progress bar when the video is playing */

.flashContainer.playing:hover .progressBar{
	display:block;
}

To customize the look of the player you just need to change the color values above. Also you can edit the png files with the play/pause buttons. Clearly this is much easier than modifying the looks of the default youtube player. It also strips all of the unnecessary chrome and leaves you with what matters most – your video.

With this our Custom YouTube Player plugin is complete!

Did you like this tutorial? Share your thoughts in the comment section below.

Powered by Gewgley