      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