Skip to content
Browse files

[#1254] Youtube player using the wrapper.

  • Loading branch information...
1 parent fc7fb67 commit 56bc05a8c0acecf145648b9d264fdd3facbb5241 @ScottDowne committed Jan 14, 2013
View
418 players/youtube/popcorn.youtube.js
@@ -1,411 +1,31 @@
(function( window, Popcorn ) {
- var videoIdRegex = new RegExp( "(?:http://www\\.|http://|www\\.|\\.|^)(?:youtu).*(?:/|v=)(.{11})" );
- // A global callback for youtube... that makes me angry
- window.onYouTubePlayerAPIReady = function() {
- onYouTubePlayerAPIReady.ready = true;
- for ( var i = 0; i < onYouTubePlayerAPIReady.waiting.length; i++ ) {
- onYouTubePlayerAPIReady.waiting[ i ]();
- }
+ var canPlayType = function( nodeName, url ) {
+ return ( typeof url === "string" &&
+ Popcorn.HTMLYouTubeVideoElement._canPlaySrc( url ) );
};
- // existing youtube references can break us.
- // remove it and use the one we can trust.
- if ( window.YT ) {
- window.quarantineYT = window.YT;
- window.YT = null;
- }
-
- onYouTubePlayerAPIReady.waiting = [];
-
- var _loading = false;
-
Popcorn.player( "youtube", {
- _canPlayType: function( nodeName, url ) {
- return typeof url === "string" && videoIdRegex.test( url ) && nodeName.toLowerCase() !== "video";
- },
- _setup: function( options ) {
- if ( !window.YT && !_loading ) {
- _loading = true;
- Popcorn.getScript( "//youtube.com/player_api" );
- }
-
- var media = this,
- autoPlay = false,
- container = document.createElement( "div" ),
- currentTime = 0,
- paused = true,
- seekTime = 0,
- firstGo = true,
- seeking = false,
- fragmentStart = 0,
-
- // state code for volume changed polling
- lastMuted = false,
- lastVolume = 100,
- playerQueue = Popcorn.player.playerQueue();
-
- var createProperties = function() {
-
- Popcorn.player.defineProperty( media, "currentTime", {
- set: function( val ) {
-
- if ( options.destroyed ) {
- return;
- }
-
- val = Number( val );
-
- if ( isNaN ( val ) ) {
- return;
- }
-
- currentTime = val;
-
- seeking = true;
- media.dispatchEvent( "seeking" );
-
- options.youtubeObject.seekTo( val );
- },
- get: function() {
-
- return currentTime;
- }
- });
-
- Popcorn.player.defineProperty( media, "paused", {
- get: function() {
-
- return paused;
- }
- });
-
- Popcorn.player.defineProperty( media, "muted", {
- set: function( val ) {
-
- if ( options.destroyed ) {
-
- return val;
- }
-
- if ( options.youtubeObject.isMuted() !== val ) {
-
- if ( val ) {
-
- options.youtubeObject.mute();
- } else {
-
- options.youtubeObject.unMute();
- }
-
- lastMuted = options.youtubeObject.isMuted();
- media.dispatchEvent( "volumechange" );
- }
-
- return options.youtubeObject.isMuted();
- },
- get: function() {
-
- if ( options.destroyed ) {
-
- return 0;
- }
-
- return options.youtubeObject.isMuted();
- }
- });
-
- Popcorn.player.defineProperty( media, "volume", {
- set: function( val ) {
-
- if ( options.destroyed ) {
-
- return val;
- }
-
- if ( options.youtubeObject.getVolume() / 100 !== val ) {
-
- options.youtubeObject.setVolume( val * 100 );
- lastVolume = options.youtubeObject.getVolume();
- media.dispatchEvent( "volumechange" );
- }
-
- return options.youtubeObject.getVolume() / 100;
- },
- get: function() {
-
- if ( options.destroyed ) {
-
- return 0;
- }
-
- return options.youtubeObject.getVolume() / 100;
- }
- });
-
- media.play = function() {
-
- if ( options.destroyed ) {
-
- return;
- }
-
- paused = false;
- playerQueue.add(function() {
-
- if ( options.youtubeObject.getPlayerState() !== 1 ) {
-
- seeking = false;
- options.youtubeObject.playVideo();
- } else {
- playerQueue.next();
- }
- });
- };
-
- media.pause = function() {
-
- if ( options.destroyed ) {
-
- return;
- }
-
- paused = true;
- playerQueue.add(function() {
-
- if ( options.youtubeObject.getPlayerState() !== 2 ) {
-
- options.youtubeObject.pauseVideo();
- } else {
- playerQueue.next();
- }
- });
- };
- };
-
- container.id = media.id + Popcorn.guid();
- options._container = container;
- media.appendChild( container );
-
- var youtubeInit = function() {
-
- var src, query, params, playerVars, queryStringItem, firstPlay = true, seekEps = 0.1;
-
- var timeUpdate = function() {
-
- if ( options.destroyed ) {
- return;
- }
-
- var ytTime = options.youtubeObject.getCurrentTime();
-
- if ( !seeking ) {
- currentTime = ytTime;
- } else if ( currentTime >= ytTime - seekEps && currentTime <= ytTime + seekEps ) {
- seeking = false;
- seekEps = 0.1;
- media.dispatchEvent( "seeked" );
- } else {
- // seek didn't work very well, try again with higher tolerance
- seekEps *= 2;
- options.youtubeObject.seekTo( currentTime );
- }
-
- media.dispatchEvent( "timeupdate" );
-
- setTimeout( timeUpdate, 200 );
- };
-
- // delay is in seconds
- var fetchDuration = function( delay ) {
- var ytDuration = options.youtubeObject.getDuration();
-
- if ( isNaN( ytDuration ) || ytDuration === 0 ) {
- setTimeout( function() {
- fetchDuration( delay * 2 );
- }, delay*1000 );
- } else {
- // set duration and dispatch ready events
- media.duration = ytDuration;
- media.dispatchEvent( "durationchange" );
-
- media.dispatchEvent( "loadedmetadata" );
- media.dispatchEvent( "loadeddata" );
-
- media.readyState = 4;
-
- timeUpdate();
-
- media.dispatchEvent( "canplay" );
- media.dispatchEvent( "canplaythrough" );
- }
- };
-
- // Default controls to off
- options.controls = +options.controls === 0 || +options.controls === 1 ? options.controls : 0;
- options.annotations = +options.annotations === 1 || +options.annotations === 3 ? options.annotations : 1;
-
- src = videoIdRegex.exec( media.src )[ 1 ];
-
- query = ( media.src.split( "?" )[ 1 ] || "" )
- .replace( /v=.{11}/, "" );
- query = query.replace( /&t=(?:(\d+)m)?(?:(\d+)s)?/, function( all, minutes, seconds ) {
-
- // Make sure we have real zeros
- minutes = minutes | 0; // bit-wise OR
- seconds = seconds | 0; // bit-wise OR
-
- fragmentStart = ( +seconds + ( minutes * 60 ) );
- return "";
- });
- query = query.replace( /&start=(\d+)?/, function( all, seconds ) {
-
- // Make sure we have real zeros
- seconds = seconds | 0; // bit-wise OR
-
- fragmentStart = seconds;
- return "";
- });
-
- autoPlay = ( /autoplay=1/.test( query ) );
-
- params = query.split( /[\&\?]/g );
- playerVars = { wmode: "transparent" };
-
- for( var i = 0; i < params.length; i++ ) {
- queryStringItem = params[ i ].split( "=" );
- playerVars[ queryStringItem[ 0 ] ] = queryStringItem[ 1 ];
- }
-
- // Don't show related videos when ending
- playerVars.rel = playerVars.rel || 0;
-
- // Don't show YouTube's branding
- playerVars.modestbranding = playerVars.modestbranding || 1;
-
- // Don't show annotations by default
- playerVars.iv_load_policy = playerVars.iv_load_policy || 3;
-
- // Don't show video info before playing
- playerVars.showinfo = playerVars.showinfo || 0;
-
- // Show/hide controls.
- playerVars.controls = playerVars.controls || ( options.controls || 0 );
-
- options.youtubeObject = new YT.Player( container.id, {
- height: "100%",
- width: "100%",
- wmode: "transparent",
- playerVars: playerVars,
- videoId: src,
- events: {
- "onReady": function(){
-
- // pulling initial volume states form baseplayer
- lastVolume = media.volume;
- lastMuted = media.muted;
-
- volumeupdate();
-
- paused = media.paused;
- createProperties();
- options.youtubeObject.playVideo();
-
- media.currentTime = fragmentStart;
-
- media.dispatchEvent( "loadstart" );
-
- // wait to dispatch ready events until we get a duration
- },
- "onStateChange": function( state ){
-
- if ( options.destroyed || state.data === -1 ) {
- return;
- }
-
- // state.data === 2 is for pause events
- // state.data === 1 is for play events
- if ( state.data === 2 ) {
- paused = true;
- media.dispatchEvent( "pause" );
- playerQueue.next();
- } else if ( state.data === 1 && !firstPlay ) {
- paused = false;
- media.dispatchEvent( "play" );
- media.dispatchEvent( "playing" );
- playerQueue.next();
- } else if ( state.data === 0 ) {
- media.dispatchEvent( "ended" );
- } else if ( state.data === 1 && firstPlay ) {
- firstPlay = false;
-
- // pulling initial paused state from autoplay or the baseplayer
- // also need to explicitly set to paused otherwise.
- if ( autoPlay || !media.paused ) {
- paused = false;
- }
-
- if ( paused ) {
- options.youtubeObject.pauseVideo();
- }
-
- fetchDuration( 0.025 );
- }
- },
- "onError": function( error ) {
-
- if ( [ 2, 100, 101, 150 ].indexOf( error.data ) !== -1 ) {
- media.error = {
- customCode: error.data
- };
- media.dispatchEvent( "error" );
- }
- }
- }
- });
-
- var volumeupdate = function() {
-
- if ( options.destroyed ) {
-
- return;
- }
-
- if ( lastMuted !== options.youtubeObject.isMuted() ) {
-
- lastMuted = options.youtubeObject.isMuted();
- media.dispatchEvent( "volumechange" );
- }
-
- if ( lastVolume !== options.youtubeObject.getVolume() ) {
-
- lastVolume = options.youtubeObject.getVolume();
- media.dispatchEvent( "volumechange" );
- }
-
- setTimeout( volumeupdate, 250 );
- };
- };
+ _canPlayType: canPlayType
+ });
- if ( onYouTubePlayerAPIReady.ready ) {
+ Popcorn.youtube = function( container, url, options ) {
+ if ( typeof console !== "undefined" && console.warn ) {
+ console.warn( "Deprecated player 'youtube'. Please use Popcorn.HTMLYouTubeVideoElement directly." );
+ }
- youtubeInit();
- } else {
+ var media = Popcorn.HTMLYouTubeVideoElement( container ),
+ popcorn = Popcorn( media, options );
- onYouTubePlayerAPIReady.waiting.push( youtubeInit );
- }
- },
- _teardown: function( options ) {
+ // Set the src "soon" but return popcorn instance first, so
+ // the caller can get get error events.
+ setTimeout( function() {
+ media.src = url;
+ }, 0 );
- options.destroyed = true;
+ return popcorn;
+ };
- var youtubeObject = options.youtubeObject;
- if( youtubeObject ){
- youtubeObject.stopVideo();
- youtubeObject.clearVideo && youtubeObject.clearVideo();
- }
+ Popcorn.youtube.canPlayType = canPlayType;
- this.removeChild( document.getElementById( options._container.id ) );
- }
- });
}( window, Popcorn ));
View
4 players/youtube/popcorn.youtube.unit.html
@@ -12,6 +12,8 @@
-->
<script src="../../popcorn.js"></script>
<script src="../../modules/player/popcorn.player.js"></script>
+ <script src="../../wrappers/common/popcorn._MediaElementProto.js"></script>
+ <script src="../../wrappers/youtube/popcorn.HTMLYouTubeVideoElement.js"></script>
<script src="popcorn.youtube.js"></script>
<script src="popcorn.youtube.unit.js"></script>
<script src="../../test/inject.js"></script>
@@ -41,7 +43,7 @@ <h2 id="qunit-userAgent"></h2>
<div id="video" style="width: 360px; height: 300px" ></div>
<div id="video2" style="width: 360px; height: 300px" ></div>
<div id="video3" style="width: 360px; height: 300px" ></div>
- <div id="video4"></div>
+ <div id="video4" style="width: 400px; height: 400px" ></div>
<div id="video6" style="width: 360px; height: 300px" ></div>
<div id="video7" style="width: 360px; height: 300px" ></div>
<div id="video8" style="width: 360px; height: 300px" ></div>
View
164 players/youtube/popcorn.youtube.unit.js
@@ -440,7 +440,7 @@ asyncTest( "Popcorn YouTube Plugin Url Regex Test", function() {
asyncTest( "Player height and width", function() {
- expect( 2 );
+ expect( 4 );
var popcorn = Popcorn.youtube( "#video4", "http://www.youtube.com/watch?v=nfGV32RNkhw" );
var readyStatePoll = function() {
@@ -450,10 +450,15 @@ asyncTest( "Player height and width", function() {
setTimeout( readyStatePoll, 10 );
} else {
- equal( popcorn.media.children[ 0 ].width, "100%",
- "Youtube player width is 100%" );
- equal( popcorn.media.children[ 0 ].height, "100%",
- "Youtube player height is 100%" );
+ equal( popcorn.media.width, "400",
+ "Youtube player width is 400" );
+ equal( popcorn.media.height, "400",
+ "Youtube player height is 400" );
+
+ equal( popcorn.media.offsetWidth, "400",
+ "Youtube player offset width is 400" );
+ equal( popcorn.media.offsetHeight, "400",
+ "Youtube player offset height is 400" );
popcorn.destroy();
start();
}
@@ -468,17 +473,15 @@ asyncTest( "Player Errors", function() {
expect( 2 );
- var pop = Popcorn.youtube( "#video4", "http://www.youtube.com/watch?v=abcdefghijk", {
- events: {
- error: function() {
+ var pop = Popcorn.youtube( "#video4", "http://www.youtube.com/watch?v=abcdefghijk" );
- ok( true, "error trigger by invalid URL" );
- equal( pop.error.customCode, 100, "error.customCode is 100 for invalid URL" );
- pop.destroy();
- start();
- }
- }
- });
+ pop.on( "error", function() {
+
+ ok( pop.error, "error trigger by invalid URL" );
+ equal( pop.error.code, 2, "Error code 2" );
+ pop.destroy();
+ start();
+ } );
});
asyncTest( "YouTube ended event", function() {
@@ -499,33 +502,10 @@ asyncTest( "YouTube ended event", function() {
pop.play();
});
-asyncTest( "youtube player gets a proper _teardown", function() {
-
- var count = 0,
- expects = 1;
-
- function plus() {
- if ( ++count === expects ) {
-
- start();
- }
- }
-
- expect( expects );
-
- var popcorn = Popcorn.youtube( "#video9", "http://www.youtube.com/watch?v=nfGV32RNkhw" );
- popcorn.on( "loadeddata", function() {
-
- popcorn.destroy();
- equal( popcorn.media.children.length, 0, "" );
- plus();
- });
-});
-
asyncTest( "Youtube ready state events", function() {
var popped,
- expects = 4,
+ expects = 3,
count = 0,
state = 0;
@@ -540,108 +520,22 @@ asyncTest( "Youtube ready state events", function() {
expect( expects );
- var popcorn = Popcorn.youtube( "#video9", "http://www.youtube.com/watch?v=nfGV32RNkhw" );
- popcorn.on( "loadeddata", function() {
+ popped = Popcorn.youtube( "#video6", "http://www.youtube.com/watch?v=nfGV32RNkhw" );
+ popped.on( "canplaythrough", function() {
- popcorn.destroy();
- equal( popcorn.media.children.length, 0, "" );
+ equal( state++, 2, "canplaythrough fired first" );
plus();
});
+ popped.on( "loadedmetadata", function() {
- popped = Popcorn.youtube( "#video6", "http://www.youtube.com/watch?v=nfGV32RNkhw", {
- events: {
- canplaythrough: function( e ) {
-
- equal( state++, 2, "canplaythrough fired first" );
- plus();
- },
- loadedmetadata: function( e ) {
-
- equal( state++, 0, "loadedmetadata fired third" );
- plus();
- },
- loadeddata: function( e ) {
-
- equal( state++, 1, "loadeddata fired last" );
- plus();
- }
- }
+ equal( state++, 0, "loadedmetadata fired third" );
+ plus();
});
+ popped.on( "loadeddata", function() {
-});
-
-asyncTest( "Youtube media start time fragment", function() {
-
- var popcorn1, popcorn2, popcorn3, popcorn4,
- count = 0, expects = 4,
- // Youtube's fragment can be off by give or take a second.
- epsilon = 1;
-
- expect( expects );
-
- function plus() {
- if ( ++count === expects ) {
-
- popcorn1.destroy();
- popcorn2.destroy();
- popcorn3.destroy();
- popcorn4.destroy();
- start();
- }
- }
-
- var firstTest = function() {
-
- popcorn1.off( "loadeddata", firstTest );
- ok( Math.ceil( popcorn1.currentTime() ) + epsilon >= 130, "youtube fragment works with &start=130" );
- plus();
- },
- secondTest = function() {
-
- popcorn2.off( "loadeddata", secondTest );
- ok( Math.ceil( popcorn2.currentTime() ) + epsilon >= 130, "youtube fragment works with &t=2m10s" );
- plus();
- },
- thirdTest = function() {
-
- popcorn3.off( "loadeddata", thirdTest );
- ok( Math.ceil( popcorn3.currentTime() ) + epsilon >= 120, "youtube fragment works with &t=2m" );
- plus();
- },
- fourthTest = function() {
-
- popcorn4.off( "loadeddata", fourthTest );
- ok( Math.ceil( popcorn4.currentTime() )+ epsilon >= 10, "youtube fragment works with &t=10s" );
- plus();
- };
-
- popcorn1 = Popcorn.youtube( "#video8", "http://www.youtube.com/watch?v=nfGV32RNkhw&start=130" );
- popcorn1.on( "loadeddata", firstTest);
- if ( popcorn1.readyState >= 4 ) {
-
- firstTest();
- }
-
- popcorn2 = Popcorn.youtube( "#video9", "http://www.youtube.com/watch?v=nfGV32RNkhw&t=2m10s" );
- popcorn2.on( "loadeddata", secondTest);
- if ( popcorn2.readyState >= 4 ) {
-
- secondTest();
- }
-
- popcorn3 = Popcorn.youtube( "#video10", "http://www.youtube.com/watch?v=nfGV32RNkhw&t=2m" );
- popcorn3.on( "loadeddata", thirdTest);
- if ( popcorn3.readyState >= 4 ) {
-
- thirdTest();
- }
-
- popcorn4 = Popcorn.youtube( "#video11", "http://www.youtube.com/watch?v=nfGV32RNkhw&t=10s" );
- popcorn4.on( "loadeddata", fourthTest);
- if ( popcorn4.readyState >= 4 ) {
-
- fourthTest();
- }
+ equal( state++, 1, "loadeddata fired last" );
+ plus();
+ });
});
asyncTest( "youtube player quarantine", function() {
View
6 wrappers/common/popcorn._MediaElementProto.js
@@ -189,6 +189,12 @@
}
},
+ id: {
+ get: function() {
+ return this.parentNode.id;
+ }
+ },
+
seekable: {
get: function() {
return _fakeTimeRanges;
View
25 wrappers/youtube/popcorn.HTMLYouTubeVideoElement.js
@@ -79,8 +79,8 @@
duration: NaN,
ended: false,
paused: true,
- width: parent.width|0 ? parent.width : MIN_WIDTH,
- height: parent.height|0 ? parent.height : MIN_HEIGHT,
+ width: parent.offsetWidth|0 ? parent.offsetWidth : MIN_WIDTH,
+ height: parent.offsetHeight|0 ? parent.offsetHeight : MIN_HEIGHT,
error: null
},
playerReady = false,
@@ -551,6 +551,25 @@
}
},
+
+ offsetWidth: {
+ get: function() {
+ return elem.width;
+ },
+ set: function( aValue ) {
+ impl.width = aValue;
+ }
+ },
+
+ offsetHeight: {
+ get: function() {
+ return elem.height;
+ },
+ set: function( aValue ) {
+ impl.height = aValue;
+ }
+ },
+
currentTime: {
get: function() {
return getCurrentTime();
@@ -635,7 +654,7 @@
// Helper for identifying URLs we know how to play.
HTMLYouTubeVideoElement.prototype._canPlaySrc = function( url ) {
- return (/(?:http:\/\/www\.|http:\/\/|www\.|\.|^)(youtu)/).test( url ) ?
+ return (/(?:http:\/\/www\.|http:\/\/|www\.|\.|^)(youtu).*(?:\/|v=)(.{11})/).test( url ) ?
"probably" :
EMPTY_STRING;
};

0 comments on commit 56bc05a

Please sign in to comment.
Something went wrong with that request. Please try again.