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