From 4198f3ce2c2a6b6501046b2c8c9f7454c95a383a Mon Sep 17 00:00:00 2001 From: calzoneman Date: Thu, 7 Nov 2013 17:19:36 -0600 Subject: [PATCH] Add support for Google Docs videos --- lib/channel.js | 2 ++ lib/get-info.js | 73 +++++++++++++++++++++++++++++++++++++++++ lib/media.js | 19 +++++++++-- lib/playlist.js | 4 +++ www/assets/js/player.js | 56 ++++++++++++++++++++++++++++++- www/assets/js/util.js | 7 ++++ 6 files changed, 158 insertions(+), 3 deletions(-) diff --git a/lib/channel.js b/lib/channel.js index 0fe7a4ef..83da616f 100644 --- a/lib/channel.js +++ b/lib/channel.js @@ -550,6 +550,8 @@ Channel.prototype.getIPRank = function (ip, callback) { Channel.prototype.cacheMedia = function(media) { var self = this; + if (media.type === "gd") + return false; // Prevent the copy in the playlist from messing with this one media = media.dup(); if(media.temp) { diff --git a/lib/get-info.js b/lib/get-info.js index 3ef66159..7dc0ce63 100644 --- a/lib/get-info.js +++ b/lib/get-info.js @@ -615,6 +615,79 @@ var Getters = { id = CustomEmbedFilter(id); var media = new Media(id, "Custom Media", "--:--", "cu"); callback(false, media); + }, + + /* google docs */ + gd: function (id, callback) { + var options = { + host: "docs.google.com", + path: "/file/d/" + id + "/edit", + port: 443 + }; + + urlRetrieve(https, options, function (status, res) { + if (status !== 200) { + callback("Google Docs rejected: HTTP " + status, false); + return; + } + + var m = res.match(/main\((.*?)\);<\/script>/); + if (m) { + try { + var data = m[1]; + data = data.substring(data.indexOf(",") + 1); + data = data.replace(/'(.*?)'([:\,\}\]])/g, "\"$1\"$2"); + data = "[" + data + "]"; + var js = JSON.parse(data); + var title = js[0].title; + var seconds = js[1].videodetails.duration / 1000; + var med = new Media(id, title, seconds, "gd"); + + var fv = js[1].videoplay.flashVars; + var fvstr = ""; + for (var k in fv) { + if (k === "autoplay") + fv[k] = "1"; + fvstr += "&" + k + "=" + encodeURIComponent(fv[k]); + } + fvstr = fvstr.substring(1); + + var url = js[1].videoplay.swfUrl + "&enablejsapi=1"; + med.object = { + type: "application/x-shockwave-flash", + allowscriptaccess: "always", + allowfullscreen: "true", + wmode: "opaque", + data: url + }; + + med.params = [ + { + name: "allowFullScreen", + value: "true" + }, + { + name: "allowScriptAccess", + value: "always" + }, + { + name: "wmode", + value: "opaque" + }, + { + name: "flashvars", + value: fvstr + } + ]; + + callback(false, med); + } catch (e) { + callback("Parsing of Google Docs output failed", null); + } + } else { + callback(res, null); + } + }); } }; diff --git a/lib/media.js b/lib/media.js index 7dae37c7..407e6735 100644 --- a/lib/media.js +++ b/lib/media.js @@ -33,19 +33,27 @@ Media.prototype.dup = function() { // Returns an object containing the data in this Media but not the // prototype Media.prototype.pack = function() { - return { + var x = { id: this.id, title: this.title, seconds: this.seconds, duration: this.duration, type: this.type, }; + + if (this.object) { + x.object = this.object; + } + if (this.params) { + x.params = this.params; + } + return x; } // Same as pack() but includes the currentTime variable set by the channel // when the media is being synchronized Media.prototype.fullupdate = function() { - return { + var x = { id: this.id, title: this.title, seconds: this.seconds, @@ -54,6 +62,13 @@ Media.prototype.fullupdate = function() { currentTime: this.currentTime, paused: this.paused, }; + if (this.object) { + x.object = this.object; + } + if (this.params) { + x.params = this.params; + } + return x; } Media.prototype.timeupdate = function() { diff --git a/lib/playlist.js b/lib/playlist.js index 0ae20894..36ebe898 100644 --- a/lib/playlist.js +++ b/lib/playlist.js @@ -126,6 +126,8 @@ Playlist.prototype.load = function(data, callback) { for(var i in data.pl) { var e = data.pl[i].media; var m = new Media(e.id, e.title, e.seconds, e.type); + m.object = e.object; + m.params = e.params; var it = this.makeItem(m); it.temp = data.pl[i].temp; it.queueby = data.pl[i].queueby; @@ -208,6 +210,8 @@ Playlist.prototype.addMedia = function (data) { } var m = new Media(data.id, data.title, data.seconds, data.type); + m.object = data.object; + m.params = data.params; var item = this.makeItem(m); item.queueby = data.queueby; item.temp = data.temp; diff --git a/www/assets/js/player.js b/www/assets/js/player.js index 1e05dd2f..63bd2a2a 100644 --- a/www/assets/js/player.js +++ b/www/assets/js/player.js @@ -787,6 +787,59 @@ var CustomPlayer = function (data) { self.seek = function () { }; }; +var GoogleDocsPlayer = function (data) { + var self = this; + self.init = function (data) { + self.videoId = data.id; + self.videoLength = data.seconds; + self.paused = false; + var wmode = USEROPTS.wmode_transparent ? "transparent" : "opaque"; + self.player = $("", data.object)[0]; + $(self.player).attr("data", data.object.data); + $(self.player).attr("width", VWIDTH) + .attr("height", VHEIGHT); + data.params.forEach(function (p) { + $("", p).appendTo(self.player); + }); + removeOld($(self.player)); + }; + + self.init(data); + + self.load = function (data) { + self.init(data); + }; + + self.pause = function () { + if(self.player && self.player.pauseVideo) + self.player.pauseVideo(); + }; + + self.play = function () { + if(self.player && self.player.playVideo) + self.player.playVideo(); + }; + + self.isPaused = function (callback) { + if(self.player && self.player.getPlayerState) { + var state = self.player.getPlayerState(); + callback(state != YT.PlayerState.PLAYING); + } else { + callback(false); + } + }; + + self.getTime = function (callback) { + if(self.player && self.player.getCurrentTime) + callback(self.player.getCurrentTime()); + }; + + self.seek = function (time) { + if(self.player && self.player.seekTo) + self.player.seekTo(time, true); + }; +}; + function handleMediaUpdate(data) { // Don't update if the position is past the video length, but // make an exception when the video length is 0 seconds @@ -882,7 +935,8 @@ var constructors = { "rt": RTMPPlayer, "jw": JWPlayer, "im": ImgurPlayer, - "cu": CustomPlayer + "cu": CustomPlayer, + "gd": GoogleDocsPlayer }; function loadMediaPlayer(data) { diff --git a/www/assets/js/util.js b/www/assets/js/util.js index 93a98230..1f1bacd5 100644 --- a/www/assets/js/util.js +++ b/www/assets/js/util.js @@ -1402,6 +1402,13 @@ function parseMediaLink(url) { }; } + if ((m = url.match(/docs\.google\.com\/file\/d\/(.*?)\/edit/))) { + return { + id: m[1], + type: "gd" + }; + } + return { id: null, type: null