diff --git a/build-player.js b/build-player.js index 0e82b334..d02f3536 100644 --- a/build-player.js +++ b/build-player.js @@ -18,6 +18,7 @@ var order = [ 'hitbox.coffee', 'ustream.coffee', 'imgur.coffee', + 'gdrive-youtube.coffee', 'update.coffee' ]; diff --git a/player/embed.coffee b/player/embed.coffee index b42d965a..85c85dbc 100644 --- a/player/embed.coffee +++ b/player/embed.coffee @@ -37,6 +37,7 @@ window.EmbedPlayer = class EmbedPlayer extends Player object = $('').attr( type: 'application/x-shockwave-flash' data: embed.src + wmode: 'opaque' ) genParam('allowfullscreen', 'true').appendTo(object) genParam('allowscriptaccess', 'always').appendTo(object) diff --git a/player/gdrive-youtube.coffee b/player/gdrive-youtube.coffee new file mode 100644 index 00000000..37aa08a2 --- /dev/null +++ b/player/gdrive-youtube.coffee @@ -0,0 +1,103 @@ +window.GoogleDriveYouTubePlayer = class GoogleDriveYouTubePlayer extends Player + constructor: (data) -> + if not (this instanceof GoogleDriveYouTubePlayer) + return new GoogleDriveYouTubePlayer(data) + + @setMediaProperties(data) + @init(data) + + init: (data) -> + embed = $('').attr( + type: 'application/x-shockwave-flash' + src: "https://www.youtube.com/get_player?docid=#{data.id}&ps=docs\ + &partnerid=30&enablejsapi=1&cc_load_policy=1\ + &auth_timeout=86400000000" + flashvars: 'autoplay=1&playerapiid=uniquePlayerId' + wmode: 'opaque' + allowscriptaccess: 'always' + ) + removeOld(embed) + + window.onYouTubePlayerReady = => + if PLAYER != this + return + + @yt = embed[0] + window.gdriveStateChange = @onStateChange.bind(this) + @yt.addEventListener('onStateChange', 'gdriveStateChange') + @onReady() + + load: (data) -> + @setMediaProperties(data) + @init(data) + + onReady: -> + @yt.ready = true + @setVolume(VOLUME) + @setQuality(USEROPTS.default_quality) + + onStateChange: (ev) -> + if PLAYER != this + return + + if (ev == YT.PlayerState.PAUSED and not @paused) or + (ev == YT.PlayerState.PLAYING and @paused) + @paused = (ev == YT.PlayerState.PAUSED) + if CLIENT.leader + sendVideoUpdate() + + if ev == YT.PlayerState.ENDED and CLIENT.leader + socket.emit('playNext') + + play: -> + @paused = false + if @yt and @yt.ready + @yt.playVideo() + + pause: -> + @paused = true + if @yt and @yt.ready + @yt.pauseVideo() + + seekTo: (time) -> + if @yt and @yt.ready + @yt.seekTo(time, true) + + setVolume: (volume) -> + if @yt and @yt.ready + if volume > 0 + # If the player is muted, even if the volume is set, + # the player remains muted + @yt.unMute() + @yt.setVolume(volume * 100) + + setQuality: (quality) -> + if not @yt or not @yt.ready + return + + ytQuality = switch String(quality) + when '240' then 'small' + when '360' then 'medium' + when '480' then 'large' + when '720' then 'hd720' + when '1080' then 'hd1080' + when 'best' then 'highres' + else 'auto' + + if ytQuality != 'auto' + @yt.setPlaybackQuality(ytQuality) + + getTime: (cb) -> + if @yt and @yt.ready + cb(@yt.getCurrentTime()) + else + cb(0) + + getVolume: (cb) -> + if @yt and @yt.ready + if @yt.isMuted() + cb(0) + else + cb(@yt.getVolume() / 100) + else + cb(VOLUME) diff --git a/player/update.coffee b/player/update.coffee index 6231c4c1..25bf546a 100644 --- a/player/update.coffee +++ b/player/update.coffee @@ -2,7 +2,7 @@ TYPE_MAP = yt: YouTubePlayer vi: VimeoPlayer dm: DailymotionPlayer - gd: VideoJSPlayer + gd: GoogleDriveYouTubePlayer gp: VideoJSPlayer fi: FilePlayer jw: FilePlayer @@ -16,7 +16,7 @@ TYPE_MAP = im: ImgurPlayer window.loadMediaPlayer = (data) -> - if data.meta.direct + if data.meta.direct and data.type != 'gd' try window.PLAYER = new VideoJSPlayer(data) catch e diff --git a/www/js/player.js b/www/js/player.js index 2ce598eb..e7efe95b 100644 --- a/www/js/player.js +++ b/www/js/player.js @@ -1,5 +1,5 @@ (function() { - var CUSTOM_EMBED_WARNING, CustomEmbedPlayer, DEFAULT_ERROR, DailymotionPlayer, EmbedPlayer, FilePlayer, HITBOX_ERROR, HitboxPlayer, ImgurPlayer, LivestreamPlayer, Player, RTMPPlayer, SoundCloudPlayer, TYPE_MAP, TwitchPlayer, USTREAM_ERROR, UstreamPlayer, VideoJSPlayer, VimeoPlayer, YouTubePlayer, codecToMimeType, genParam, sortSources, + var CUSTOM_EMBED_WARNING, CustomEmbedPlayer, DEFAULT_ERROR, DailymotionPlayer, EmbedPlayer, FilePlayer, GoogleDriveYouTubePlayer, HITBOX_ERROR, HitboxPlayer, ImgurPlayer, LivestreamPlayer, Player, RTMPPlayer, SoundCloudPlayer, TYPE_MAP, TwitchPlayer, USTREAM_ERROR, UstreamPlayer, VideoJSPlayer, VimeoPlayer, YouTubePlayer, codecToMimeType, genParam, sortSources, extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, hasProp = {}.hasOwnProperty; @@ -851,7 +851,8 @@ var key, object, ref, value; object = $('').attr({ type: 'application/x-shockwave-flash', - data: embed.src + data: embed.src, + wmode: 'opaque' }); genParam('allowfullscreen', 'true').appendTo(object); genParam('allowscriptaccess', 'always').appendTo(object); @@ -1099,11 +1100,152 @@ })(EmbedPlayer); + window.GoogleDriveYouTubePlayer = GoogleDriveYouTubePlayer = (function(superClass) { + extend(GoogleDriveYouTubePlayer, superClass); + + function GoogleDriveYouTubePlayer(data) { + if (!(this instanceof GoogleDriveYouTubePlayer)) { + return new GoogleDriveYouTubePlayer(data); + } + this.setMediaProperties(data); + this.init(data); + } + + GoogleDriveYouTubePlayer.prototype.init = function(data) { + var embed; + embed = $('').attr({ + type: 'application/x-shockwave-flash', + src: "https://www.youtube.com/get_player?docid=" + data.id + "&ps=docs&partnerid=30&enablejsapi=1&cc_load_policy=1&auth_timeout=86400000000", + flashvars: 'autoplay=1&playerapiid=uniquePlayerId', + wmode: 'opaque', + allowscriptaccess: 'always' + }); + removeOld(embed); + return window.onYouTubePlayerReady = (function(_this) { + return function() { + if (PLAYER !== _this) { + return; + } + _this.yt = embed[0]; + window.gdriveStateChange = _this.onStateChange.bind(_this); + _this.yt.addEventListener('onStateChange', 'gdriveStateChange'); + return _this.onReady(); + }; + })(this); + }; + + GoogleDriveYouTubePlayer.prototype.load = function(data) { + this.setMediaProperties(data); + return this.init(data); + }; + + GoogleDriveYouTubePlayer.prototype.onReady = function() { + this.yt.ready = true; + this.setVolume(VOLUME); + return this.setQuality(USEROPTS.default_quality); + }; + + GoogleDriveYouTubePlayer.prototype.onStateChange = function(ev) { + if (PLAYER !== this) { + return; + } + if ((ev === YT.PlayerState.PAUSED && !this.paused) || (ev === YT.PlayerState.PLAYING && this.paused)) { + this.paused = ev === YT.PlayerState.PAUSED; + if (CLIENT.leader) { + sendVideoUpdate(); + } + } + if (ev === YT.PlayerState.ENDED && CLIENT.leader) { + return socket.emit('playNext'); + } + }; + + GoogleDriveYouTubePlayer.prototype.play = function() { + this.paused = false; + if (this.yt && this.yt.ready) { + return this.yt.playVideo(); + } + }; + + GoogleDriveYouTubePlayer.prototype.pause = function() { + this.paused = true; + if (this.yt && this.yt.ready) { + return this.yt.pauseVideo(); + } + }; + + GoogleDriveYouTubePlayer.prototype.seekTo = function(time) { + if (this.yt && this.yt.ready) { + return this.yt.seekTo(time, true); + } + }; + + GoogleDriveYouTubePlayer.prototype.setVolume = function(volume) { + if (this.yt && this.yt.ready) { + if (volume > 0) { + this.yt.unMute(); + } + return this.yt.setVolume(volume * 100); + } + }; + + GoogleDriveYouTubePlayer.prototype.setQuality = function(quality) { + var ytQuality; + if (!this.yt || !this.yt.ready) { + return; + } + ytQuality = (function() { + switch (String(quality)) { + case '240': + return 'small'; + case '360': + return 'medium'; + case '480': + return 'large'; + case '720': + return 'hd720'; + case '1080': + return 'hd1080'; + case 'best': + return 'highres'; + default: + return 'auto'; + } + })(); + if (ytQuality !== 'auto') { + return this.yt.setPlaybackQuality(ytQuality); + } + }; + + GoogleDriveYouTubePlayer.prototype.getTime = function(cb) { + if (this.yt && this.yt.ready) { + return cb(this.yt.getCurrentTime()); + } else { + return cb(0); + } + }; + + GoogleDriveYouTubePlayer.prototype.getVolume = function(cb) { + if (this.yt && this.yt.ready) { + if (this.yt.isMuted()) { + return cb(0); + } else { + return cb(this.yt.getVolume() / 100); + } + } else { + return cb(VOLUME); + } + }; + + return GoogleDriveYouTubePlayer; + + })(Player); + TYPE_MAP = { yt: YouTubePlayer, vi: VimeoPlayer, dm: DailymotionPlayer, - gd: VideoJSPlayer, + gd: GoogleDriveYouTubePlayer, gp: VideoJSPlayer, fi: FilePlayer, jw: FilePlayer, @@ -1119,7 +1261,7 @@ window.loadMediaPlayer = function(data) { var e; - if (data.meta.direct) { + if (data.meta.direct && data.type !== 'gd') { try { return window.PLAYER = new VideoJSPlayer(data); } catch (_error) { diff --git a/www/js/util.js b/www/js/util.js index fd13a2e7..38994699 100644 --- a/www/js/util.js +++ b/www/js/util.js @@ -1910,13 +1910,18 @@ function waitUntilDefined(obj, key, fn) { } function hidePlayer() { + /* 2015-09-16 + * Originally used to hide the player while a modal was open because of + * certain flash videos that always rendered on top. Seems to no longer + * be an issue. Uncomment this if it is. if (!PLAYER) return; $("#ytapiplayer").hide(); + */ } function unhidePlayer() { - $("#ytapiplayer").show(); + //$("#ytapiplayer").show(); } function chatDialog(div) {