// A lot can be done with xAPI and videos, and this post is about how I overcame some technical hurdles with YouTube’s API, specifically.
First of all, for basic setup of the YouTube player we follow the instructions here. We will need to include swfobject.js to allow cross-domain iframe calls, and create an iframe in which to house the player. I chose to follow the documentation pretty closely on this one.

Next, we will need to make a call to swfobject.embedSWF(). Note here that we will pass in a few parameters and attributes like params = { allowScriptAccess: “always” };. This is what my function looks like. Note that I pass in the id (ytapiplayer) where I want the player located.

swfobject.embedSWF("http://www.youtube.com/v/" +
videoID + //initial video to load "?enablejsapi=1&playerapiid=ytplayer&version=3",
"800", "500", //initial width and height of player
"8",null, null, params, atts);

Next, we will want to track when this player changes state (loaded, playing, paused, finished, etc). We should at the very least have functions in our JavaScript named onYouTubePlayerReady and onPlayerStateChange. The first is called when the player is loaded. In this function, we need to add the state change event handler.

function onYouTubePlayerReady(playerId) {
    ytplayer = document.getElementById("myytplayer");
    //player loaded
    //do some stuff here if you want!

The second function will be called every time the YouTube player changes state, and in turn these states can be used to track specific interactions and events. My onPlayerStateChange looks very simple but it’s able to capture some pretty important data.

function onPlayerStateChange(newState) {
    switch (newState) {
    	case (YT.PlayerState.PLAYING):
    	case (YT.PlayerState.PAUSED):
        if (lastPlayerState == YT.PlayerState.PLAYING) {
            videoWatched(lastPlayerTime, ytplayer.getCurrentTime())
        } else if (lastPlayerState == YT.PlayerState.PAUSED) {
            videoSkipped(lastPlayerTime, ytplayer.getCurrentTime());
    	case (YT.PlayerState.ENDED):
    	case (YT.PlayerState.UNSTARTED):
    lastPlayerTime = ytplayer.getCurrentTime();
    lastPlayerState = newState;

From here, we can call videoStarted, videoPaused, and videoEnded. These send very simple xAPI statements (see http://rusticisoftware.github.io/TinCanJS/). We also keep track of the player’s previous time and state. This lets us send some meaningful statements about what parts of the video the user watched, and which parts were skipped over.
Protip: Call Google’s data api for further information about the video, like the title.

$.getJSON('https://gdata.youtube.com/feeds/api/videos/' + videoID + '?v=2&alt=json',
            function (data) {
			videoTitle = data.entry.title.$t;
                //do some stuff here

Using the TinCanJS library, we can send statements corresponding to the actual part of the video watched. We pass in the start and end times as extensions. Similarly, to send a statement where the user skips a part of the video, we would send the start and end times of the section skipped as extensions.

function videoWatched(start, finish) {//start and finish in seconds
        actor: Cards.getActor(),
        verb: {
            id: "http://activitystrea.ms/schema/1.0/watch",
            display: {'en-US': 'watched'}
        target: {
            id: 'http://www.youtube.com/watch?v=' + videoID,
            definition: {
                name: { "en-US": videoTitle + " from " + timeString(start) + " to " + timeString(finish) },
                extensions: {
                    "http://demo.watershedlrs.com/tincan/extensions/start_point": timeString(start),
                    "http://demo.watershedlrs.com/tincan/extensions/end_point": timeString(finish)

There is a ton of other information that we can get from this api. We can get keywords, playlists, subscriptions, comments, pretty much anything. We can use this information to make the xAPI statements readable and meaningful. “Ervin watched http://www.youtube.com/watch?v=AmC9SmCBUj4” now becomes “Ervin watched Gordon Ramsay: How to Cook the Perfect Steak”

Another hurdle we might encounter is capturing when the user is actually watching the video. If the user clicks play and then leaves the window or minimizes the browser, we want to know that they have taken the main window out of focus.

In traditional content this is pretty straightforward and done with the window’s blur and focus events. These events should send statements indicating that the user suspended and resumed the activity, respectively. But when we embed the YouTube player in an iframe this adds a bit of complexity. This is because the clicking inside the iframe triggers the main window’s blur event. Essentially the iframe does not count as part of the window. To get past this we keep track of when the mouse enters and exits the iframe. For further information on this I suggest this thread.