JW Player - Amazon Web Services CDN 和高级 Javascript 调试

JW Player - Amazon Web Services CDN and Advanced Javascript Debugging

我在以下页面嵌入了一个定制的 JW Player 7 Pro:http://dev.sharepoint-videos.com/jw-player-self-hosted/

嵌入代码如下:

<!--Course Video, Scripts and Style-->
<div id="visualSPPlayer">Loading the player...</div>
<script type="text/javascript">
  var playerInstance = jwplayer("visualSPPlayer");
  playerInstance.setup({
    file: "http://dbt8c2ssdzlxg.cloudfront.net/Search2013.mp4",
    primary: "HTML5",
    image: "https://assets-jpcust.jwpsrv.com/thumbs/2kOAeo0k-320.jpg?1443200073230",
    width: "100%",
    aspectratio: "16:9",
    tracks: [
      {
        file: "http://dbt8c2ssdzlxg.cloudfront.net/captions/Search/Captions.srt",
        label: "English",
        kind: "captions",
      },
      {
        file: 'http://dbt8c2ssdzlxg.cloudfront.net/chapters/Search/Chapters.vtt',
        kind: 'chapters'
  
      },
      {
        file: "http://dbt8c2ssdzlxg.cloudfront.net/thumbnails/search_thumbnails.vtt",
        kind: "thumbnails"
      }
    ],
    skin: {
      name: "vapor",
      active: "#E16933",
      inactive: "#E16933",
      background: "#333333"
    }
  });
</script>
<script type="application/javascript" src="http://dev.sharepoint-videos.com/wp-content/themes/symplex-child/js/player.js"></script>
<link rel="stylesheet" href="http://dev.sharepoint-videos.com/wp-content/themes/symplex-child/css/player.css" type="text/css" media="screen"/>

player.js文件内容:

jQuery(document).ready(function () {

  jQuery(function ($) {

    var playerInstance = jwplayer();
    var chapters = [];
    var captions = [];
    var toc = [];
    var caption = -1;
    var matches = [];
    var seekArr = [];
    var seekPos = [];
    var seePos;
    var query = "";
    var cycle = -1;

    var transcript = document.getElementById('courseTranscript');
    var search = document.getElementById('courseSearch');
    var match = document.getElementById('courseMatch');

    var caption_file;
    var chapter_file;

    playerInstance.onReady(function () {
      //Self-Hosted
      caption_file = playerInstance.getPlaylist()[0].tracks[0].file;
      chapter_file = playerInstance.getPlaylist()[0].tracks[1].file;

      if (playerInstance.getRenderingMode() == "flash") {
        return;
      }

      tag = document.querySelector('video');
      tag.defaultPlaybackRate = 1.0;
      tag.playbackRate = 1.0;

      playerInstance.addButton("http://dev.sharepoint-videos.com/wp-content/uploads/2015/09/hare.png", "1.5x", function () {
        playerInstance.seek(playerInstance.getPosition());
        tag.playbackRate = 1.5;
      }, "playerHighSpeed");

      playerInstance.addButton("http://dev.sharepoint-videos.com/wp-content/uploads/2015/09/normal.png", "1.0x", function () {
        playerInstance.seek(playerInstance.getPosition());
        tag.playbackRate = 1.0;
      }, "playerNormalSpeed");

      playerInstance.addButton("http://dev.sharepoint-videos.com/wp-content/uploads/2015/09/snail.png", "0.5x", function () {
        playerInstance.seek(playerInstance.getPosition());
        tag.playbackRate = 0.5;
      }, "playerSlowSpeed");
    });

    //Adds Player Focus on Playing
    playerInstance.on('play', function () {
      $('html, body').animate({
        scrollTop: $(".jwplayer").offset().top - 190
      }, 1000);
    });

    playerInstance.onReady(function () {
      $.get(caption_file, function (data) {
        data = data.trim();
        var t = data.split("\n\r\n");

        for (var i = 0; i < t.length; i++) {
          var c = parse(t[i]);
          chapters.push(c);
        }
        loadCaptions();
        loadChapters();
      });
      //
    });

    // Load chapters / captions
    function loadCaptions() {
      $.get(caption_file, function (data) {
        data = data.trim();
        var t = data.split("\n\r\n");
        t.pop();
        var h = "<p>";
        var s = 0;
        for (var i = 0; i < t.length; i++) {
          var c = parse(t[i]);
          
          if (s < chapters.length && c.begin > chapters[s].begin) {
            s++;
          }
          
          h += "<span id='caption" + i + "'>" + c.text + "</span>";
          captions.push(c);
        }
        transcript.innerHTML = h + "</p>";
      });
    };

    function parse(d) {
      var a = d.split("\n");
      //console.log(a[1]);
      var i = a[1].indexOf(' --> ');
      var t = a[2]; //Caption text

      if (a[3]) {
        t += " " + a[3];
      }
      
      t = t.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
      
      return {
        begin: seconds(a[1].substr(0, i)),
        btext: a[1].substr(3, i - 7),
        end: seconds(a[1].substr(i + 5)),
        text: t
      }
    };

    function seconds(s) {
      var a = s.split(':');
      secs = a[2].substring(0, a[2].indexOf(','));
      var r = Number(secs) + Number(a[a.length - 2]) * 60;

      if (a.length > 2) {
        r += Number(a[a.length - 3]) * 3600;
      }
      
      return r;
    };

    function toc_seconds(s) {
      var a = s.split(':');
      secs = a[2].substring(0, a[2].indexOf('.'));
      var r = Number(secs) + Number(a[a.length - 2]) * 60;
      
      if (a.length > 2) {
        r += Number(a[a.length - 3]) * 3600;
      }
      
      return r;
    };

    function toc_time(s) {
      var a = s.split(':');
      var ms = a[2].split(".");
      var h = a[0];

      if (h != "00") {
        var r = a[0] + ":" + a[1] + ":" + ms[0];
      } else {
        var r = a[1] + ":" + ms[0];
      }

      return r;
    };

    // Highlight current caption and chapter
    playerInstance.onTime(function (e) {
      var p = e.position;
      for (var j = 0; j < captions.length; j++) {
        if (captions[j].begin < p && captions[j].end > p) {
          if (j != caption) {
            var c = document.getElementById('caption' + j);
            if (caption > -1) {
              document.getElementById('caption' + caption).className = "";
            }
            c.className = "current";
            if (query == "") {
              transcript.scrollTop = c.offsetTop - transcript.offsetTop - 40;
            }
            caption = j;
          }
          break;
        }
      }
    });

    // Hook up interactivity
    transcript.addEventListener("click", function (e) {
      if (e.target.id.indexOf("caption") == 0) {
        var i = Number(e.target.id.replace("caption", ""));
        playerInstance.seek(captions[i].begin);
      }
    });
    /**/
    
    search.addEventListener('focus', function (e) {
      setTimeout(function () {
        search.select();
      }, 100);
      resetSearch();
      $("#prevMatchLink").hide();
      $("#nextMatchLink").hide();
    });
    
    search.addEventListener('keydown', function (e) {
      if (e.keyCode == 27) {
        resetSearch();
        $("#prevMatchLink").hide();
        $("#nextMatchLink").hide();
      } else if (e.keyCode == 13) {
        $("#prevMatchLink").show();
        $("#nextMatchLink").show();
        var q = this.value.toLowerCase();
        if (q.length > 0) {
          if (q == query) {
            if (cycle >= matches.length - 1) {
              cycleSearch(0);

            } else {

              cycleSearch(cycle + 1);
            }
          } else {
            resetSearch();
            searchTranscript(q);
          }
        } else {
          resetSearch();
        }
      } else if (e.keyCode == 37) {
        cycleSearch(cycle - 1);
      }
      else if (e.keyCode == 39) {
        cycleSearch(cycle + 1);
      }
    });

    $("#prevMatchLink").click(function (e) {
      e.preventDefault();
      cycleSearch(cycle - 1);
    });

    $("#nextMatchLink").click(function (e) {
      e.preventDefault();
      cycleSearch(cycle + 1);
    });

    // Execute search
    function searchTranscript(q) {
      matches = [];
      query = q;
      for (var i = 0; i < captions.length; i++) {
        var m = captions[i].text.toLowerCase().indexOf(q);
        if (m > -1) {
          document.getElementById('caption' + i).innerHTML =
            captions[i].text.substr(0, m) + "<em>" +
              captions[i].text.substr(m, q.length) + "</em>" +
              captions[i].text.substr(m + q.length);
          matches.push(i);
        }
      }
      
      if (matches.length) {
        cycleSearch(0);
      } else {
        resetSearch();
      }
    };

    function cycleSearch(i) {
      if (cycle > -1) {
        var o = document.getElementById('caption' + matches[cycle]);
        o.getElementsByTagName("em")[0].className = "";
      }
      var c = document.getElementById('caption' + matches[i]);
      c.getElementsByTagName("em")[0].className = "current";
      match.innerHTML = (i + 1) + " of " + matches.length;
      transcript.scrollTop = c.offsetTop - transcript.offsetTop - 40;
      cycle = i;
    };

    function resetSearch() {
      if (matches.length) {
        for (var i = 0; i < captions.length; i++) {
          document.getElementById('caption' + i).innerHTML = captions[i].text;
        }
      }
      query = "";
      matches = [];
      match.innerHTML = "0 of 0";
      cycle = -1;
      transcript.scrollTop = 0;
    };

    var videoTitle = $(".videoTitle").text();
    var hasPlayed = false;

    playerInstance.onBeforePlay(function (event) {
      if (hasPlayed == false) {
        ga('send', 'event', 'Video', 'Play', videoTitle);
        hasPlayed = true;
      }
    });

    //Can be used to trigger the Course to Marked Completed so the user doesn't have to
    playerInstance.on('complete', function () {

    });

    function loadChapters() {

      $.get(chapter_file, function (data) {
        data = data.trim();
        var c = data.split("\n\r\n");
        var d;

        for (var i = 0; i < c.length; i++) {
          d = c[i].split("\n");
          //pushes in Title for each chapter
          toc.push(d[0]);
          //pushes in the time intervals for each chapter
          seekArr.push(d[1]);
        };

        for (var a = 0; a < seekArr.length; a++) {
          //Splits the time interval and pushes the start interval for each chapter
          var tempPos = seekArr[a].split(" --> ");
          seekPos.push(tempPos[0]);
        };

        runTOC(seekPos);
        var toc_output = "";
        $.each(toc, function (i, v) {
          toc_output += "<li class=ch" + i + "><a href='#' onclick='jwplayer().seek(" + toc_seconds(seekPos[i]) + ");'>" + v + "</a> (" + toc_time(seekPos[i]) + ")</li>"
        });

        if (toc.length < 7) {
          toc_output += " <li class='blank'> </li><li class='blank'> </li>";
        }

        $(".courseTitles ul").html(toc_output);
      });
    };

    function runTOC(x) {
      playerInstance.onTime(function (event) {

        for (var i = 0; i < x.length; i++) {
          if (event.position > toc_seconds(x[i])) {
            $(".courseTitles ul li").removeClass("active");
            $(".courseTitles ul li.ch" + i).addClass('active');
          }
        };
      });
    }
  });
});

我们使用 Amazon Web Services 和 Cloudfront 托管视频和 Chapter/Captions VTT 文件。

我们已经包含了字幕中的交互式脚本以及视频准备好播放后要加载的动态视频章节。

我注意到的一件事是章节和成绩单并不总是加载并且需要刷新页面几次所以我认为这可能是等式的 AWS 方面的缓存问题。

我已经使用了 Google Chrome 并且当章节和脚本未加载时开发者控制台中没有错误。

应该注意的是,当我们使用 JW 平台云托管解决方案时,此功能可以完美运行,因此它似乎是 AWS/Cloudfront CDN 的一个因素。

      function load_jwp_scripts() { 
     
   wp_enqueue_script('jwplayer-js', plugins_url( "/js/jwplayer.js", __FILE__), array(), '1.0', false);
   wp_enqueue_script('jwplayer-license-js', plugins_url( "/js/jwplayer_license.js", __FILE__), array(), '1.0', false);
   wp_enqueue_script('jwplayer-player-js', plugins_url( "/js/player.js", __FILE__), array('jquery'), '1.0', true);
   
      }
  
     add_action('wp_enqueue_scripts', 'load_jwp_scripts');

我找到了解决问题的可行方法。

问题是 $(document).ready 声明的使用。它与 jwplayer.on("ready").

不兼容

删除它,它再次正确呈现。