function UrlController($scope, $compile) {
$scope.urls = [
"https://www.youtube.com/watch?v=klGHKTsryfs",
"https://www.youtube.com/watch?v=simTvKaAGBI",
"https://www.youtube.com/watch?v=N1Xjxkla8Es"
];
// Try and create clean env for creating the videojs player in
function preInit() {
if ($scope.player) {
try {
$scope.player.dispose();
} catch (e) {
console.error(e);
}
}
$("#theVideo").remove();
$("#videoContainer").prepend($('<video width="650" height="500px" controls="" id="theVideo" class="video-js vjs-default-skin"></video>'));
}
$scope.initWithSrc = function() {
preInit();
$scope.player = videojs("theVideo", {
techOrder: ["youtube"],
src: "https://www.youtube.com/watch?v=XjwZAa2EjKA"
});
};
$scope.initWithoutSrc = function() {
preInit();
$scope.player = videojs("theVideo", {
techOrder: ["youtube"]
});
};
$scope.updateUrl = function(url) {
if (url) {
$scope.url = url;
console.debug("input: " + url);
}
$scope.player.src($scope.url);
};
}
<!DOCTYPE html>
<html ng-app>
<head>
<meta charset="utf-8" />
<title>Videojs tech tests</title>
<script data-require="jquery@*" data-semver="2.0.3" src="http://code.jquery.com/jquery-2.0.3.min.js"></script>
<link data-require="video.js@4.2.2" data-semver="4.2.2" rel="stylesheet" href="http://vjs.zencdn.net/4.2.2/video-js.css" />
<script data-require="video.js@4.2.2" data-semver="4.2.2" src="http://vjs.zencdn.net/4.2.2/video.js"></script>
<script src="http://code.angularjs.org/1.2.1/angular.js" data-semver="1.2.1" data-require="angular.js@*"></script>
<script>
document.write('<base href="' + document.location + '" />');
</script>
<link rel="stylesheet" href="style.css" />
<script type="text/javascript" charset="utf-8" src="videojs-youtube.js"></script>
<script src="app.js"></script>
</head>
<body ng-controller="UrlController">
<div>
<button ng-click="initWithSrc()">Init with source</button>
<button ng-click="initWithoutSrc()">Init without source</button>
</div>
<div id="videoContainer"></div>
<div>
<form ng-submit="updateUrl()">
<input type="text" placeholder="Add new url here" ng-model="url" />
<input type="submit" value="Change" class="btn-primary" />
</form>
<button ng-repeat="default in urls" ng-click="updateUrl(default)">{{default}}</button>
</div>
</body>
</html>
/* Put your css in here */
.wideInput{
width: 100%;
}
.floater{
float:left;
width: 45%;
}
#logOutput {
height: 100px;
position: fixed;
bottom: 0px;
overflow-y: auto;
overflow-x: hidden;
width: 100%;
padding: 0px;
margin: 0px;
}
.logElement {
}
.even{
background-color: seagreen;
}
.odd{
background-color: maroon;
}
/**
* @fileoverview YouTube Media Controller - Wrapper for YouTube Media API
* @version https://github.com/eXon/videojs-youtube/tree/9b5ff37e0fdf7f603486330390715d66aa62585e/src/media.youtube.js
*/
/**
* YouTube Media Controller - Wrapper for YouTube Media API
* @param {videojs.Player|Object} player
* @param {Object=} options
* @param {Function=} ready
* @constructor
*/
videojs.Youtube = videojs.MediaTechController.extend({
/** @constructor */
init: function(player, options, ready){
videojs.MediaTechController.call(this, player, options, ready);
// Copy the JavaScript options if they exists
if (typeof options['source'] != 'undefined') {
for (var key in options['source']) {
player.options()[key] = options['source'][key];
}
}
// Save those for internal usage
this.player_ = player;
this.player_el_ = document.getElementById(player.id());
this.player_el_.className = this.player_el_.className + ' vjs-youtube';
// Make sure nothing get in the way of the native player for iOS
if (videojs.IS_IOS) {
player.options()['ytcontrols'] = true;
}
this.id_ = this.player_.id() + '_youtube_api';
this.el_ = videojs.Component.prototype.createEl('iframe', {
id: this.id_,
className: 'vjs-tech',
scrolling: 'no',
marginWidth: 0,
marginHeight: 0,
frameBorder: 0,
webkitAllowFullScreen: 'true',
mozallowfullscreen: 'true',
allowFullScreen: 'true'
});
// This makes sure the mousemove is not lost within the iframe
// Only way to make sure the control bar shows when we come back in the video player
this.iframeblocker = videojs.Component.prototype.createEl('div', {
className: 'iframeblocker'
});
// Make sure to not block the play or pause
var self = this;
var toggleThis = function() {
if (self.paused()) {
self.play();
} else {
self.pause();
}
};
if (this.iframeblocker.addEventListener) {
this.iframeblocker.addEventListener('click', toggleThis);
} else {
this.iframeblocker.attachEvent('onclick', toggleThis);
}
this.player_el_.insertBefore(this.iframeblocker, this.player_el_.firstChild);
this.player_el_.insertBefore(this.el_, this.iframeblocker);
this.parseSrc(player.options()['src']);
var params = {
enablejsapi: 1,
iv_load_policy: 3,
playerapiid: this.id(),
disablekb: 1,
wmode: 'transparent',
controls: (this.player_.options()['ytcontrols'])?1:0,
showinfo: 0,
modestbranding: 1,
rel: 0,
autoplay: (this.player_.options()['autoplay'])?1:0,
loop: (this.player_.options()['loop'])?1:0,
list: this.playlistId
};
if (typeof params.list == 'undefined') {
delete params.list;
}
// If we are not on a server, don't specify the origin (it will crash)
if (window.location.protocol != 'file:'){
params.origin = window.location.protocol + '//' + window.location.host;
}
this.el_.src = 'https://www.youtube.com/embed/' + this.videoId + '?' + videojs.Youtube.makeQueryString(params);
if (this.player_.options()['ytcontrols']){
// Disable the video.js controls if we use the YouTube controls
this.player_.controls(false);
} else {
// Show the YouTube poster if their is no custom poster
if (!this.player_.poster()) {
if (this.videoId == null) {
// Set the black background if their is no video initially
this.iframeblocker.style.backgroundColor = 'black';
this.iframeblocker.style.display = 'block';
} else {
this.player_.poster('https://img.youtube.com/vi/' + this.videoId + '/0.jpg');
}
}
}
if (videojs.Youtube.apiReady){
this.loadYoutube();
} else {
// Add to the queue because the YouTube API is not ready
videojs.Youtube.loadingQueue.push(this);
// Load the YouTube API if it is the first YouTube video
if(!videojs.Youtube.apiLoading){
var tag = document.createElement('script');
tag.src = '//www.youtube.com/iframe_api';
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
videojs.Youtube.apiLoading = true;
}
}
this.triggerReady();
}
});
videojs.Youtube.prototype.dispose = function(){
videojs.MediaTechController.prototype.dispose.call(this);
};
videojs.Youtube.prototype.parseSrc = function(src){
this.srcVal = src;
// Regex to parse the video ID
var regId = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/;
var match = src.match(regId);
if (match && match[2].length == 11){
this.videoId = match[2];
} else {
this.videoId = null;
}
// Regex to parse the playlist ID
var regPlaylist = /[?&]list=([^#\&\?]+)/;
match = src.match(regPlaylist);
if (match != null && match.length > 1) {
this.playlistId = match[1];
} else {
// Make sure their is no playlist
if (this.playlistId) {
delete this.playlistId;
}
}
};
videojs.Youtube.prototype.src = function(src){
if (src) {
this.parseSrc(src);
if (this.videoId == null) {
// Set the black background if the URL isn't valid
this.iframeblocker.style.backgroundColor = 'black';
this.iframeblocker.style.display = 'block';
} else {
this.ytplayer.loadVideoById(this.videoId);
// Update the poster
this.player_el_.getElementsByClassName('vjs-poster')[0].style.backgroundImage = 'url(https://img.youtube.com/vi/' + this.videoId + '/0.jpg)';
this.iframeblocker.style.backgroundColor = '';
this.iframeblocker.style.display = '';
this.player_.poster('https://img.youtube.com/vi/' + this.videoId + '/0.jpg');
}
}
return this.srcVal;
};
videojs.Youtube.prototype.load = function(){};
videojs.Youtube.prototype.play = function(){
if (this.videoId != null) {
if (this.isReady_){
this.ytplayer.playVideo();
} else {
// Display the spinner until the YouTube video is ready to play
this.player_.trigger('waiting');
this.playOnReady = true;
}
}
};
videojs.Youtube.prototype.pause = function(){ this.ytplayer.pauseVideo(); };
videojs.Youtube.prototype.paused = function(){ return (this.ytplayer)?(this.lastState !== YT.PlayerState.PLAYING && this.lastState !== YT.PlayerState.BUFFERING):true; };
videojs.Youtube.prototype.currentTime = function(){ return (this.ytplayer)?this.ytplayer.getCurrentTime():0; };
videojs.Youtube.prototype.setCurrentTime = function(seconds){ this.ytplayer.seekTo(seconds, true); this.player_.trigger('timeupdate'); };
videojs.Youtube.prototype.duration = function(){ return (this.ytplayer)?this.ytplayer.getDuration():0; };
videojs.Youtube.prototype.volume = function() {
if (this.ytplayer && isNaN(this.volumeVal)) {
this.volumeVal = this.ytplayer.getVolume() / 100.0;
}
return this.volumeVal;
};
videojs.Youtube.prototype.setVolume = function(percentAsDecimal){
if (percentAsDecimal && percentAsDecimal != this.volumeVal) {
this.ytplayer.setVolume(percentAsDecimal * 100.0);
this.volumeVal = percentAsDecimal;
this.player_.trigger('volumechange');
}
};
videojs.Youtube.prototype.muted = function() { return this.mutedVal; };
videojs.Youtube.prototype.setMuted = function(muted) {
if (muted) {
this.ytplayer.mute();
} else {
this.ytplayer.unMute();
}
this.mutedVal = muted;
this.player_.trigger('volumechange');
};
videojs.Youtube.prototype.buffered = function(){
if (this.ytplayer && this.ytplayer.getVideoBytesLoaded) {
var loadedBytes = this.ytplayer.getVideoBytesLoaded();
var totalBytes = this.ytplayer.getVideoBytesTotal();
if (!loadedBytes || !totalBytes) return 0;
var duration = this.ytplayer.getDuration();
var secondsBuffered = (loadedBytes / totalBytes) * duration;
var secondsOffset = (this.ytplayer.getVideoStartBytes() / totalBytes) * duration;
return videojs.createTimeRange(secondsOffset, secondsOffset + secondsBuffered);
} else {
return videojs.createTimeRange(0, 0);
}
};
videojs.Youtube.prototype.supportsFullScreen = function(){ return true; };
// YouTube is supported on all platforms
videojs.Youtube.isSupported = function(){ return true; };
// You can use video/youtube as a media in your HTML5 video to specify the source
videojs.Youtube.canPlaySource = function(srcObj){
return (srcObj.type == 'video/youtube');
};
// Always can control the volume
videojs.Youtube.canControlVolume = function(){ return true; };
////////////////////////////// YouTube specific functions //////////////////////////////
// All videos created before YouTube API is loaded
videojs.Youtube.loadingQueue = [];
// Create the YouTube player
videojs.Youtube.prototype.loadYoutube = function(){
this.ytplayer = new YT.Player(this.id_, {
events: {
onReady: function(e) { e.target.vjsTech.onReady(); },
onStateChange: function(e) { e.target.vjsTech.onStateChange(e.data); },
onPlaybackQualityChange: function(e){ e.target.vjsTech.onPlaybackQualityChange(e.data); },
onError: function(e){ e.target.vjsTech.onError(e.data); }
}
});
this.ytplayer.vjsTech = this;
};
// Transform a JavaScript object into URL params
videojs.Youtube.makeQueryString = function(args){
var array = [];
for (var key in args){
if (args.hasOwnProperty(key)){
array.push(encodeURIComponent(key) + '=' + encodeURIComponent(args[key]));
}
}
return array.join('&');
};
// Called when YouTube API is ready to be used
window.onYouTubeIframeAPIReady = function(){
var yt;
while ((yt = videojs.Youtube.loadingQueue.shift())){
yt.loadYoutube();
}
videojs.Youtube.loadingQueue = [];
videojs.Youtube.apiReady = true;
};
videojs.Youtube.prototype.onReady = function(){
this.isReady_ = true;
this.player_.trigger('apiready');
// Play ASAP if they clicked play before it's ready
if (this.playOnReady) {
this.play();
}
};
videojs.Youtube.prototype.onStateChange = function(state){
if (state != this.lastState){
switch(state){
case -1:
this.player_.trigger('durationchange');
break;
case YT.PlayerState.ENDED:
// Replace YouTube play button by our own
if (!this.player_.options()['ytcontrols']) {
this.player_el_.getElementsByClassName('vjs-poster')[0].style.display = 'block';
this.player_.bigPlayButton.show();
}
this.player_.trigger('ended');
break;
case YT.PlayerState.PLAYING:
// Make sure the big play is not there
this.player_.bigPlayButton.hide();
this.player_.trigger('timeupdate');
this.player_.trigger('durationchange');
this.player_.trigger('playing');
this.player_.trigger('play');
break;
case YT.PlayerState.PAUSED:
this.player_.trigger('pause');
break;
case YT.PlayerState.BUFFERING:
this.player_.trigger('timeupdate');
this.player_.trigger('waiting');
break;
case YT.PlayerState.CUED:
break;
}
this.lastState = state;
}
};
videojs.Youtube.prototype.onPlaybackQualityChange = function(quality){
switch(quality){
case 'medium':
this.player_.videoWidth = 480;
this.player_.videoHeight = 360;
break;
case 'large':
this.player_.videoWidth = 640;
this.player_.videoHeight = 480;
break;
case 'hd720':
this.player_.videoWidth = 960;
this.player_.videoHeight = 720;
break;
case 'hd1080':
this.player_.videoWidth = 1440;
this.player_.videoHeight = 1080;
break;
case 'highres':
this.player_.videoWidth = 1920;
this.player_.videoHeight = 1080;
break;
case 'small':
this.player_.videoWidth = 320;
this.player_.videoHeight = 240;
break;
default:
this.player_.videoWidth = 0;
this.player_.videoHeight = 0;
break;
}
this.player_.trigger('ratechange');
};
videojs.Youtube.prototype.onError = function(error){
this.player_.error = error;
this.player_.trigger('error');
};
// Stretch the YouTube poster
// Keep the iframeblocker in front of the player when the user is inactive
// (ONLY way because the iframe is so selfish with events)
(function() {
var style = document.createElement('style');
style.innerHTML = ' \
.vjs-youtube .vjs-poster { background-size: cover; }\
.iframeblocker { display:none;position:absolute;top:0;left:0;width:100%;height:100%; }\
.vjs-youtube.vjs-user-inactive .iframeblocker { display:block; } \
';
document.head.appendChild(style);
})();