1 /** The minplayer namespace. */
  2 var minplayer = minplayer || {};
  3 
  4 /** All the media player implementations */
  5 minplayer.players = minplayer.players || {};
  6 
  7 /**
  8  * @constructor
  9  * @extends minplayer.players.base
 10  * @class The YouTube media player.
 11  *
 12  * @param {object} context The jQuery context.
 13  * @param {object} options This components options.
 14  * @param {object} queue The event queue to pass events around.
 15  */
 16 minplayer.players.youtube = function(context, options, queue) {
 17 
 18   /** The quality of the YouTube stream. */
 19   this.quality = 'default';
 20 
 21   // Derive from players base.
 22   minplayer.players.base.call(this, context, options, queue);
 23 };
 24 
 25 /** Derive from minplayer.players.base. */
 26 minplayer.players.youtube.prototype = new minplayer.players.base();
 27 
 28 /** Reset the constructor. */
 29 minplayer.players.youtube.prototype.constructor = minplayer.players.youtube;
 30 
 31 /**
 32  * @see minplayer.plugin.construct
 33  * @this minplayer.players.youtube
 34  */
 35 minplayer.players.youtube.prototype.construct = function() {
 36 
 37   // Call the players.flash constructor.
 38   minplayer.players.base.prototype.construct.call(this);
 39 
 40   // Set the plugin name within the options.
 41   this.options.pluginName = 'youtube';
 42 };
 43 
 44 /**
 45  * @see minplayer.players.base#getPriority
 46  * @param {object} file A {@link minplayer.file} object.
 47  * @return {number} The priority of this media player.
 48  */
 49 minplayer.players.youtube.getPriority = function(file) {
 50   return 10;
 51 };
 52 
 53 /**
 54  * @see minplayer.players.base#canPlay
 55  * @return {boolean} If this player can play this media type.
 56  */
 57 minplayer.players.youtube.canPlay = function(file) {
 58 
 59   // Check for the mimetype for youtube.
 60   if (file.mimetype === 'video/youtube') {
 61     return true;
 62   }
 63 
 64   // If the path is a YouTube path, then return true.
 65   return (file.path.search(/^http(s)?\:\/\/(www\.)?youtube\.com/i) === 0);
 66 };
 67 
 68 /**
 69  * Return the ID for a provided media file.
 70  *
 71  * @param {object} file A {@link minplayer.file} object.
 72  * @return {string} The ID for the provided media.
 73  */
 74 minplayer.players.youtube.getMediaId = function(file) {
 75   var reg = /^http[s]?\:\/\/(www\.)?youtube\.com\/watch\?v=([a-zA-Z0-9_\-]+)/i;
 76   if (file.path.search(reg) === 0) {
 77     return file.path.match(reg)[2];
 78   }
 79   else {
 80     return file.path;
 81   }
 82 };
 83 
 84 /**
 85  * Returns a preview image for this media player.
 86  *
 87  * @param {object} file A {@link minplayer.file} object.
 88  * @param {string} type The type of image.
 89  * @param {function} callback Called when the image is retrieved.
 90  */
 91 minplayer.players.youtube.getImage = function(file, type, callback) {
 92   type = (type == 'thumbnail') ? '1' : '0';
 93   callback('http://img.youtube.com/vi/' + file.id + '/' + type + '.jpg');
 94 };
 95 
 96 /**
 97  * Translates the player state for the YouTube API player.
 98  *
 99  * @param {number} playerState The YouTube player state.
100  */
101 minplayer.players.youtube.prototype.setPlayerState = function(playerState) {
102   switch (playerState) {
103     case YT.PlayerState.CUED:
104       break;
105     case YT.PlayerState.BUFFERING:
106       this.onWaiting();
107       break;
108     case YT.PlayerState.PAUSED:
109       this.onPaused();
110       break;
111     case YT.PlayerState.PLAYING:
112       this.onPlaying();
113       break;
114     case YT.PlayerState.ENDED:
115       this.onComplete();
116       break;
117     default:
118       break;
119   }
120 };
121 
122 /**
123  * Called when an error occurs.
124  *
125  * @param {string} event The onReady event that was triggered.
126  */
127 minplayer.players.youtube.prototype.onReady = function(event) {
128   minplayer.players.base.prototype.onReady.call(this);
129   if (!this.options.autoplay) {
130     this.pause();
131   }
132   this.onLoaded();
133 };
134 
135 /**
136  * Checks to see if this player can be found.
137  * @return {bool} TRUE - Player is found, FALSE - otherwise.
138  */
139 minplayer.players.youtube.prototype.playerFound = function() {
140   var id = 'iframe#' + this.options.id + '-player.youtube-player';
141   var iframe = this.display.find(id);
142   return (iframe.length > 0);
143 };
144 
145 /**
146  * Called when the player state changes.
147  *
148  * @param {object} event A JavaScript Event.
149  */
150 minplayer.players.youtube.prototype.onPlayerStateChange = function(event) {
151   this.setPlayerState(event.data);
152 };
153 
154 /**
155  * Called when the player quality changes.
156  *
157  * @param {string} newQuality The new quality for the change.
158  */
159 minplayer.players.youtube.prototype.onQualityChange = function(newQuality) {
160   this.quality = newQuality.data;
161 };
162 
163 /**
164  * Determines if the player should show the playloader.
165  *
166  * @param {string} preview The preview image.
167  * @return {bool} If this player implements its own playLoader.
168  */
169 minplayer.players.youtube.prototype.hasPlayLoader = function(preview) {
170   return minplayer.hasTouch || !preview;
171 };
172 
173 /**
174  * Determines if the player should show the controller.
175  *
176  * @return {bool} If this player implements its own playLoader.
177  */
178 minplayer.players.youtube.prototype.hasController = function() {
179   return minplayer.isIDevice;
180 };
181 
182 /**
183  * @see minplayer.players.base#create
184  * @return {object} The media player entity.
185  */
186 minplayer.players.youtube.prototype.create = function() {
187   minplayer.players.base.prototype.create.call(this);
188 
189   // Insert the YouTube iframe API player.
190   var tag = document.createElement('script');
191   tag.src = 'https://www.youtube.com/player_api';
192   var firstScriptTag = document.getElementsByTagName('script')[0];
193   firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
194 
195   // Get the player ID.
196   this.playerId = this.options.id + '-player';
197 
198   // Poll until the YouTube API is ready.
199   this.poll((function(player) {
200     return function() {
201       var ready = jQuery('#' + player.playerId).length > 0;
202       ready = ready && ('YT' in window);
203       ready = ready && (typeof YT.Player == 'function');
204       if (ready) {
205         // Determine the origin of this script.
206         jQuery('#' + player.playerId).addClass('youtube-player');
207         var origin = location.protocol;
208         origin += '//' + location.hostname;
209         origin += (location.port && ':' + location.port);
210 
211         var playerVars = {};
212         if (minplayer.isIDevice) {
213           playerVars.origin = origin;
214         }
215         else {
216           playerVars = {
217             enablejsapi: minplayer.isIDevice ? 0 : 1,
218             origin: origin,
219             wmode: 'opaque',
220             controls: minplayer.isAndroid ? 1 : 0
221           };
222         }
223 
224         // Create the player.
225         player.player = new YT.Player(player.playerId, {
226           height: '100%',
227           width: '100%',
228           frameborder: 0,
229           videoId: player.mediaFile.id,
230           playerVars: playerVars,
231           events: {
232             'onReady': function(event) {
233               player.onReady(event);
234             },
235             'onStateChange': function(event) {
236               player.onPlayerStateChange(event);
237             },
238             'onPlaybackQualityChange': function(newQuality) {
239               player.onQualityChange(newQuality);
240             },
241             'onError': function(errorCode) {
242               player.onError(errorCode);
243             }
244           }
245         });
246       }
247       return !ready;
248     };
249   })(this), 200);
250 
251   // Return the player.
252   return jQuery(document.createElement('div')).attr({
253     id: this.playerId
254   });
255 };
256 
257 /**
258  * @see minplayer.players.base#load
259  * @return {boolean} If this action was performed.
260  */
261 minplayer.players.youtube.prototype.load = function(file) {
262   if (minplayer.players.base.prototype.load.call(this, file)) {
263     this.player.loadVideoById(file.id, 0, this.quality);
264     return true;
265   }
266 
267   return false;
268 };
269 
270 /**
271  * @see minplayer.players.base#play
272  * @return {boolean} If this action was performed.
273  */
274 minplayer.players.youtube.prototype.play = function() {
275   if (minplayer.players.base.prototype.play.call(this)) {
276     this.onWaiting();
277     this.player.playVideo();
278     return true;
279   }
280 
281   return false;
282 };
283 
284 /**
285  * @see minplayer.players.base#pause
286  * @return {boolean} If this action was performed.
287  */
288 minplayer.players.youtube.prototype.pause = function() {
289   if (minplayer.players.base.prototype.pause.call(this)) {
290     this.player.pauseVideo();
291     return true;
292   }
293 
294   return false;
295 };
296 
297 /**
298  * @see minplayer.players.base#stop
299  * @return {boolean} If this action was performed.
300  */
301 minplayer.players.youtube.prototype.stop = function() {
302   if (minplayer.players.base.prototype.stop.call(this)) {
303     this.player.stopVideo();
304     return true;
305   }
306 
307   return false;
308 };
309 
310 /**
311  * @see minplayer.players.base#seek
312  * @return {boolean} If this action was performed.
313  */
314 minplayer.players.youtube.prototype.seek = function(pos) {
315   if (minplayer.players.base.prototype.seek.call(this, pos)) {
316     this.onWaiting();
317     this.player.seekTo(pos, true);
318     return true;
319   }
320 
321   return false;
322 };
323 
324 /**
325  * @see minplayer.players.base#setVolume
326  * @return {boolean} If this action was performed.
327  */
328 minplayer.players.youtube.prototype.setVolume = function(vol) {
329   if (minplayer.players.base.prototype.setVolume.call(this, vol)) {
330     this.player.setVolume(vol * 100);
331     return true;
332   }
333 
334   return false;
335 };
336 
337 /**
338  * @see minplayer.players.base#getVolume
339  */
340 minplayer.players.youtube.prototype.getVolume = function(callback) {
341   this.getValue('getVolume', callback);
342 };
343 
344 /**
345  * @see minplayer.players.base#getDuration.
346  */
347 minplayer.players.youtube.prototype.getDuration = function(callback) {
348   this.getValue('getDuration', callback);
349 };
350 
351 /**
352  * @see minplayer.players.base#getCurrentTime
353  */
354 minplayer.players.youtube.prototype.getCurrentTime = function(callback) {
355   this.getValue('getCurrentTime', callback);
356 };
357 
358 /**
359  * @see minplayer.players.base#getBytesStart.
360  */
361 minplayer.players.youtube.prototype.getBytesStart = function(callback) {
362   this.getValue('getVideoStartBytes', callback);
363 };
364 
365 /**
366  * @see minplayer.players.base#getBytesLoaded.
367  */
368 minplayer.players.youtube.prototype.getBytesLoaded = function(callback) {
369   this.getValue('getVideoBytesLoaded', callback);
370 };
371 
372 /**
373  * @see minplayer.players.base#getBytesTotal.
374  */
375 minplayer.players.youtube.prototype.getBytesTotal = function(callback) {
376   this.getValue('getVideoBytesTotal', callback);
377 };
378