0

I took the Github xAPI script for "playing a youtube video" and tried to modify it to show two videos instead of one. Ultimately I would like to list five or six videos in this page. Unfortunately I cannot get it to show more than one video at a time. Instead it only shows one video and that is the second one that I have listed. Can someone tell me how I can modify this code to list more than one video? Also, I changed my LRS credentials before posting this question for obvious reasons. Many thanks.

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="initial-scale=1.0, maximum-scale=2.0">
  <meta name="description" content="A shorthand syntax for communicating xAPI Statements">
  <meta name="author" content="ADL">
  <link rel="icon" href="favicon.ico">

  <title>xAPI Youtube Video Tracking</title>
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css">
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css">
</head>

<body>

  <section class="container">

    <div class="page-header">
      <h1 class="text-primary"><i class="fa fa-youtube"></i> xAPI Youtube Video Tracking</h1>
      <h3 class="text-muted">Send Youtube Video interactions to an LRS with xAPI</h3>
    </div>

    <div class="row">

      <div class="form-group col-md-12">

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

        <div id="player2"></div>

        <p>This example uses minimal javascript and the youtube <a href="https://developers.google.com/youtube/iframe_api_reference" target="_blank">iframe API</a>.</p>
        <p>Statements are built with xapi-youtube-statements.js and dispatched to an LRS with xapiwrapper.min.js using a custom ADL.XAPIYoutubeStatements.onStateChangeCallback function.</p>
        <p>You can view statements <a href="http://adlnet.github.io/xapi-statement-viewer/">with the statement viewer</a>.</p>

      </div><!-- .col-md-12 -->

    </div><!-- .row -->

  </section><!-- .container -->

  <script type="text/javascript" src="lib/xapiwrapper.min.js"></script>
  <script type="text/javascript" src="src/xapi-youtube-statements.js"></script>

  <script>

    var video = "6hwHKOYCYL4"; // Change this to your video ID
    var videoName = "Microlearning vs Traditional Learning";


    var video2 = "SUJkBCHB4vQ"; // Change this to your video ID
    var videoName2 = "Micro Learning is a BIG deal";





    // "global" variables read by ADL.XAPIYoutubeStatements
    ADL.XAPIYoutubeStatements.changeConfig({
      "actor":  {"mbox":"mailto:john.menken@syniverse.com", "name":"John M."},
      "videoActivity": {"id":"https://www.youtube.com/watch?v=" + video, "definition":{"name": {"en-US":videoName}} }
    });

    ADL.XAPIYoutubeStatements.changeConfig({
      "actor":  {"mbox":"mailto:john.menken@syniverse.com", "name":"John M."},
      "videoActivity": {"id":"https://www.youtube.com/watch?v=" + video2, "definition":{"name": {"en-US":videoName2}} }
    });





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

    var player;
    function onYouTubeIframeAPIReady() {
      player = new YT.Player('player', {
        height: '390',
        width: '640',
        videoId: video,
        playerVars: { 'autoplay': 0 },
        events: {
          'onReady': ADL.XAPIYoutubeStatements.onPlayerReady,
          'onStateChange': ADL.XAPIYoutubeStatements.onStateChange
        }
      });
    }


    var player2;
    function onYouTubeIframeAPIReady() {
      player2 = new YT.Player('player2', {
        height: '390',
        width: '640',
        videoId: video2,
        playerVars: { 'autoplay': 0 },
        events: {
          'onReady': ADL.XAPIYoutubeStatements.onPlayerReady,
          'onStateChange': ADL.XAPIYoutubeStatements.onStateChange
        }
      });
    }



    initYT();



    // Auth for the LRS
    var conf = {
        "endpoint" : "https://www2.test.com/test/ScormEngineInterface/TCAPI/",
        "auth" : "Basic " + toBase64("test:test"),
    };

    ADL.XAPIWrapper.changeConfig(conf);

    /*
     * Custom Callbacks
     */
    ADL.XAPIYoutubeStatements.onPlayerReadyCallback = function(stmt) {
      console.log("on ready callback");
    }

    // Dispatch Youtube statements with XAPIWrapper
    ADL.XAPIYoutubeStatements.onStateChangeCallback = function(event, stmt) {
      console.log(stmt);
      if (stmt) {
        stmt['timestamp'] = (new Date()).toISOString();
        ADL.XAPIWrapper.sendStatement(stmt, function(){});
      } else {
        console.warn("no statement found in callback for event: " + event);
      }
    }

  </script>

</body>
</html>
John M.
  • 347
  • 1
  • 11

1 Answers1

1

You are overwriting the onYouTubeIframeAPIReady callback immediately upon setting the first one, so that when the iframe is ready the first callback is no longer the value of that function. (Have to think asynchronously.) That function should only ever get called once, and should only have a single definition. To make this work, you need to move the instantiation of player2 into the onYouTubeIframeAPIReady function. (This portion is a duplicate of onYouTubeIframeAPIReady called once but multiple videos needed on a page)

Also note that ADL's wrapper is effectively using a singleton for communications with the LRS, so you are going to get all of your statements for both videos with the activity as the object from the second video (because it is the later call to changeConfig). I don't see a way around this other than to wrap their state change handler with your own function that calls changeConfig each time the event occurs, and even then you'd have the potential for a race condition.

Community
  • 1
  • 1
Brian J. Miller
  • 2,169
  • 1
  • 12
  • 12