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