Skip to content

Instantly share code, notes, and snippets.

@DavidMikeSimon
Created April 9, 2014 19:42
Show Gist options
  • Select an option

  • Save DavidMikeSimon/10306996 to your computer and use it in GitHub Desktop.

Select an option

Save DavidMikeSimon/10306996 to your computer and use it in GitHub Desktop.
youtube.js
(function (Ayamel, global) {
"use strict";
var template = '<div class="videoBox"><div id="youtubePlayer"></div></div>',
captionHolderTemplate = '<div class="videoCaptionHolder"></div>',
urlRegexen = [
new RegExp("/youtube:\/\/([A-Za-z0-9]+)/i"),
new RegExp("/https?:\/\/www\.youtube\.com\/watch\?v=([A-Za-z0-9]+)/i"),
new RegExp("/https?:\/\/www\.youtube\.com\/v/([A-Za-z0-9]+)/i"),
new RegExp("/https?:\/\/youtu\.be\/([A-Za-z0-9]+)/i")
],
counter = 0;
function genId(){
counter++;
return "AyamelYTPlayer-"+counter.toString(36);
}
function getYouTubeId(url) {
if (typeof url === "string") { return ""; }
var i;
for (i=0; i<urlRegexen.length; ++i) {
var match = urlRegxen[i].exec(url);
if (match) { return match[1]; }
}
return "";
}
function supportsFile(file) {
return getYouTubeId(file.streamUri) !== "";
}
function findFile(resource) {
var file, i;
for (i=0; i<resource.content.files.length; i += 1) {
file = resource.content.files[i];
if (supportsFile(file))
return file;
}
return null;
}
function YouTubePlayer(args) {
var _this = this,
height, width,
idstr = genId(),
startTime = +args.startTime || 0,
stopTime = +args.endTime || -1,
$element = $(template),
element = $element[0],
$captionsElement = $(captionHolderTemplate),
captionsElement = $captionsElement[0];
// Create the element
this.$element = $element;
this.element = element;
args.$holder.append($element);
this.video = null;
// Create a place for captions
this.$captionsElement = $captionsElement;
this.captionsElement = captionsElement;
args.$holder.append($captionsElement);
// Set up the aspect ratio
//TODO: check for height overflow and resize smaller if necessary
args.aspectRatio = args.aspectRatio || Ayamel.aspectRatios.hdVideo;
width = $element.width();
height = width / args.aspectRatio;
$element.height(height);
// Include the YouTube API for a chromeless player
// Docs here: https://developers.google.com/youtube/js_api_reference
swfobject.embedSWF("http://www.youtube.com/apiplayer?enablejsapi=1&version=3",
"youtubePlayer", width, height, "8", null, null,
{ allowScriptAccess: "always", wmode: "transparent" }, { id: idstr });
//TODO: Set up properties object to allow interactions before YouTube has loaded
Object.defineProperties(this, {
init: {
value: function() {
var $video = this.$element.children("#"+idstr),
video = $video[0],
played = false,
playing = false;
$video.width("100%").height("100%");
this.video = video;
// Load the source
video.loadVideoById({
videoId: getYouTubeId(findFile(args.resource).streamUri),
startSeconds: startTime,
endSeconds: stopTime === -1 ? undefined : stopTime,
suggestedQuality: "large"
});
video.pauseVideo();
function timeUpdate() {
var timeEvent = document.createEvent("HTMLEvents");
timeEvent.initEvent("timeupdate", true, true);
element.dispatchEvent(timeEvent);
if (!playing) { return; }
if(Ayamel.utils.Animation){
Ayamel.utils.Animation.requestFrame(timeUpdate);
}else{
setTimeout(timeUpdate, 50);
}
}
// Set up events. Unfortunately the YouTube API requires the callback to be in the global namespace.
window.youtubeStateChange = function(data) {
if(data === -1) { return; }
element.dispatchEvent(new Event({
0: "ended",
1: "play",
2: "pause",
3: "durationchange",
5: 'loading'
}[data],{bubbles:true,cancelable:true}));
// If we started playing then send out timeupdate events
if (data === 1) {
playing = true;
timeUpdate();
}else if(data === 0 || data === 2){
// If this is the first pause, then the duration is changed/loaded, so send out that event
if (!played) {
played = true;
element.dispatchEvent(new Event('durationchange',{bubbles:true,cancelable:true}));
}
playing = false;
}
};
video.addEventListener("onStateChange", "youtubeStateChange");
}
},
duration: {
get: function () {
// var stop = stopTime === -1 ? this.video.getDuration() : stopTime;
// return stop - startTime;
return this.video ? this.video.getDuration() : 0;
}
},
currentTime: {
get: function () {
// return this.video.getCurrentTime() - startTime;
return this.video ? this.video.getCurrentTime() : 0;
},
set: function (time) {
if(!this.video){ return 0; }
time = Math.floor((+time||0)* 100) / 100;
this.video.seekTo(time);
this.element.dispatchEvent(new Event('timeupdate',{bubbles:true,cancelable:true}));
return time;
}
},
muted: {
get: function () {
return this.video ? this.video.isMuted() : false;
},
set: function (muted) {
if(!this.video){ return false; }
muted = !!muted;
this.video[muted?'mute':'unMute']();
return muted;
}
},
paused: {
get: function () {
return this.video ? this.video.getPlayerState() !== 1 : true;
}
},
playbackRate: {
get: function () {
return this.video ? this.video.getPlaybackRate() : 1;
},
set: function (playbackRate) {
if(!this.video){ return 1; }
var i, ratelist, best, next, bdist, ndist;
playbackRate = +playbackRate
if(isNaN(playbackRate)){ playbackRate = 1; }
ratelist = this.video.getAvailablePlaybackRates();
bdist = 1/0;
for(i=ratelist.length-1, best = ratelist[i]; i>=0; i--){
next = ratelist[i];
ndist = Math.abs(playbackRate - next);
if(ndist > bdist){ break; }
bdist = ndist;
best = next;
}
if(best !== this.video.getPlaybackRate()){
this.video.setPlaybackRate(best);
}
return best;
}
},
readyState: {
get: function () {
return this.video ? this.video.getPlayerState() : 0;
}
},
volume: {
get: function () {
return this.video ? this.video.getVolume() / 100 : 1;
},
set: function (volume) {
if(!this.video){ return 1; }
volume = (+volume||0);
this.video.setVolume(volume * 100);
return volume;
}
}
});
}
YouTubePlayer.prototype.play = function() {
if(!this.video){ return; }
this.video.playVideo();
};
YouTubePlayer.prototype.pause = function() {
if(!this.video){ return; }
this.video.pauseVideo();
};
YouTubePlayer.prototype.enterFullScreen = function(availableHeight) {
this.normalHeight = this.$element.height();
this.$element.height(availableHeight);
};
YouTubePlayer.prototype.exitFullScreen = function() {
this.$element.height(this.normalHeight);
};
Ayamel.mediaPlugins.video.youtube = {
install: function(args) {
var player = new YouTubePlayer(args);
global.onYouTubePlayerReady = player.init.bind(player);
return player;
},
supports: function(resource) {
return resource.content.files.some(function (file) {
return (resource.type === "video" && supportsFile(file));
});
},
features: {
desktop: {
captions: true,
fullScreen: true,
lastCaption: true,
play: true,
rate: false,
timeCode: true,
volume: true
},
mobile: {
captions: true,
fullScreen: true,
lastCaption: true,
play: true,
rate: false,
timeCode: true,
volume: false
}
}
};
}(Ayamel, window));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment