YouTube成立于2005年,现已发展成为占主导地位的视频共享网站。 播放列表是YouTube使用最广泛的功能之一。 您可以建立自己和其他用户上传的视频的播放列表,并在YouTube上共享您的播放列表。 您还可以通过将播放列表嵌入网站,博客或社交媒体页面来共享它们。 但是,嵌入式播放列表播放器缺少YouTube.com上本机播放器的全部功能。
图1显示了本机youtube.com播放列表播放器。
图1.本机播放列表播放器
图2显示了默认的嵌入式播放器。
图2.嵌入式播放列表播放器

在嵌入式播放器中:
- 默认情况下,包含的视频列表处于隐藏状态,并且仅显示为叠加层,而不是播放器旁边(如YouTube.com上所示)。
- 随机播放列表的功能已删除。
- 由其作者添加到播放列表中的注释将被删除。
当我决定在自己的博客中嵌入一个播放列表,其中包含2014年FIFA世界杯资格赛中取得的一些最佳进球时,我遇到了这些限制。 在YouTube API,jQuery,JsRender模板引擎和Bootstrap前端框架的帮助下,我扩展并改进了默认嵌入式播放器,以创建与本地播放器等效的版本。 在本文中,我将带您完成相同的过程。 您将在YouTube上建立播放列表,添加带有精彩时刻(例如目标)的注释,然后使用相同的工具来构建嵌入式播放器,以恢复缺少的功能并改善用户体验。
完整的项目代码存储在DevOps Services上。 我已将该应用程序部署到IBM Bluemix™,因此您可以看到它的运行情况。 您可以使用任何主机站点(包括Bluemix )来部署代码。
注意 :要派生本文项目的代码,请单击右上角的“ 编辑代码”按钮(如果尚未登录,请输入DevOps Services凭据),然后单击菜单上的“ 分叉”按钮以创建一个新的项目。
第一步是获取用于访问YouTube API的密钥。
获取YouTube API密钥
要访问任何Google服务(包括YouTube)的API,您必须首先在Google Developers Console上注册一个项目并创建一个API密钥。 每天最多可免费访问各种API,具体数量因服务而异,并且每个拥有Google帐户的人都可以使用。
使用您的Google凭据登录Google Developers Console ,然后点击创建项目 。 默认情况下,项目名称和项目ID文本框包含随机值。 输入项目名称和ID,然后点击创建 。
图3.创建一个新的Google API项目
在项目仪表板中,单击“ API和身份验证”以打开可用API的列表。 向下滚动到YouTube Data API v3 ,然后单击标为“ 关”的相关按钮,以启用对项目的访问权限。 选择“ API和身份验证”>“凭据”,然后在“公共API访问”下选择“ 创建新密钥 ”。 选择浏览器密钥 。
在“ 创建浏览器密钥并配置允许的引用程序”对话框中,可以将对API密钥的访问限制为来自某些域的请求,例如您自己的站点或Bluemix 。 除非您限制访问,否则您的API密钥将对应用程序HTML源中的所有人可见。 在开发过程中这不是问题,因此将对话框留空并单击创建 。 但是,在部署项目后,请返回此步骤并限制对您应用程序域中请求的访问权限,以使第三方无法在其他应用程序中使用密钥。
如果从DevOps Services克隆了项目,则可以立即在index.html文件中指示的位置插入密钥,以使代码成功运行。
配置Google JavaScript客户端库以访问YouTube API
Google提供了各种语言的客户端库,包括JavaScript。 通过将此HTML标记包含在文档的<body>
标记(而不是<head>
标记)中,将JavaScript客户端导入到HTML文档中:
<script src="https://apis.google.com/js/client.js?onload=function"></script>
建议的最佳做法是将此<script>
标记放在<body>
标记的末尾。
加载客户端库后, gapi
(Google API)对象在页面上的窗口范围内可用。 您刚刚添加的<script>
标记中的onload
参数是指在库加载后立即调用的回调函数。 该函数的定义必须在加载客户端的<script>
标记之前。 该函数调用gapi.client.load( api name , version , callback function )
方法以加载客户端将使用的API。 在这种情况下,您可以使用gapi.client.load('youtube', 'v3', onYouTubeApiLoad)
加载YouTube API。 onYouTubeApiLoad
是一种单行函数,它调用setAPIKey
方法。 在setAPIKey
,将密钥设置为Google浏览器密钥的值。
客户端提供了用于访问Google服务的各种API调用的方法。 要获取播放列表中的项目列表,您将使用异步函数解析YouTube API中playlistItems.list
方法的响应,并将相关属性存储在名为YouTubePlayList
JavaScript对象的实例中。 您将在本文中开发YouTubePlaylist
对象。 对象的构造函数在下面显示JavaScript函数中定义。
清单1. YouTubePlaylist.js
function YouTubePlaylist(id, entries) {
this.id = id;
this.entries = entries;
this.currently_playing = 0;
this.randomizer = false;
}
-
id
是播放列表的ID。 -
entries
是播放列表中视频的JSON数组。 -
currently_playing
是entries
数组中当前播放的视频的索引。 -
randomizer
指示播放是否随机化。
创建带有响应参数的JSON对象
现在,使用响应中所需的参数创建一个JSON对象。 您只需要可用参数完整列表的一小部分。
part
参数是要由调用返回的属性的逗号分隔值(CSV)列表。 使用contentDetails
和snippet
属性。 snippet
属性包含有关每个视频的基本信息。 contentDetails
包含视频ID和播放列表作者添加的所有注释。 当您确定视频中的亮点时, contentDetails
将很重要。 playListId
参数是您将使用的播放列表的ID。 出于本文的目的,我设置了一个播放列表,其突出显示的ID为PLLzJfby7cTLTbusOgXca-yIpVOImC1mWe
。 在播放列表中,请注意,目标时间已添加为注释。 JSON requestOptions
对象现在看起来像这样:
var requestOptions = {
playlistId: playlist_id,
part: 'contentDetails,snippet'
};
调用gapi.client.youtube.playlistItems.list()
与此JSON对象作为参数方法返回一个对象与两种方法: execute
和subscribe
。 使用异步函数调用execute
方法,并将响应作为参数。
request.execute(function(response) {});
响应中的items
数组包含播放列表中的视频列表。 您将使用jQuery each()
方法来遍历所有项目。 您将视频ID,视频的中型缩略图,标题和注释存储在JSON对象中,然后将其添加到数组中。
清单2.添加JSON对象的entries
阵列
var entries = [];
$.each( response.items, function( key, val ) {
var entry = {};
entry.video_id = val.snippet.resourceId.videoId;
entry.image_src = val.snippet.thumbnails.medium.url;
entry.title = val.snippet.title;
entry.note = val.contentDetails.note;
entries.push(entry);
});
创建YouTubePlayList
对象
通过调用具有播放列表ID和entries
数组的构造函数(请参见清单1)来创建新的YouTubePlaylist
对象,并将该对象作为以播放列表ID命名的新变量存储在window作用域中。
window[playlist_id] = new YouTubePlaylist(playlistId, entries);
现在,您可以使用window[playlist_id]
访问YouTubePlaylist
对象。 稍后,您将使用window[playlist_id]
调用YouTubePlaylist
对象的其他功能。
使用模板通过JsRender格式化播放器
播放器的轮廓类似于图4,右侧的缩略图,标题和注释列表由播放列表中的条目组成。
图4.新播放器的轮廓
默认情况下,HTML字符集不包含与本机YouTube播放器上的下一个,上一个和随机图标相似的图标。 您的应用将使用Bootstrap前端框架提供的图标。 要导入Bootstrap样式表,请将以下代码段添加到<head>
标记中。
<link rel="stylesheet"
type="text/css"
href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">
您将使用JsRender模板引擎来渲染YouTubePlaylist
对象。 您可以在<script>
标记中定义一个JsRender模板,并将其type
属性设置为text/x-jsrender
。 通过使用要渲染的对象调用x-jsrender
模板的render()
方法来生成HTML。 您可以使用双大括号符号{{:}}
在模板中呈现变量。 例如, {{:id}}
呈现传递给模板的对象的id
属性。
清单3中所示的模板将YouTube播放器嵌入到网页中。
清单3.嵌入YouTube播放器的模板
<object width="640"
height="360"
data="http://www.youtube.com/v/video id?version=3&enablejsapi=1&playerapiid=id"
id="player id"
type="application/x-shockwave-flash">
<param value="always" name="allowScriptAccess">
<param value="true" name="allowFullScreen">
</object>
在清单3中, video id
是要播放的视频的ID,而播放器ID是播放器对象本身的ID。 初始化时,您将提示entries
数组中的第一个视频,因此请在嵌入式播放器模板中输入{{:entries[0].video_id}}
作为视频ID。 对于播放器ID,请使用播放列表ID,即{{:id}}
。
初始化YouTube播放器对象后,播放器将自动以播放器ID作为参数调用onYouTubePlayerReady()
函数,以自定义其状态更改时的行为。 播放器的状态为未开始,结束,正在播放,暂停,正在缓冲和提示视频; 这些被枚举为-1、0、1、2、3和4。在API的当前版本中,无法自定义回调函数。 现在,您将在窗口范围内定义一个函数,以将下一个视频加载到播放列表中(稍后将在YouTubePlaylist
对象中定义此函数),并将其作为事件侦听器添加到播放器。
清单4.用于在播放列表中加载下一个视频的函数
function onYouTubePlayerReady(playerApiId) {
var player = document.getElementById(playerApiId);
window["onStateChange" + playerApiId] = function(state) {
switch(state) {
case 0:
var video_player = document.getElementById(player_id);
video_player.loadVideoById(window[player_id].getNextVideo(), 0, "large");
break;
}
};
player.addEventListener("onStateChange", "onStateChange" + playerApiId);
}
要遍历视频数组,您将使用JsRender {{for}}
标签。 您将使用清单5中的模板在图4的右侧创建每个播放列表条目。
清单5.用于在列表中创建每个播放列表条目的模板
{{for entries}}
<div class="playListEntry {{if #index == 0}}nowPlaying{{/if}}" id="{{:video_id}}">
<div class="playListEntryThumbnail">
<img src="{{:image_src}}"/>
</div>
<div class="playListEntryDescription">
<div class="playListEntryTitle">{{:title}}</div>
<div class="playListEntryNote">{{:note}}</div>
</div>
</div>
{{/for}}
在清单5中, entries
是YouTubePlaylist
对象中的entries
属性。 数组中对象的索引存储在#index
变量中。 因为列表中的第一个条目是创建时播放器中加载的视频,所以您将通过使用{{if}}
标记在for
循环中将nowPlaying
CSS类应用于第一个playListEntry
类。
通过将Bootstrap glyphicon
类应用于span
,使用glyphicon-backward
, glyphicon-forward
和glyphicon-random
类,可以创建播放器底部的控件。
<div class="playListControls">
<span class="playListControl disabled glyphicon glyphicon-backward"/>
<span class="playListControl glyphicon glyphicon-forward"/>
<span class="playListControl glyphicon glyphicon-random"/>
</div>
请注意,最初,“上一个”图标被禁用,因为当播放器首次加载时,它默认为播放列表中的第一个条目,因此没有以前的视频可以播放。
您需要将呈现HTML添加到具有此代码段的ID playlist
的页面上的空白div
(请记住window[player_id]
引用了您在“ 创建YouTubePlaylist ”部分中创建的对象。
$('#' + player_id).html($('#playListPlayerTemplate').render(window[player_id]));
要使此代码可重复使用,请将其移至带有签名addPlaylistToElement(playlist_id, element_id)
的函数中,然后可以使用addPlaylistToElement('PLLzJfby7cTLTbusOgXca-yIpVOImC1mWe', 'playlist')
进行调用。
添加控件
返回到YouTubePlaylist
对象并开始添加功能。 稍后,您将使用该对象的增强版本来完成网页中的播放器。
向对象添加六个函数: previous()
, next()
, getCurrentlyPlaying()
, setCurrentlyPlaying()
, randomize()
和isRandomized()
。 previous
和next
功能移至播放列表中的相关视频,如果操作成功,则返回true
否则,则返回false(即,如果用户在播放列表中的第一个条目上单击“上一个”或在操作上单击“下一个”,则返回false)。最后一个条目)。 getCurrentlyPlaying()
返回播放列表中当前正在播放的视频的ID。 randomize()
设置或isRandomizer()
设置对象中的random
属性,而isRandomizer()
返回random属性的值。
清单6显示了Next()
函数。
清单6. Next()
函数
next: function() {
var retVal = false;
if(this.randomizer) {
retVal = true;
this.currently_playing = Math.floor((Math.random() * this.entries.length));
}
else if(this.currently_playing <= this.entries.length) {
retVal = true;
this.currently_playing++;
}
return retVal;
在Next()
函数,首先检查如果random
属性被设置,并且如果是,则设置currently_playing
索引中的随机值entries
阵列。 如果未设置random
属性,并且currently_playing
索引小于数组中的视频数量(也就是说,您尚未传递播放列表中的最后一个视频),则将索引的值增加1以移至下一个视频并返回true
表示操作成功。 如果失败,则返回false
。
清单7显示了previous()
函数。 如果currently_playing
索引大于零(也就是说,用户正在观看播放列表中除第一个视频以外的任何视频),则将索引减1并返回true
表示操作成功;否则,返回true
。 否则,返回false
。
清单7. previous()
函数
previous: function() {
var retVal = false;
if(this.currently_playing > 0) {
retVal = true;
this.currently_playing--;
}
return retVal;
}
在getCurrentlyPlaying()
函数中,返回getCurrentlyPlaying()
数组中当前播放索引的视频ID。
getCurrentlyPlaying: function() {
return this.entries[this.currently_playing].video_id;
}
清单8显示了setCurrentlyPlaying()
函数。 给定一个video_id
从当前播放列表中,设置currently_playing
到在元件的索引entries array
与该值。
清单8. setCurrentlyPlaying()
函数
setCurrentlyPlaying: function(video_id) {
for(var index = 0; index < this.entries.length; index++) {
if (this.entries[index].video_id === video_id) {
this.currently_playing = index;
break;
}
}
}
在randomize()
函数中,将randomizer
属性的值反转(从true到false,反之亦然),并返回新值。
randomize: function() {
this.randomizer = !(this.randomizer);
return this.randomizer;
}
isRandomized()
函数返回播放列表的randomizer
属性的值,即播放列表是否在随机播放中:
isRandomized: function() {
return this.randomizer;
}
使用添加的功能
现在添加功能以使用JavaScript对象的添加功能。
首先,添加一个辅助函数来安排播放列表的控件。 如果播放器是随机播放:
- “随机”图标突出显示。
- 由于您不在任何地方录制以前播放的视频,因此始终禁用“上一个”图标。
- 由于播放列表永远不会随机播放“最后一个”视频,因此始终启用“下一个”图标。 (考虑一下:当您的MP3播放器随机播放时,它会停止播放吗?)
如果播放列表不是随机播放的:
- 仅当播放列表中的第一个条目正在播放时,“上一个”图标才被禁用。
- 仅当播放列表中的最后一个条目正在播放时,“下一个”图标才被禁用。
- 禁用“随机”图标,直到再次单击它。
清单9显示了helper函数。
清单9.用于安排播放列表控件的Helper函数
function arrangePlayerControls(player_id) {
var playListPlayer = $('#' + player_id + 'playListPlayer');
if(window[player_id].isRandomized()) {
$('#' + player_id + 'Backward').addClass('disabled');
$('#' + player_id + 'Forward').removeClass('disabled');
$('#' + player_id + 'Random').addClass('randomizeActive');
}
else {
$('#' + player_id + 'Random').removeClass('randomizeActive');
var playListEntries = $('#' + player_id + 'playListEntries');
if(playListEntries.children(":first").hasClass('nowPlaying')) {
$('#' + player_id + 'Backward').addClass('disabled');
}
else {
$('#' + player_id + 'Backward').removeClass('disabled');
}
if(playListEntries.children(":last").hasClass('nowPlaying')) {
$('#' + player_id + 'Forward').addClass('disabled');
}
else {
$('#' + player_id + 'Forward').removeClass('disabled');
}
}
}
接下来,添加一个功能,以给定的播放列表ID和开始播放的时间索引将视频加载到播放器中。 请记住从当前播放视频的div
删除nowPlaying
类,并将其添加到新视频的div
中。 然后,调用清单9中的helper函数来安排播放列表图标。 清单10显示了视频加载功能。
清单10.将视频加载到播放器中的函数
function loadVideoForPlayer(currently_playing_video_id, player_id, time) {
time = time || 0;
var video_id = window[player_id].getCurrentlyPlaying();
$('#' + currently_playing_video_id).removeClass('nowPlaying')
$('#' + video_id).addClass('nowPlaying');
$('#' + player_id + 'playListEntries').scrollTop($('#' + video_id).index() * 80);
document.getElementById(player_id).loadVideoById(video_id, time, "large");
arrangePlayerControls(player_id);
}
最后,添加一个功能以在给定播放列表中加载下一个视频,但前提是该播放列表中有另一个视频(也就是说,您不在随机播放中,并且当前视频不是最后一个视频)。
清单11.如果存在下一个视频,则加载视频的函数
function loadNextVideo(player_id) {
var currently_playing_video_id = window[player_id].getCurrentlyPlaying();
if(window[player_id].next()) {
loadVideoForPlayer(currently_playing_video_id, player_id);
}
}
这个功能类似于您在声明匿名函数onYouTubePlayerReady()
见清单4 ),所以重构case 0
块onYouTubePlayerReady()
调用loadNextVideo()
来代替。
您可能已经注意到,我在播放列表中为每个视频添加了目标时间。 借助这些新功能,您可以将目标时间用作链接热点,直接跳到视频中的目标,而无需全程观看。 在$.each()
在环addPlaylistToElement()
存储所述注释每一个的值playlistItem
对象在局部变量,然后使用JavaScript match()
函数以从纸币返回的次阵列,使用正则表达式/[0-9]*:[0-5][0-9]/g
进行查找。 然后,您可以遍历此数组,并用链接替换变量中每次的值,以使用播放器ID,要播放的视频和开始的时间索引来调用cueThisVideo()
函数。 请记住,YouTube API loadVideoById()
调用花费的时间以秒为单位,因此请使用字符串中的冒号作为分隔符,将时间分成一个数组。 将第一个索引(分钟)中的值乘以60,将其转换为秒,然后将其添加到第二个索引中的秒,以获取秒的总数。 例如,1:30变成数组[1,30](1 * 60)+ 30 = 90秒。 最后,用新的链接替换注释中的时间。 每次处理便笺中的内容时,都将完整的字符串作为该条目的便笺存储在entries
数组中,如清单12所示。
清单12.用链接替换笔记中的时间
var note = val.contentDetails.note;
var times = note.match(/[0-9]*:[0-5][0-9]/g);
times.forEach(function(value, index, array) {
var time = value.split(":");
var seconds = parseInt(time[0]) * 60;
seconds += parseInt(time[1]);
note = note.replace(value,
"<span class='timeLink' onclick='cueThisVideo(\""
+ player_id + "\", \""
+ video_id + "\", "
+ seconds + ");'>"
+ value + "</span>");
});
entry.note = note;
剩下的就是重新访问模板并添加对这些新功能的调用。 您希望用户能够通过单击标题或缩略图来使视频排队,将播放列表中的上一个或下一个视频排队,并使用控制面板中的相关按钮将播放列表随机化。 清单13显示了模板中完成的更改,以使用添加的功能。
清单13.重构的模板代码
<div onclick="cueThisVideo('{{:~player_id}}', '{{:video_id}}');"
class="playListEntryThumbnail">
<img src="{{:image_src}}"/>
</div>
<div onclick="cueThisVideo('{{:~player_id}}', '{{:video_id}}');"
class="playListEntryTitle">
{{:title}}
</div>
<span id="{{:id}}Backward"
class="playListControl disabled glyphicon glyphicon-backward"
onclick="if(!$(this).hasClass('disabled')) { loadPreviousVideo('{{:id}}') }">
</span>
<span id="{{:id}}Forward"
class="playListControl glyphicon glyphicon-forward"
onclick="if(!$(this).hasClass('disabled')) { loadNextVideo('{{:id}}') }">
</span>
<span id="{{:id}}Random"
class="playListControl glyphicon glyphicon-random"
onclick="window['{{:id}}'].randomize();arrangePlayerControls('{{:id}}');">
</span>
现在,您的自定义播放器已准备就绪,可以摇滚!
结论
本文演示了如何使用YouTube API和一些简单JavaScript和样式来呈现嵌入式YouTube播放列表,并具有与YouTube.com上的本地播放列表相同的功能。 有关用于进一步增强播放器的一些建议,请参阅我的DevOps Services页面中的README文件。 随意分叉项目代码以实施任何或所有这些建议。
翻译自: https://www.ibm.com/developerworks/web/library/wa-bluemix-youtube/index.html