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  *
 47  * @param {object} file A {@link minplayer.file} object.
 48  * @return {number} The priority of this media player.
 49  */
 50 minplayer.players.youtube.getPriority = function(file) {
 51   return 10;
 52 };
 53 
 54 /**
 55  * @see minplayer.players.base#canPlay
 56  *
 57  * @param {object} file A {@link minplayer.file} object.
 58  * @return {boolean} If this player can play this media type.
 59  */
 60 minplayer.players.youtube.canPlay = function(file) {
 61 
 62   // Check for the mimetype for youtube.
 63   if (file.mimetype === 'video/youtube') {
 64     return true;
 65   }
 66 
 67   // If the path is a YouTube path, then return true.
 68   var regex = /^http(s)?\:\/\/(www\.)?(youtube\.com|youtu\.be)/i;
 69   return (file.path.search(regex) === 0);
 70 };
 71 
 72 /**
 73  * Return the ID for a provided media file.
 74  *
 75  * @param {object} file A {@link minplayer.file} object.
 76  * @return {string} The ID for the provided media.
 77  */
 78 minplayer.players.youtube.getMediaId = function(file) {
 79   var regex = '^http[s]?\\:\\/\\/(www\\.)?';
 80   regex += '(youtube\\.com\\/watch\\?v=|youtu\\.be\\/)';
 81   regex += '([a-zA-Z0-9_\\-]+)';
 82   var reg = RegExp(regex, 'i');
 83 
 84   // Locate the media id.
 85   if (file.path.search(reg) === 0) {
 86     return file.path.match(reg)[3];
 87   }
 88   else {
 89     return file.path;
 90   }
 91 };
 92 
 93 /**
 94  * Returns a preview image for this media player.
 95  *
 96  * @param {object} file A {@link minplayer.file} object.
 97  * @param {string} type The type of image.
 98  * @param {function} callback Called when the image is retrieved.
 99  */
100 minplayer.players.youtube.getImage = function(file, type, callback) {
101   type = (type === 'thumbnail') ? '1' : '0';
102   callback('https://img.youtube.com/vi/' + file.id + '/' + type + '.jpg');
103 };
104 
105 /**
106  * Parse a single playlist node.
107  *
108  * @param {object} item The youtube item.
109  * @return {object} The mediafront node.
110  */
111 minplayer.players.youtube.parseNode = function(item) {
112   var node = (typeof item.video !== 'undefined') ? item.video : item;
113   return {
114     title: node.title,
115     description: node.description,
116     mediafiles: {
117       image: {
118         'thumbnail': {
119           path: node.thumbnail.sqDefault
120         },
121         'image': {
122           path: node.thumbnail.hqDefault
123         }
124       },
125       media: {
126         'media': {
127           player: 'youtube',
128           id: node.id
129         }
130       }
131     }
132   };
133 };
134 
135 /**
136  * Returns information about this youtube video.
137  *
138  * @param {object} file The file to load.
139  * @param {function} callback Called when the node is loaded.
140  */
141 minplayer.players.youtube.getNode = function(file, callback) {
142   var url = 'https://gdata.youtube.com/feeds/api/videos/' + file.id;
143   url += '?v=2&alt=jsonc';
144   jQuery.get(url, function(data) {
145     callback(minplayer.players.youtube.parseNode(data.data));
146   });
147 };
148 
149 /**
150  * Translates the player state for the YouTube API player.
151  *
152  * @param {number} playerState The YouTube player state.
153  */
154 minplayer.players.youtube.prototype.setPlayerState = function(playerState) {
155   switch (playerState) {
156     case YT.PlayerState.CUED:
157       break;
158     case YT.PlayerState.BUFFERING:
159       this.onWaiting();
160       break;
161     case YT.PlayerState.PAUSED:
162       this.onPaused();
163       break;
164     case YT.PlayerState.PLAYING:
165       this.onPlaying();
166       break;
167     case YT.PlayerState.ENDED:
168       this.onComplete();
169       break;
170     default:
171       break;
172   }
173 };
174 
175 /**
176  * Called when an error occurs.
177  *
178  * @param {string} event The onReady event that was triggered.
179  */
180 minplayer.players.youtube.prototype.onReady = function(event) {
181   minplayer.players.base.prototype.onReady.call(this);
182   if (!this.options.autoplay) {
183     this.pause();
184   }
185   this.onLoaded();
186 };
187 
188 /**
189  * Checks to see if this player can be found.
190  * @return {bool} TRUE - Player is found, FALSE - otherwise.
191  */
192 minplayer.players.youtube.prototype.playerFound = function() {
193   var id = 'iframe#' + this.options.id + '-player.youtube-player';
194   var iframe = this.display.find(id);
195   return (iframe.length > 0);
196 };
197 
198 /**
199  * Called when the player state changes.
200  *
201  * @param {object} event A JavaScript Event.
202  */
203 minplayer.players.youtube.prototype.onPlayerStateChange = function(event) {
204   this.setPlayerState(event.data);
205 };
206 
207 /**
208  * Called when the player quality changes.
209  *
210  * @param {string} newQuality The new quality for the change.
211  */
212 minplayer.players.youtube.prototype.onQualityChange = function(newQuality) {
213   this.quality = newQuality.data;
214 };
215 
216 /**
217  * Determines if the player should show the playloader.
218  *
219  * @param {string} preview The preview image.
220  * @return {bool} If this player implements its own playLoader.
221  */
222 minplayer.players.youtube.prototype.hasPlayLoader = function(preview) {
223   return minplayer.hasTouch || !preview;
224 };
225 
226 /**
227  * Determines if the player should show the controller.
228  *
229  * @return {bool} If this player implements its own playLoader.
230  */
231 minplayer.players.youtube.prototype.hasController = function() {
232   return minplayer.isIDevice;
233 };
234 
235 /**
236  * @see minplayer.players.base#create
237  * @return {object} The media player entity.
238  */
239 minplayer.players.youtube.prototype.createPlayer = function() {
240   minplayer.players.base.prototype.createPlayer.call(this);
241 
242   // Insert the YouTube iframe API player.
243   var youtube_script = 'https://www.youtube.com/player_api';
244   if (jQuery('script[src="' + youtube_script + '"]').length === 0) {
245     var tag = document.createElement('script');
246     tag.src = youtube_script;
247     var firstScriptTag = document.getElementsByTagName('script')[0];
248     firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
249   }
250 
251   // Get the player ID.
252   this.playerId = this.options.id + '-player';
253 
254   // Poll until the YouTube API is ready.
255   this.poll(this.options.id + '_youtube', (function(player) {
256     return function() {
257       var ready = jQuery('#' + player.playerId).length > 0;
258       ready = ready && ('YT' in window);
259       ready = ready && (typeof YT.Player === 'function');
260       if (ready) {
261         // Determine the origin of this script.
262         jQuery('#' + player.playerId).addClass('youtube-player');
263         var origin = location.protocol;
264         origin += '//' + location.hostname;
265         origin += (location.port && ':' + location.port);
266 
267         var playerVars = {};
268         if (minplayer.isIDevice) {
269           playerVars.origin = origin;
270         }
271         else {
272           playerVars = {
273             enablejsapi: minplayer.isIDevice ? 0 : 1,
274             origin: origin,
275             wmode: 'opaque',
276             controls: minplayer.isAndroid ? 1 : 0,
277             rel: 0,
278             showinfo: 0
279           };
280         }
281 
282         // Create the player.
283         player.player = new YT.Player(player.playerId, {
284           height: '100%',
285           width: '100%',
286           frameborder: 0,
287           videoId: player.mediaFile.id,
288           playerVars: playerVars,
289           events: {
290             'onReady': function(event) {
291               player.onReady(event);
292             },
293             'onStateChange': function(event) {
294               player.onPlayerStateChange(event);
295             },
296             'onPlaybackQualityChange': function(newQuality) {
297               player.onQualityChange(newQuality);
298             },
299             'onError': function(errorCode) {
300               player.onError(errorCode);
301             }
302           }
303         });
304       }
305       return !ready;
306     };
307   })(this), 200);
308 
309   // Return the player.
310   return jQuery(document.createElement('div')).attr({
311     id: this.playerId
312   });
313 };
314 
315 /**
316  * @see minplayer.players.base#load
317  */
318 minplayer.players.youtube.prototype.load = function(file, callback) {
319   minplayer.players.base.prototype.load.call(this, file, function() {
320     this.player.loadVideoById(file.id, 0, this.quality);
321     if (callback) {
322       callback.call(this);
323     }
324   });
325 };
326 
327 /**
328  * @see minplayer.players.base#play
329  */
330 minplayer.players.youtube.prototype.play = function(callback) {
331   minplayer.players.base.prototype.play.call(this, function() {
332     this.onWaiting();
333     this.player.playVideo();
334     if (callback) {
335       callback.call(this);
336     }
337   });
338 };
339 
340 /**
341  * @see minplayer.players.base#pause
342  */
343 minplayer.players.youtube.prototype.pause = function(callback) {
344   minplayer.players.base.prototype.pause.call(this, function() {
345     this.player.pauseVideo();
346     if (callback) {
347       callback.call(this);
348     }
349   });
350 };
351 
352 /**
353  * @see minplayer.players.base#stop
354  */
355 minplayer.players.youtube.prototype.stop = function(callback) {
356   minplayer.players.base.prototype.stop.call(this, function() {
357     this.player.stopVideo();
358     if (callback) {
359       callback.call(this);
360     }
361   });
362 };
363 
364 /**
365  * @see minplayer.players.base#_seek
366  */
367 minplayer.players.youtube.prototype._seek = function(pos) {
368   this.onWaiting();
369   this.player.seekTo(pos, true);
370 };
371 
372 /**
373  * @see minplayer.players.base#setVolume
374  */
375 minplayer.players.youtube.prototype.setVolume = function(vol, callback) {
376   minplayer.players.base.prototype.setVolume.call(this, vol, function() {
377     this.player.setVolume(vol * 100);
378     if (callback) {
379       callback.call(this);
380     }
381   });
382 };
383 
384 /**
385  * @see minplayer.players.base#getVolume
386  */
387 minplayer.players.youtube.prototype._getVolume = function(callback) {
388   callback(this.player.getVolume());
389 };
390 
391 /**
392  * @see minplayer.players.base#getDuration.
393  */
394 minplayer.players.youtube.prototype._getDuration = function(callback) {
395   callback(this.player.getDuration());
396 };
397 
398 /**
399  * @see minplayer.players.base#getCurrentTime
400  */
401 minplayer.players.youtube.prototype._getCurrentTime = function(callback) {
402   callback(this.player.getCurrentTime());
403 };
404 
405 /**
406  * @see minplayer.players.base#getBytesStart.
407  */
408 minplayer.players.youtube.prototype._getBytesStart = function(callback) {
409   callback(this.player.getVideoStartBytes());
410 };
411 
412 /**
413  * @see minplayer.players.base#getBytesLoaded.
414  */
415 minplayer.players.youtube.prototype._getBytesLoaded = function(callback) {
416   callback(this.player.getVideoBytesLoaded());
417 };
418 
419 /**
420  * @see minplayer.players.base#getBytesTotal.
421  */
422 minplayer.players.youtube.prototype._getBytesTotal = function(callback) {
423   callback(this.player.getVideoBytesTotal());
424 };
425