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